|
| 1 | +# cli |
| 2 | + |
| 3 | +## CLI Structure |
| 4 | + |
| 5 | +- **CLI package taxonomy**: Every package under `internal/cli/` follows: |
| 6 | + parent.go (Cmd wiring), doc.go, `cmd/root/` or `cmd/<sub>/` |
| 7 | + (implementation), `core/` (shared helpers) |
| 8 | +- **cmd/ directories**: Only cmd.go, run.go, and tests — helpers and |
| 9 | + output go to `core/` |
| 10 | +- **core/ structs**: Consolidated into a single `types.go` file |
| 11 | +- **User-facing text via assets**: All text routed through |
| 12 | + `internal/assets` with YAML-backed TextDescKeys — no inline strings |
| 13 | + in `core/` or `cmd/` packages |
| 14 | +- **config/ doc.go**: Every package under `internal/config/` must have |
| 15 | + a doc.go with the project header and a one-line package comment |
| 16 | +- **DescKey prefix**: Not CmdDescKey — `cmd.DescKeyFoo` not |
| 17 | + `cmd.CmdDescKeyFoo` (Go package hygiene, avoids stutter) |
| 18 | +- **Cobra Use: fields**: Must reference `cmd.Use*` constants, never raw |
| 19 | + strings or `cmd.DescKey*` |
| 20 | +- **Run functions exported PascalCase**: `Run`, `RunImport`, |
| 21 | + `RunArchive` etc. No private `runXXX` variants |
| 22 | +- **write/ packages write to stdio only**: Functions take |
| 23 | + `*cobra.Command`, not `io.Writer`. Exception: `write/rc` writes to |
| 24 | + `os.Stderr` because rc loads before cobra |
| 25 | +- **Package directory names singular**: Unless Go convention requires |
| 26 | + plural |
| 27 | +- **Import grouping**: stdlib — blank line — external deps (cobra, |
| 28 | + yaml) — blank line — ctx imports. Three groups, always in this order |
| 29 | +- **camelCase import aliases**: `cFlag` not `cflag`, `cfgFmt` not |
| 30 | + `cfgfmt` |
| 31 | +- **Icons and symbols as token constants**: Not unicode escapes |
| 32 | +- **Cross-cutting domain types in internal/entity**: Types used by one |
| 33 | + package stay in that package; types used across packages go to entity |
| 34 | + |
| 35 | +- Warn format strings centralized in config/warn/ — use warn.Close, |
| 36 | + warn.Write, warn.Remove, warn.Mkdir, warn.Rename, warn.Walk, warn.Getwd, |
| 37 | + warn.Readdir, warn.Marshal instead of inline format strings in log.Warn calls |
| 38 | + |
| 39 | +- Nav frontmatter title: fields must not contain ctx — frontmatter does not |
| 40 | + support backticks, so the brand stays out of nav titles entirely (Hub, not The |
| 41 | + ctx Hub). Body headings can use `ctx` since markdown supports backticks. |
| 42 | + |
| 43 | +- CLI flags and slash-commands inside headings or admonition titles must be |
| 44 | + backticked: `--keep-frontmatter=false`, `/ctx-reflect`. The title-case engine |
| 45 | + in hack/title-case-headings.py protects these patterns automatically, but |
| 46 | + authors should still backtick at write time for clarity. |
| 47 | + |
| 48 | +- File extensions inside headings must be backticked when title-case |
| 49 | + capitalization would otherwise apply: write `CONSTITUTION.md`, not |
| 50 | + CONSTITUTION.Md. The title-case engine refuses to capitalize lowercase tokens |
| 51 | + following a literal . dot, but explicit backticks remain the clearest signal. |
| 52 | +- New editor integrations include an MCP-merge test covering: create / empty |
| 53 | + file / preserve existing keys / skip when registered / reject malformed JSON |
| 54 | + |
| 55 | +- Substrate vs. artifact placement: cognitive substrate (consumed and mutated |
| 56 | + via ctx-mediated paths — `ctx agent`, `ctx decision add`, `/ctx-kb-ingest`, |
| 57 | + `/ctx-handover`, ceremonies) lives under `.context/`; project artifacts (read |
| 58 | + and edited directly by humans — `specs/`, `CLAUDE.md`, `GETTING_STARTED.md`, |
| 59 | + `docs/`) live at the project root; tool config and tool homes (`.ctxrc`, |
| 60 | + `.claude/`) live at root by dotfile/tool convention. The kb is substrate, not |
| 61 | + artifact: direct file edits remain possible per Invariant 1, but the |
| 62 | + skill-mediated path is the discipline. Rationale recorded in DECISIONS.md. |
| 63 | + |
| 64 | +## User-Facing Surface Completeness |
| 65 | + |
| 66 | +When a change adds or alters a user-facing surface — a new |
| 67 | +`ctx` subcommand, a new flag, an observable behavior change, |
| 68 | +a new exit shape, a new output line — the work is **not |
| 69 | +complete** until every one of the following has been updated |
| 70 | +in the same commit (or the same stacked PR, with the user's |
| 71 | +explicit OK): |
| 72 | + |
| 73 | +- `internal/assets/commands/commands.yaml` and |
| 74 | + `examples.yaml` for the subcommand description and example |
| 75 | +- `internal/assets/claude/skills/ctx-<area>/SKILL.md` so the |
| 76 | + agent knows the surface exists and when to trigger it |
| 77 | +- `internal/assets/integrations/copilot-cli/skills/<...>` if |
| 78 | + a parallel skill exists for the integration |
| 79 | +- `docs/recipes/<related-recipe>.md` for any recipe that |
| 80 | + already demonstrates the broader feature; consider a new |
| 81 | + recipe if the surface is its own workflow shape |
| 82 | +- `docs/cli/<command>.md` if a per-command CLI doc page |
| 83 | + exists for this surface |
| 84 | + |
| 85 | +Splitting these into a "Phase 2 / follow-up commit / future |
| 86 | +sweep" is **deferral** in the Constitution's sense, no matter |
| 87 | +how the phase is labeled. Docs are part of the deliverable, |
| 88 | +not a separable improvement. The "I can create a follow-up |
| 89 | +task" prohibition applies verbatim. |
| 90 | + |
| 91 | +Acceptable exceptions (state them in the commit body): |
| 92 | + |
| 93 | +- The surface is internal-only (no human user encounters it). |
| 94 | +- A recipe / skill genuinely does not exist for this feature |
| 95 | + area and writing one is itself a larger separable piece of |
| 96 | + work (then file the spec for that piece in the same commit, |
| 97 | + do not just defer). |
| 98 | + |
| 99 | +The Self-check before declaring a feature commit complete is: |
| 100 | +*"If a user runs `ctx help` or asks `/ctx-<area>` to do this |
| 101 | +new thing today, will the help text / skill / recipe match |
| 102 | +what the code does?"* If no, the commit is not complete. |
| 103 | + |
0 commit comments