mirror of
https://github.com/shokinn/hosts-go.git
synced 2025-08-23 16:43:02 +00:00
Compare commits
8 commits
250091bb8a
...
e59af1cb02
Author | SHA1 | Date | |
---|---|---|---|
e59af1cb02 | |||
63c0d38455 | |||
ecb7a1ab93 | |||
9af4c54d1e | |||
103a41cdc2 | |||
ef5c2a9da8 | |||
3f794786b6 | |||
3c239e68f3 |
7 changed files with 133 additions and 8 deletions
26
AGENTS.md
Normal file
26
AGENTS.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# AGENTS
|
||||||
|
|
||||||
|
## Memory Bank Requirements
|
||||||
|
- **Always read the Memory Bank before starting any task.**
|
||||||
|
- Memory Bank lives in `memory-bank/` and contains:
|
||||||
|
- `projectbrief.md`
|
||||||
|
- `productContext.md`
|
||||||
|
- `systemPatterns.md`
|
||||||
|
- `techContext.md`
|
||||||
|
- `activeContext.md`
|
||||||
|
- `progress.md`
|
||||||
|
- The Memory Bank is the single source of project context. If a user requests **update memory bank**, review every file in the directory before making changes.
|
||||||
|
- After significant work or when insights are gained, update `activeContext.md` and `progress.md`. Create additional files in `memory-bank/` when useful for organizing new documentation.
|
||||||
|
|
||||||
|
## Development Guidelines
|
||||||
|
- **Language:** Go using modules.
|
||||||
|
- **Formatting:** run `gofmt -w` on modified Go files.
|
||||||
|
- **Testing:** practice TDD and run `go test ./...` before committing, also Test the Application interactively, to ensure the UI is also correct.
|
||||||
|
- **Linting:** run `golangci-lint run` when available.
|
||||||
|
- **Structure:** keep application code inside `internal/` packages and follow existing patterns for `core`, `tui`, and `utils`.
|
||||||
|
- **Error Handling:** return errors instead of panicking and provide clear messages.
|
||||||
|
|
||||||
|
## Commit Process
|
||||||
|
- Ensure Memory Bank is updated when project state changes.
|
||||||
|
- Run the formatting, lint, and test commands above.
|
||||||
|
- Commit only when the worktree is clean and all commands pass.
|
87
CLAUDE.md
Normal file
87
CLAUDE.md
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is a Go TUI application for managing `/etc/hosts` files using the Bubble Tea framework. The app provides a two-pane interface (list view + detail view) for viewing, editing, and managing host entries with features like activation/deactivation, sorting, and DNS resolution.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The codebase follows a clean architecture pattern with separation of concerns:
|
||||||
|
|
||||||
|
- `cmd/hosts/main.go` - Entry point that parses `/etc/hosts` and launches the TUI
|
||||||
|
- `internal/core/` - Core business logic and data models
|
||||||
|
- `models.go` - Data structures for HostEntry and HostsFile with validation
|
||||||
|
- `parser.go` - Hosts file parsing, formatting, and writing with intelligent formatting detection
|
||||||
|
- `internal/tui/` - Bubble Tea TUI components
|
||||||
|
- `model.go` - Main Bubble Tea model with pane navigation and mode management
|
||||||
|
- `view.go` - Rendering logic for the two-pane interface
|
||||||
|
- `update.go` - Message handling and state updates
|
||||||
|
- `tests/` - Test files following TDD approach
|
||||||
|
|
||||||
|
## Key Technical Patterns
|
||||||
|
|
||||||
|
### Data Models
|
||||||
|
- `HostEntry` represents individual host entries with IP, hostname, aliases, comments, and active status
|
||||||
|
- `HostsFile` contains collections of entries with methods for searching, adding, and removing
|
||||||
|
- All models include validation methods and proper error handling
|
||||||
|
|
||||||
|
### TUI Architecture
|
||||||
|
- Uses Bubble Tea's Model-View-Update pattern
|
||||||
|
- Two-pane layout with list and detail views
|
||||||
|
- Mode switching between ViewMode and EditMode
|
||||||
|
- Pane focus management (listPane/detailPane)
|
||||||
|
|
||||||
|
### File Operations
|
||||||
|
- Intelligent formatting detection preserves original hosts file style
|
||||||
|
- Atomic file operations with backup creation
|
||||||
|
- Proper validation before writing changes
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
### Build and Run
|
||||||
|
```bash
|
||||||
|
go run cmd/hosts/main.go
|
||||||
|
go build -o hosts cmd/hosts/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
```bash
|
||||||
|
go test ./... # Run all tests
|
||||||
|
go test ./tests/ # Run tests in tests directory
|
||||||
|
go test -v ./internal/core/ # Run core package tests with verbose output
|
||||||
|
go test -cover ./... # Run tests with coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
Key dependencies are managed via `go.mod`:
|
||||||
|
- Bubble Tea framework for TUI
|
||||||
|
- Bubbles for common components
|
||||||
|
- Lip Gloss for styling
|
||||||
|
- testify for test assertions
|
||||||
|
|
||||||
|
## Important Implementation Details
|
||||||
|
|
||||||
|
### Hosts File Parsing
|
||||||
|
The parser in `internal/core/parser.go` handles:
|
||||||
|
- Active and inactive (commented) entries
|
||||||
|
- Inline comments preservation
|
||||||
|
- Intelligent formatting detection (tabs vs spaces)
|
||||||
|
- IPv4/IPv6 address validation
|
||||||
|
- RFC-compliant hostname validation
|
||||||
|
|
||||||
|
### TUI Navigation
|
||||||
|
- Tab/Shift+Tab for pane switching
|
||||||
|
- Arrow keys for list navigation
|
||||||
|
- Space for toggling entry activation
|
||||||
|
- 'q' to quit application
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Comprehensive validation at model level
|
||||||
|
- Parse warnings collection during file parsing
|
||||||
|
- Graceful error recovery in TUI operations
|
||||||
|
|
||||||
|
## Memory Bank Integration
|
||||||
|
|
||||||
|
This project uses Cline's Memory Bank system located in `memory-bank/` directory. The project brief and context files provide additional architectural context and development history. Always consult these files when making significant changes to understand the project's evolution and design decisions.
|
|
@ -18,13 +18,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
} else {
|
} else {
|
||||||
m.focus = listPane
|
m.focus = listPane
|
||||||
}
|
}
|
||||||
case "e":
|
case "ctrl+e":
|
||||||
if m.mode == ViewMode {
|
if m.mode == ViewMode {
|
||||||
m.mode = EditMode
|
m.mode = EditMode
|
||||||
} else {
|
} else {
|
||||||
m.mode = ViewMode
|
m.mode = ViewMode
|
||||||
}
|
}
|
||||||
case "a":
|
case " ":
|
||||||
if m.mode == EditMode {
|
if m.mode == EditMode {
|
||||||
if entry := m.SelectedEntry(); entry != nil {
|
if entry := m.SelectedEntry(); entry != nil {
|
||||||
entry.Active = !entry.Active
|
entry.Active = !entry.Active
|
||||||
|
@ -34,7 +34,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
case "up", "k":
|
case "up", "k":
|
||||||
if m.focus == detailPane {
|
if m.focus == detailPane {
|
||||||
m.detail.LineUp(1)
|
m.detail.ScrollUp(1)
|
||||||
} else {
|
} else {
|
||||||
m.list, cmd = m.list.Update(msg)
|
m.list, cmd = m.list.Update(msg)
|
||||||
m.refreshDetail()
|
m.refreshDetail()
|
||||||
|
@ -42,7 +42,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, cmd
|
return m, cmd
|
||||||
case "down", "j":
|
case "down", "j":
|
||||||
if m.focus == detailPane {
|
if m.focus == detailPane {
|
||||||
m.detail.LineDown(1)
|
m.detail.ScrollDown(1)
|
||||||
} else {
|
} else {
|
||||||
m.list, cmd = m.list.Update(msg)
|
m.list, cmd = m.list.Update(msg)
|
||||||
m.refreshDetail()
|
m.refreshDetail()
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
- ✅ **Parser integration** loading `/etc/hosts` into the UI
|
- ✅ **Parser integration** loading `/etc/hosts` into the UI
|
||||||
|
|
||||||
### Phase 3: Edit Mode (IN PROGRESS)
|
### Phase 3: Edit Mode (IN PROGRESS)
|
||||||
- 🔄 **Edit mode toggle** via 'e' key with status bar indication
|
- 🔄 **Edit mode toggle** via 'ctrl+e' key with status bar indication
|
||||||
- 🔄 **Entry activation toggle** using 'a' key
|
- 🔄 **Entry activation toggle** using spacebar
|
||||||
- 🔄 **Model tests** covering edit mode behavior
|
- 🔄 **Model tests** covering edit mode behavior
|
||||||
|
|
||||||
### Parser Capabilities Achieved
|
### Parser Capabilities Achieved
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
- **Active work**: Expand edit mode with file integration and advanced editing
|
- **Active work**: Expand edit mode with file integration and advanced editing
|
||||||
- **Blockers**: None - comprehensive parser foundation with 54 tests completed
|
- **Blockers**: None - comprehensive parser foundation with 54 tests completed
|
||||||
- **Parser status**: Production-ready with all safety features implemented
|
- **Parser status**: Production-ready with all safety features implemented
|
||||||
|
- **Key bindings updated**: `ctrl+e` for edit mode, spacebar to toggle entries
|
||||||
|
|
||||||
### Development Readiness
|
### Development Readiness
|
||||||
- ✅ **Architecture designed**: Clear technical approach documented
|
- ✅ **Architecture designed**: Clear technical approach documented
|
||||||
|
|
|
@ -29,6 +29,17 @@ The user can:
|
||||||
- Use DNS-based resolution for hostnames
|
- Use DNS-based resolution for hostnames
|
||||||
- Quickly update IP addresses
|
- Quickly update IP addresses
|
||||||
|
|
||||||
|
Key Bindings:
|
||||||
|
- `ctrl`+`e`: Change to edit mode
|
||||||
|
- `e`: Edit the currently selected hostname entry
|
||||||
|
- `space`: Entry activation toggle
|
||||||
|
- `shift`+`up`/`down`: Move the selected entry up or down
|
||||||
|
- `i` Sort by IP addresses
|
||||||
|
- `n` Sort by (the first) hostname
|
||||||
|
- `ctrl`+`s`: Save
|
||||||
|
- `c` To open the tool configuration
|
||||||
|
- `?` or `h`: Display help about all Key Bindings
|
||||||
|
|
||||||
The program will operate in **view-only mode** by default and require explicit entry into **edit mode**, at which point it will request elevated (sudo) permissions until editing is disabled.
|
The program will operate in **view-only mode** by default and require explicit entry into **edit mode**, at which point it will request elevated (sudo) permissions until editing is disabled.
|
||||||
|
|
||||||
The project uses:
|
The project uses:
|
||||||
|
|
|
@ -72,12 +72,12 @@ func TestEditModeToggleAndActivation(t *testing.T) {
|
||||||
m := tui.NewModel(hf)
|
m := tui.NewModel(hf)
|
||||||
|
|
||||||
// enter edit mode
|
// enter edit mode
|
||||||
nm, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'e'}})
|
nm, _ := m.Update(tea.KeyMsg{Type: tea.KeyCtrlE})
|
||||||
m = nm.(tui.Model)
|
m = nm.(tui.Model)
|
||||||
assert.Equal(t, tui.EditMode, m.Mode())
|
assert.Equal(t, tui.EditMode, m.Mode())
|
||||||
|
|
||||||
// toggle active state of first entry
|
// toggle active state of first entry
|
||||||
nm, _ = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'a'}})
|
nm, _ = m.Update(tea.KeyMsg{Type: tea.KeySpace})
|
||||||
m = nm.(tui.Model)
|
m = nm.(tui.Model)
|
||||||
assert.False(t, m.SelectedEntry().Active)
|
assert.False(t, m.SelectedEntry().Active)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue