Development¶
Guide for developing and contributing to AgentSentinel.
Prerequisites¶
- Go 1.21+
- tmux 3.0+
- golangci-lint (for linting)
Getting Started¶
Clone the repository:
Build:
Run:
Development Commands¶
Build¶
Test¶
# Run all tests
go test -v ./...
# Run tests with coverage
go test -cover ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Lint¶
Format¶
Project Structure¶
agentsentinel/
├── main.go # Entry point
├── cmd/ # CLI commands
├── internal/ # Internal packages
│ ├── watcher/ # Core watch logic
│ ├── config/ # Configuration
│ ├── detector/ # Pattern detection
│ ├── tmux/ # tmux integration
│ ├── stats/ # Statistics
│ └── notify/ # Notifications
├── docs/ # Documentation
├── mkdocs.yml # MkDocs config
├── go.mod # Go module
└── go.sum # Go dependencies
Adding Detection Patterns¶
Built-in Patterns¶
Add patterns in internal/detector/detector.go:
// In NewDetector()
patterns: []*regexp.Regexp{
regexp.MustCompile(`(?i)your-new-pattern`),
// ...
}
Adding Tests¶
Add test cases in internal/detector/detector_test.go:
{
name: "Your new pattern",
input: "Your prompt text (Y/n)",
wantType: PromptAllow,
wantLine: "Your prompt text (Y/n)",
}
Adding Danger Patterns¶
In internal/detector/detector.go:
Adding a New Command¶
- Create
cmd/newcmd.go:
package cmd
import "github.com/spf13/cobra"
var newCmd = &cobra.Command{
Use: "newcmd",
Short: "Description of the command",
RunE: runNewCmd,
}
func init() {
rootCmd.AddCommand(newCmd)
}
func runNewCmd(cmd *cobra.Command, args []string) error {
// Implementation
return nil
}
- Follow existing command structure (see
cmd/status.gofor a simple example).
Modifying Watch Behavior¶
Core watch logic is in internal/watcher/watcher.go:
watcher.New()- Create watcher with dependencieswatcher.Scan(ctx)- Per-tick scan of all paneswasRecentlyApproved()- Duplicate preventionmarkApproved()- Track approved panes
The cmd/watch.go handles CLI setup and the main ticker loop.
Testing Without tmux¶
The watcher package uses interfaces for dependency injection:
type TmuxClient interface {
ListPanes() ([]string, error)
CapturePane(paneID string, lines int) (string, error)
Approve(paneID string) error
ApproveMultiple(paneID string, count int, delayMs int) error
}
Create mock implementations for testing:
type mockTmuxClient struct {
panes []string
paneContents map[string]string
}
func (m *mockTmuxClient) ListPanes() ([]string, error) {
return m.panes, nil
}
// ... implement other methods
See internal/watcher/watcher_test.go for examples.
Documentation¶
Building Docs¶
Install MkDocs with Material theme:
Serve locally:
Build static site:
Updating Architecture Diagram¶
Edit docs/architecture.d2 and regenerate:
Code Style¶
- Follow standard Go conventions
- Use
gofmtfor formatting - Run
golangci-lintbefore committing - Keep functions focused and small
- Prefer returning errors over logging them
Error Handling¶
Follow this priority:
- Return - If the function can return an error
- Log - Use context-based logging:
slogutil.LoggerFromContext(ctx) - Panic - Only for programming errors / invariant violations
Commit Messages¶
Follow Conventional Commits:
feat(detector): add support for new CLI prompt
fix: resolve race condition in approval tracking
docs: update pattern reference
test: add integration tests for watcher
Pull Request Guidelines¶
- Create a feature branch from
main - Write tests for new functionality
- Ensure all tests pass:
go test ./... - Ensure linting passes:
golangci-lint run - Update documentation if needed
- Submit PR with clear description
Release Process¶
- Update version in code
- Update CHANGELOG
- Create and push tag:
git tag v1.0.0 && git push --tags - GitHub Actions will build and release