SecOps Agent is an independent Crush fork maintained by
chenchunrun and built on top of the original project by
Charm. It connects to LLMs and gives them tools to read,
write, and execute code. It supports multiple providers (Anthropic, OpenAI,
Gemini, Bedrock, Copilot, Hyper, MiniMax, Vercel, and more), integrates with
LSPs for code intelligence, and supports extensibility via MCP servers and
agent skills.
The module path is github.com/chenchunrun/SecOps.
main.go CLI entry point (cobra via internal/cmd)
internal/
app/app.go Top-level wiring: DB, config, agents, LSP, MCP, events
cmd/ CLI commands (root, run, login, models, stats, sessions)
config/
config.go Config struct, context file paths, agent definitions
load.go crush.json loading and validation
provider.go Provider configuration and model resolution
agent/
agent.go SessionAgent: runs LLM conversations per session
coordinator.go Coordinator: routes by agent id (coder, task, planner, ops, sec)
handoff/ Crush Handoff v1: parse, validate, extract from Markdown
pending_handoff.go Pending handoff from UI session history → prompt prefix + audit
prompts.go Loads Go-template system prompts
templates/ System prompt templates (coder.md.tpl, task.md.tpl, etc.)
tools/ All built-in tools (bash, edit, view, grep, glob, etc.)
mcp/ MCP client integration
session/session.go Session CRUD backed by SQLite
message/ Message model and content types
db/ SQLite via sqlc, with migrations
sql/ Raw SQL queries (consumed by sqlc)
migrations/ Schema migrations
lsp/ LSP client manager, auto-discovery, on-demand startup
ui/ Bubble Tea v2 TUI (see internal/ui/AGENTS.md)
permission/ Tool permission checking and allow-lists
secops_permission.go SecOps risk-aware permission decision chain
security/
capability.go Capability-based gate model
risk_assessment.go Multi-factor risk assessment engine
attack/ Reduced ATT&CK corpus + evidence reasoner
redact/ Shared credential-redaction patterns (used by
audit/SIEM export and the shell command log)
audit/
audit.go Audit event model and service
audit_store.go Persistent audit storage
compliance_report.go Compliance report generation
siem_export.go SIEM exporters (redaction delegated to security/redact)
computer/ Stable Computer identity, lifecycle, and backend adapters
sandbox/
executor.go Local/Docker/SSH execution backend
skills/ Skill file discovery and loading
shell/ Bash command execution with background job support
event/ Telemetry (PostHog)
pubsub/ Internal pub/sub for cross-component messaging
filetracker/ Tracks files touched per session
history/ Prompt history
integration/ End-to-end integration tests
agent/tools/secops/ SecOps tool implementations (18 categories)
Public docs/ content should stay limited to stable guides and
legal/licensing materials.
Process records, validation logs, release-closeout notes, and working security
documents should remain local-only. These are intentionally excluded from the
public repository through .gitignore rules for docs/project/,
docs/release/, docs/security/, and LOCAL_* materials unless
intentionally reclassified for publication.
charm.land/fantasy: LLM provider abstraction layer. Handles protocol differences between Anthropic, OpenAI, Gemini, etc. Used ininternal/appandinternal/agent.charm.land/bubbletea/v2: TUI framework powering the interactive UI.charm.land/lipgloss/v2: Terminal styling.charm.land/glamour/v2: Markdown rendering in the terminal.charm.land/catwalk: Snapshot/golden-file testing for TUI components.sqlc: Generates Go code from SQL queries ininternal/db/sql/.
- Config is a Service: accessed via
config.Service, not global state. - Tools are self-documenting: each tool has a
.goimplementation and a.mddescription file ininternal/agent/tools/. - Fixed tool families use toolset builders: when adding a fixed built-in
agent tool, prefer extending an existing
Build*ToolSet(...)dataset or adding a new dataset-backed toolset ininternal/agent/tools/instead of appending raw constructors incoordinator.buildTools. - System prompts are Go templates:
internal/agent/templates/*.md.tplwith runtime data injected. - Context files: Crush reads AGENTS.md, CRUSH.md, CLAUDE.md, GEMINI.md
(and
.localvariants) from the working directory for project-specific instructions. - Planner agent id:
planneris its own agent entry (AgentPlanner), not a Task profile switch. Defaults to read-only tools (glob,grep,ls,sourcegraph,view), no bash or SecOps execution; useoptions.active_agentor/plannerin the TUI. - Crush Handoff v1 (agent-to-agent): Producers embed Handoff JSON in a
Markdown fenced code block labeled
crush-handoff(seeinternal/agent/handoff). Logic inpending_handoff.go; for consumerscoder,task,ops_agent, andsecurity_expert_agent,SessionAgent.Runlifts the newest valid pending handoff (after the last user message in history) intopromptPrefixviahandoff.FormatForPrompt. Each injection is recorded as audit eventagent_handoff_consumed(EventTypeAgentHandoffConsumed), includingdetailssuch ashandoff_versionandsource_assistant_message_id. - Persistence: SQLite + sqlc. All queries live in
internal/db/sql/, generated code ininternal/db/. Migrations ininternal/db/migrations/. - Pub/sub:
internal/pubsubfor decoupled communication between agent, UI, and services. - Computer boundary:
internal/computerowns stable logical IDs and lifecycle state. Local, Docker, and SSH are adapters behind that boundary; provider SDK types must not enter the core Computer model. - CGO disabled: builds with
CGO_ENABLED=0andGOEXPERIMENT=greenteagc.
- Module and build caches: If downloads from
proxy.golang.orgtime out or sandbox permission interferes with the default Go cache, point caches at repo-local directories (both are gitignored):
GOMODCACHE="$(pwd)/.gomodcache" GOCACHE="$(pwd)/.gocache" go test ./...
GOMODCACHE="$(pwd)/.gomodcache" GOCACHE="$(pwd)/.gocache" go build .- Build:
go build .orgo run . - Test:
task testorgo test ./...(run single test:go test ./internal/llm/prompt -run TestGetContextFromPaths). Handoff + audit regression:go test ./internal/agent -run 'TestPendingHandoff|TestRecordHandoff' -count=1 - Update Golden Files:
go test ./... -update(regenerates.goldenfiles when test output changes)- Update specific package:
go test ./internal/tui/components/core -update(in this case, we're updating "core")
- Update specific package:
- Lint:
task lint:fix - Format:
task fmt(gofumpt -w .) - Modernize:
task modernize(runsmodernizewhich makes code simplifications) - Dev:
task dev(runs with profiling enabled)
- Imports: Use
goimportsformatting, group stdlib, external, internal packages. - Formatting: Use gofumpt (stricter than gofmt), enabled in golangci-lint.
- Naming: Standard Go conventions — PascalCase for exported, camelCase for unexported.
- Types: Prefer explicit types, use type aliases for clarity (e.g.,
type AgentName string). - Error handling: Return errors explicitly, use
fmt.Errorffor wrapping. - Context: Always pass
context.Contextas first parameter for operations. - Interfaces: Define interfaces in consuming packages, keep them small and focused.
- Structs: Use struct embedding for composition, group related fields.
- Constants: Use typed constants with iota for enums, group in const blocks.
- Testing: Use testify's
requirepackage, parallel tests witht.Parallel(),t.SetEnv()to set environment variables. Always uset.Tempdir()when in need of a temporary directory. This directory does not need to be removed. - JSON tags: Use snake_case for JSON field names.
- File permissions: Use octal notation (0o755, 0o644) for file permissions.
- Log messages: Log messages must start with a capital letter (e.g.,
"Failed to save session" not "failed to save session").
- This is enforced by
task lint:logwhich runs as part oftask lint.
- This is enforced by
- Comments: End comments in periods unless comments are at the end of the line.
When writing tests that involve provider configurations, use the mock providers to avoid API calls:
func TestYourFunction(t *testing.T) {
// Enable mock providers for testing
originalUseMock := config.UseMockProviders
config.UseMockProviders = true
defer func() {
config.UseMockProviders = originalUseMock
config.ResetProviders()
}()
// Reset providers to ensure fresh mock data
config.ResetProviders()
// Your test code here - providers will now return mock data
providers := config.Providers()
// ... test logic
}- ALWAYS format any Go code you write.
- First, try
gofumpt -w .. - If
gofumptis not available, usegoimports. - If
goimportsis not available, usegofmt. - You can also use
task fmtto rungofumpt -w .on the entire project, as long asgofumptis on thePATH.
- First, try
- Comments that live on their own lines should start with capital letters and end with periods. Wrap comments at 78 columns.
- ALWAYS use semantic commits (
fix:,feat:,chore:,refactor:,docs:,sec:, etc). - Try to keep commits to one line, not including your attribution. Only use multi-line commits when additional context is truly necessary.
Anytime you need to work on the TUI, read internal/ui/AGENTS.md before
starting work.