coddy-agent / rules
coddy-project/coddy-agent/.cursor/rules/core-modules.mdc
Main internal packages at a glance
Cursor rule156 starsChanged 3 days ago
---
description: Main internal packages at a glance
globs: internal/**/*.go
alwaysApply: false
---
# Core modules (sketch)
- **`internal/acp`** - ACP RPC server, session lifecycle from editors.
- **`internal/agent`** - tool loop and LLM turns.
- **`internal/session`** - session manager and mode (`agent` / `plan` / `ask`).
- **`internal/config`** - YAML and flags, plus the **`FileWatcher`** (`watch.go`) that installs a `config.yaml` somebody else changed into a running process: a stat gate, `LoadWithPaths`, the process overrides re-applied through `Adjust`, and a marshal-comparison against the live document so a save this process made itself, a comment or a reordered key announces nothing. `coddy serve` points it at `session.Manager.ReplaceConfig`, which is the one choke point every surface already observes.
- **`internal/tools`** - filesystem, shell, todo, MCP merge, etc. Shell also owns the background task family (**`run_command`** **`background: true`** plus **`background_list`** / **`background_output`** / **`background_wait`** / **`background_stop`**), backed by **`internal/bgtask`**. See **`docs/features/background-tasks.md`**. **`internal/tools/preview`** is the **`preview_server`** tool: a static file server over a project directory on a free localhost port, run on goroutines as a **`KindServer`** task of the same pool (no hard timeout unless the call names one). See **`docs/features/preview-server.md`**.
- **`internal/mcp`** - MCP transports (`client.go`, `remote.go`, `connect.go`), the merged server list with scope/origin labels (`manage.go`), and the **workspace trust gate** for project-local `.coddy/mcp.json` (`trust.go`, `gate.go`). That file arrives with the checkout, so its declarations are not started or contacted until the operator approves that exact declaration for that workspace: policy `mcp.project_trust` (`ask` default / `allow` / `deny`), approvals in `<home>/mcp-trust.json` keyed by canonical workspace + declaration digest. **Every connect or probe of a *configured* server must go through `TrustGate.Connect` / `TrustGate.Probe`** (they re-check immediately before the spawn); `mcp.Connect` directly is only for ACP client-supplied servers. Approval surfaces: `coddy mcp list|trust|untrust`, `POST /coddy/mcp/{name}/trust|untrust`, Settings → MCP servers. See `docs/features/mcp.md`.
- **`internal/bgtask`** - process-wide, session-scoped pool for work that outlives a tool call. A task is whatever a **`Runner`** starts (**`CommandRunner`** for shell commands) or whatever a **`LaunchFunc`** hands to **`Pool.Launch`**: that is how a subagent run becomes a **`KindAgent`** task, with **`Spec.Agent`** / **`Snapshot.Agent`** `{name, session_id}` naming the child session. One scheduling path (`start`) serves `Start`, `Adopt` and `Launch`; do not add a second scheduling mechanism or status surface.
- **`internal/subagents`** - subagent **definitions** (markdown + YAML frontmatter, `definition.go`: aliases, bounds, SHA-256 digest, `EffectiveTools` / `NarrowPermissionMode` / `ResolveTimeoutSeconds`), the **loader** (`loader.go`: `subagents.dirs` precedence, canonical **scopes** `builtin` / `user` / `project`, `deny` never reads project dirs), the **trust receipts** for project-scope files (`trust.go`: `TrustStore` at `<home>/subagents-trust.json`, `Decide`, keyed by canonical workspace + name + digest under `subagents.project_trust`), the process-wide **`Limiter`** (refuses, never queues), and the **catalog** (`catalog.go`: `BuildCatalog`, `PromptBlock`, `WriteListing`). Pure decisions only: no sessions, no loop. **`internal/agent/subagent.go`** applies them (spawn hook, permission relay, report; once the spawning turn has ended the relay hands a detached child's prompt to a **`DetachedPermissionBroker`** - the console's modal, or in `coddy serve` **`serve.Runtime`**, which offers it to every surface that is up (the HTTP server: the parent chat and a `--remote` console; a Telegram bot: the session's chat) and takes the first answer - and refuses with a reason where no broker exists; `spawn_agent` `resume` continues a finished run of the session in its own child instead of a second child starting over) and **`internal/session/subagent.go`** owns child sessions (ordinary `sess_` ids, bundles nested under `<parent>/subagents/`, `ErrSubagentReadOnly`, `DeleteSessionTree`, and `SubagentSpec.Resume`, which reopens a finished child from its bundle for the next run). Approval surfaces: `coddy agents list|trust|untrust` and `POST /coddy/subagents/{name}/trust|untrust`; **Settings → Subagents** in the web UI lists the catalog read-only. See `docs/features/subagents.md`.
- **`internal/hooks`** - operator **lifecycle hooks**: commands that read one JSON document on stdin at a point of a session and answer with an exit code plus optional JSON (`definition.go`: Claude Code's file shape, handler fields, `Parse`; `matcher.go`: exact-list-or-regex matchers with tool-name aliases such as `Bash` -> `run_command`; `loader.go`: `hooks.files`, canonical **scopes** `user` / `project`, `deny` never reads project files, `Source.Runnable`; `trust.go`: `TrustStore` at `<home>/hooks-trust.json`, receipts keyed by canonical workspace + workspace-relative file + digest under `hooks.project_trust`, `Loader.WithStore`; `catalog.go`: `BuildCatalog`, `FindSource`, `WriteListing`; `runner.go`: payload, sequential execution, timeouts and process groups via `internal/platform`, the exit-code contract, `Outcome` merging with the most restrictive decision winning). Pure decisions only: no sessions, no loop. **`internal/agent/hooks.go`** applies them: `PreToolUse` before the permission gate and `PostToolUse` / `PostToolUseFailure` after the tool in `executeToolCall`, `UserPromptSubmit` (reject, or context into the `## Hook context` prompt block) and `Stop` (follow-up submitted as a `[Stop hook]` user message, capped by `hooks.stop_loop_limit`) in the loop, `PreCompact` (veto) / `PostCompact` in `CompactSession`, `SubagentStart` (refuse, or context into the child's task) / `SubagentStop` in `subagent.go`, `Notification` (`permission_prompt`) before the permission prompt; **`internal/session/hooks.go`** fires `SessionStart` from the manager and persists its context as `hookContext`. Project-scope files follow `hooks.project_trust` like MCP declarations and subagent definitions; a held file is reported once per session as a `notice`-level UI log row. Approval surfaces: `coddy hooks list|trust|untrust`, `GET /coddy/hooks`, `POST /coddy/hooks/trust|untrust`. See `docs/features/hooks.md`.
- **`internal/skills`** - skill loading, enable/disable (`loader.go`, `disabled.go`), and remote install from repos / agents-standard marketplaces (`remote.go`, `manifest.go`, `plugin.go`: `Sync`/`SyncSource` (all or one source), `AddSource`, `RemoveRemote`/`DeleteSkill` (delete any on-disk skill; bundled = read-only via `SkillReadonly`), `ListSources`, `RemoveSource`, `CheckUpdates`, `UpdateSkill`; marketplaces added with `plugin marketplace add` in `marketplaces.go` (`AddMarketplace`, `InstallFromMarketplace` for `plugin install <plugin>@<marketplace>`, `UpdateMarketplace`; kept in `${CODDY_HOME}/skills/.marketplaces.json`, only installed plugins are updated, while a `skills.sources` entry keeps having every plugin installed); git clone via `internal/gitws.Clone`/`Pull`, or, for a marketplace entry `{"source":"archive"}`, a zip download in `archive.go` - https only, every address and redirect through the SSRF guard `remoteGuard`, a declared `sha256` checked, unpacked under size and entry caps with unsafe entries refused, skills taken from the plugin manifest (`skills` in `.claude-plugin/plugin.json`, else `skills/`, else a root `SKILL.md`, every `SKILL.md` only when there is no manifest), no git; materialized into `${CODDY_HOME}/skills` with a `.remote.json` lockfile that records each skill's installed `version`, the archive digest for an archive plugin without one). Marketplace plugin `version` (and `SKILL.md` frontmatter `version`) are surfaced by `InstalledVersion` and drive update detection (`compareVersions`). Default dirs: `~/.agents/skills` (global, shared with `npx skills`/`npx skillsbd`), `~/.coddy/skills` (coddy-specific), `${CWD}/.coddy/skills` (project-local). Remote sources are listed in `skills.sources` and fetched only on demand. Management parity across the CLI (`coddy skills add|sync|remove`, `coddy plugin marketplace list|add|remove|sync`, `coddy plugin install|remove|enable|disable`), the built-in `/plugin` chat command (`internal/agent/plugin_command.go`, deterministic like `/compact`; shared dispatcher `skills.RunPluginCommand` / `MarketplaceStatus` in `plugin.go`), HTTP (`/coddy/skills/*`), and the Settings → Skills UI. See `docs/features/skills.md`.
- **`internal/mention`** - the one grammar of `@` mentions (`grammar.go`: relative, absolute, `~` and `../` paths, quoted paths, `:N-M` / `#L` ranges, folders, `@session:` / `@rule:` / `@agent:` / `@coddy:`, web pages; every reading of an ambiguous token, the one that exists on disk winning), path resolution (`paths.go`), the workspace index for completion (`index.go`: `git ls-files` or a walk, cached per root, rebuilt when a mention starts), fuzzy ranking (`fuzzy.go`) and the `<coddy_attachment>` element with its display twin `ForDisplay` (`attachment.go`). Pure, no sessions. `internal/session/mentions.go` resolves a prompt's mentions once, into that user message (a mention never fails the prompt and never moves the system prompt); `mention_search.go` answers completion for every surface (`GET /coddy/mentions`); `mention_check.go` runs the resolver dry for the web composer's highlight (`POST /coddy/mentions/check`). The SPA twin `external/ui/src/ui/skills/draftAt.ts` and the Go grammar are held to the same cases, `internal/mention/testdata/grammar_cases.json` - change both or neither. See `docs/features/mentions.md`.
- **`internal/docs`** - the documentation built into the binary: **`docs/embed.go`** carries `nav.yaml` and its pages, `internal/docs` splits them at headings (GitHub anchors shared with `internal/docsgen`), rewrites their links (`coddy:<slug>#<anchor>` between pages, GitHub at the release tag for images and repository files), ranks sections with BM25F and reads a page or a section under a byte budget. Pure: it reads only the embedded tree. Every surface goes through it - `coddy_docs_search` / `coddy_docs_read`, the `@coddy:` mention, `GET /coddy/docs*`, the web reader, the console's F1 help, `coddy docs`. A new group folder under `docs/` must be added to the embed pattern. See `docs/features/built-in-docs.md`.
- **`internal/rules`** - rules catalog: one provider per root (`factory.go`: a chain of project folders - `.coddy/rules`, the shared `.agents/rules` as system `agents-dir`, `.cursor/rules`, `.claude/rules`, `.codex/rules` - of which only the first that holds a rule file is read, so another agent's mirror of the same rules is never loaded next to it; plus the operator's own `${CODDY_HOME}/rules` as system `user`, always read - `DefaultFactory(home)` adds it, and `Discover` accepts an absolute provider root for it; `Inspect` also names the folder read and the ones skipped for `coddy rules list`; nested `AGENTS.md` files have no provider, `agents.go` reads them on demand through `AgentsForPaths` for the folders a tool enters and never walks the tree), dedupe by file name with the project folder winning over `user`, the dialect picked by extension in `markdown.go` (`.mdc` = Cursor `description`/`globs`/`alwaysApply`, default manual; `.md` = Claude Code `paths`, unconditional without them; a header is read as YAML first, then by a lenient line reader because Cursor's own `globs: **/*.go` is not valid YAML), doublestar glob matching anchored at `Rule.Root` (`MatchGlob`), activation (`select.go`: `AlwaysOnRules` for the system prompt, `MatchAuto` for the paths a prompt mentions - `@path` or an editor's `file://` attachment; `scope.go`: `MatchScoped` for filesystem tool-call paths against globs and nested `AGENTS.md` subtrees; a rule a tool call activates rides in that call's result through `internal/agent/rules_activation.go`, and one a mention brings - `@name`, `@rule:name`, or one a mentioned path activates - in that user message through `internal/agent/mentions.go`; neither enters `{{.Rules}}`, and each comes once until a compaction folds it away) and the `coddy rules list` table (`list.go`: `RenderCatalog`). `LoadProjectDocs(home, cwd)` reads the agent home's `AGENTS.md` and `DESIGN.md` first, above the workspace's own pair, with nothing in `config.yaml` naming them, and `AgentsForPaths` reads the same pair (`preambleFiles`) for a folder a tool enters; `RenderPrompt` reports the paths it embedded, so `internal/session/instructions_load.go` (the `instructions.files` reader: `${CODDY_HOME}`, `${CWD}`, `~` and absolute entries) never sends the same file twice. See `docs/features/rules.md`.
Prefer extending these over growing **`cmd/`** or duplicating logic in **`external/`**.
## References
@architecture.md
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

