simt-dev 0.6.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/CHANGELOG.md +162 -841
  2. package/README.md +21 -135
  3. package/agents/simt-agent.md +84 -0
  4. package/bin/simt.mjs +19 -55
  5. package/docs/decisions/ADR-009-skill-layout.md +1 -1
  6. package/docs/decisions/ADR-014-workspace-is-configuration.md +1 -1
  7. package/docs/decisions/ADR-086-the-stable-prefix-comes-first-and-the-lock-records-both-digests.md +1 -1
  8. package/docs/decisions/ADR-099-the-release-verifies-served-bytes-and-the-formula-mirrors-them.md +1 -1
  9. package/docs/decisions/ADR-106-the-harness-hook-runs-through-a-repo-shim.md +46 -0
  10. package/docs/decisions/ADR-107-an-install-can-name-one-harness-and-the-skipped-ones-are-named.md +51 -0
  11. package/docs/decisions/ADR-108-an-adoption-builds-the-index-and-a-command-refreshes-it-on-read.md +59 -0
  12. package/docs/decisions/ADR-109-simt-dev-is-its-own-consumer-and-the-guards-that-kept-it-from-being-one-are-retired.md +47 -0
  13. package/docs/decisions/ADR-110-the-lock-file-follows-the-manifest-name-and-the-old-name-is-read-until-the-next-write.md +43 -0
  14. package/docs/decisions/ADR-111-the-tool-ships-one-agent-rendered-per-harness-and-installs-it-with-init-and-a-command.md +75 -0
  15. package/docs/decisions/ADR-112-the-registry-is-what-simt-dev-ships-and-a-repo-owns-its-own-skills.md +81 -0
  16. package/docs/decisions/ADR-113-a-pre-push-that-carries-only-documentation-skips-the-test-gate-and-says-so.md +87 -0
  17. package/docs/decisions/ADR-114-the-bench-pairs-at-the-task-level-a-missing-cost-is-not-zero-and-an-unasked-cross-group-compare-is-refused.md +74 -0
  18. package/docs/decisions/ADR-115-the-index-stamps-its-schema-version-migrates-older-databases-in-place-and-serves-newer-ones-from-a-sidecar.md +69 -0
  19. package/docs/decisions/ADR-116-rust-support-reaches-the-example-path-the-indent-and-the-bench-runner.md +100 -0
  20. package/docs/decisions/ADR-117-the-darwin-release-builds-on-supported-macos-15-runners-and-a-re-run-is-idempotent.md +81 -0
  21. package/docs/decisions/ADR-118-the-bench-corpus-is-keyed-on-history-is-append-only-and-is-portable-as-a-sealed-document.md +91 -0
  22. package/docs/decisions/ADR-119-the-environment-variable-points-into-a-workspace-the-repo-declared.md +90 -0
  23. package/docs/decisions/ADR-120-a-pre-push-whose-tree-is-already-proven-green-skips-the-test-gate-on-that-evidence.md +96 -0
  24. package/docs/decisions/ADR-122-the-shims-move-into-the-state-directory-and-the-legacy-directory-removes-itself.md +94 -0
  25. package/docs/decisions/ADR-123-a-surplus-positional-is-not-refused-because-arity-is-not-in-the-source.md +57 -0
  26. package/docs/decisions/ADR-124-the-manifest-has-a-schema-in-the-tool-and-its-unknown-keys-stay-the-repositorys.md +64 -0
  27. package/docs/decisions/ADR-125-a-shared-context-belongs-to-a-read-and-a-writer-reads-directly.md +65 -0
  28. package/package.json +13 -3
  29. package/rules/core.md +9 -2
  30. package/rules/mdc/slice-commit.mdc +57 -4
  31. package/rules/mdc/test-gate.mdc +2 -0
  32. package/rules/stacks/android.md +3 -3
  33. package/rules/stacks/connectiq.md +2 -2
  34. package/rules/stacks/dart.md +1 -1
  35. package/rules/stacks/deno.md +1 -1
  36. package/rules/stacks/dotnet.md +1 -1
  37. package/rules/stacks/elixir.md +1 -1
  38. package/rules/stacks/go.md +1 -1
  39. package/rules/stacks/jvm.md +3 -2
  40. package/rules/stacks/node.md +2 -2
  41. package/rules/stacks/python.md +7 -6
  42. package/rules/stacks/ruby.md +1 -1
  43. package/rules/stacks/rust.md +1 -1
  44. package/rules/stacks/swift.md +1 -1
  45. package/rules/stacks/typescript.md +2 -2
  46. package/rules/stacks/uv.md +3 -2
  47. package/skills/simt-commands/SKILL.md +70 -0
  48. package/src/advice/advice.mjs +7 -3
  49. package/src/agenthooks/decide.mjs +141 -9
  50. package/src/agenthooks/opencode.mjs +72 -0
  51. package/src/agenthooks/prompt.mjs +10 -3
  52. package/src/agenthooks/render.mjs +186 -19
  53. package/src/agenthooks/shim.mjs +55 -0
  54. package/src/agents/render.mjs +277 -0
  55. package/src/api.mjs +29 -0
  56. package/src/argv.mjs +37 -3
  57. package/src/bench/ablate.mjs +31 -14
  58. package/src/bench/adapters.json +16 -0
  59. package/src/bench/corpus.mjs +150 -0
  60. package/src/bench/grade.mjs +23 -49
  61. package/src/bench/mine.mjs +247 -36
  62. package/src/bench/probes.mjs +104 -68
  63. package/src/bench/process.mjs +128 -0
  64. package/src/bench/report.mjs +260 -84
  65. package/src/bench/run.mjs +251 -150
  66. package/src/bench/variants.mjs +112 -40
  67. package/src/brief/assemble.mjs +44 -3
  68. package/src/ci/workflow.mjs +46 -14
  69. package/src/cli.mjs +45 -0
  70. package/src/commands/agent.mjs +205 -0
  71. package/src/commands/agenthook.mjs +113 -20
  72. package/src/commands/bench.mjs +192 -18
  73. package/src/commands/brief.mjs +5 -4
  74. package/src/commands/ci.mjs +2 -1
  75. package/src/commands/compare.mjs +50 -49
  76. package/src/commands/completions.mjs +2 -6
  77. package/src/commands/decide.mjs +10 -9
  78. package/src/commands/distill.mjs +4 -3
  79. package/src/commands/doctor.mjs +11 -7
  80. package/src/commands/evidence.mjs +19 -12
  81. package/src/commands/explain.mjs +0 -1
  82. package/src/commands/gate.mjs +98 -28
  83. package/src/commands/graph.mjs +20 -8
  84. package/src/commands/harness.mjs +3 -2
  85. package/src/commands/help.mjs +160 -31
  86. package/src/commands/hooks.mjs +163 -50
  87. package/src/commands/ignore.mjs +2 -1
  88. package/src/commands/impact.mjs +8 -9
  89. package/src/commands/index.mjs +278 -180
  90. package/src/commands/init.mjs +171 -38
  91. package/src/commands/lease.mjs +206 -39
  92. package/src/commands/lessons.mjs +72 -15
  93. package/src/commands/local.mjs +3 -3
  94. package/src/commands/map.mjs +47 -27
  95. package/src/commands/mcp.mjs +0 -1
  96. package/src/commands/pack.mjs +9 -6
  97. package/src/commands/plan.mjs +6 -5
  98. package/src/commands/plugin.mjs +3 -3
  99. package/src/commands/prbody.mjs +3 -3
  100. package/src/commands/repoindex.mjs +5 -3
  101. package/src/commands/review.mjs +2 -2
  102. package/src/commands/rules.mjs +124 -33
  103. package/src/commands/shared.mjs +2 -3
  104. package/src/commands/skills.mjs +82 -45
  105. package/src/commands/skillscompile.mjs +12 -12
  106. package/src/commands/skillsinstall.mjs +14 -2
  107. package/src/commands/slice.mjs +396 -26
  108. package/src/commands/spec.mjs +151 -0
  109. package/src/commands/status.mjs +4 -7
  110. package/src/commands/sync.mjs +50 -33
  111. package/src/commands/tests.mjs +11 -11
  112. package/src/commands/tokens.mjs +2 -2
  113. package/src/commands/undo.mjs +29 -31
  114. package/src/commands/upgrade.mjs +40 -4
  115. package/src/commands/why.mjs +19 -12
  116. package/src/compare/hooksstate.mjs +23 -1
  117. package/src/compare/learn.mjs +82 -57
  118. package/src/compare/parallel.mjs +50 -0
  119. package/src/compare/row.mjs +26 -3
  120. package/src/completions.mjs +5 -0
  121. package/src/context.mjs +64 -0
  122. package/src/decisions/dir.mjs +84 -0
  123. package/src/decisions/index-doc.mjs +46 -0
  124. package/src/decisions/index.mjs +109 -11
  125. package/src/diff.mjs +1 -1
  126. package/src/distill/distill.mjs +17 -2
  127. package/src/distill/engine.mjs +5 -1
  128. package/src/ecosystem/node-runner.mjs +144 -0
  129. package/src/explain.mjs +18 -6
  130. package/src/graph/load.mjs +42 -20
  131. package/src/hooks/capability.mjs +17 -4
  132. package/src/hooks/dispatcher.mjs +513 -36
  133. package/src/hooks/gates.mjs +0 -43
  134. package/src/hooks/resolve.mjs +11 -10
  135. package/src/hooks/shim.mjs +17 -9
  136. package/src/identity.mjs +96 -15
  137. package/src/index/extract.mjs +192 -11
  138. package/src/index/persist.mjs +307 -47
  139. package/src/index/store.mjs +263 -60
  140. package/src/index/tests.mjs +6 -35
  141. package/src/journal.mjs +372 -84
  142. package/src/kernel/card.mjs +37 -12
  143. package/src/kernel/checks.mjs +44 -9
  144. package/src/kernel/variants.mjs +27 -16
  145. package/src/lease.mjs +97 -15
  146. package/src/ledger/index.mjs +120 -25
  147. package/src/local/client.mjs +4 -6
  148. package/src/locate.mjs +33 -9
  149. package/src/manifest/schema.mjs +213 -0
  150. package/src/mcp/resources.mjs +12 -13
  151. package/src/mcp/server.mjs +151 -38
  152. package/src/mcp/tools.mjs +90 -16
  153. package/src/measure/session.mjs +3 -2
  154. package/src/memory/decide.mjs +98 -43
  155. package/src/memory/lessons.mjs +168 -33
  156. package/src/name.mjs +9 -0
  157. package/src/node-version.mjs +37 -0
  158. package/src/output/index.mjs +111 -3
  159. package/src/packs/manage.mjs +17 -3
  160. package/src/packs.mjs +20 -2
  161. package/src/plan/index.mjs +31 -8
  162. package/src/plan/template.mjs +27 -5
  163. package/src/plugins.mjs +6 -2
  164. package/src/prbody.mjs +7 -5
  165. package/src/projection/baselines.mjs +1 -1
  166. package/src/projection/disclosure.mjs +2 -2
  167. package/src/projection/loaders.mjs +41 -1
  168. package/src/projection/lock.mjs +31 -14
  169. package/src/projection/manifest.mjs +89 -53
  170. package/src/projection/markers.mjs +43 -3
  171. package/src/projection/mdc.mjs +34 -7
  172. package/src/projection/project.mjs +162 -18
  173. package/src/projection/rules.mjs +58 -6
  174. package/src/projection/surfaces.mjs +9 -1
  175. package/src/repo-root.mjs +88 -0
  176. package/src/resolve/body.mjs +4 -0
  177. package/src/resolve/fingerprint.mjs +24 -15
  178. package/src/resolve/local.mjs +58 -0
  179. package/src/review/critic.mjs +6 -3
  180. package/src/secrets.mjs +165 -22
  181. package/src/state.mjs +76 -0
  182. package/src/status.mjs +9 -4
  183. package/src/toolwords.mjs +8 -0
  184. package/src/tsconfig.mjs +39 -99
  185. package/src/workspace.mjs +55 -10
  186. package/templates/plan/tracker.json +1 -1
  187. package/docs/decisions/README.md +0 -591
@@ -1,4 +1,5 @@
1
- import { emit, fail, jsonFailMode } from '../output/index.mjs';
1
+ import { emit, emitJson, fail, jsonFailMode } from '../output/index.mjs';
2
+ import { SPEC, SPEC_PATHS, tierOf } from './spec.mjs';
2
3
  import { parse, rawJsonFlag } from '../argv.mjs';
3
4
  import { CLI, MANIFEST_FILE, STATE_DIR } from '../name.mjs';
4
5
  import { SESSION_ENV } from '../agenthooks/policy.mjs';
@@ -47,6 +48,7 @@ ${CLI} rules list the rule catalogue (--root marks what applies to a
47
48
  ${CLI} rules show <id> one rule body
48
49
  ${CLI} rules why <id|ADR-NNN> the decision that settled it, and whether it is still in force
49
50
  (--all prints every ruling, not the first two)
51
+ ${CLI} rules decisions every known decision, shipped and repo, and which layer each came from
50
52
  ${CLI} hooks install [--yes] install the shared dispatcher; report what it would run
51
53
  (dry-run unless --yes; refuses to take over routing git
52
54
  already uses, and --yes is that consent)
@@ -58,11 +60,16 @@ ${CLI} agent-hook <event> the entry point a harness calls: pre-tool-use dec
58
60
  (reads one JSON payload on stdin and answers in that
59
61
  harness's dialect; exit 2 blocks; needs --harness)
60
62
  ${CLI} agent-hooks install write this repo's policies into the harnesses that
61
- read a hooks file
63
+ read a hooks file (--harness <id> writes one, not all)
62
64
  (dry-run unless --yes; merges into the file, keeps every
63
65
  key it does not own; Codex needs the hook trusted)
64
66
  ${CLI} agent-hooks status what each harness will run, and what is not trusted yet
65
- ${CLI} plugin <claude|gemini|cursor> generate a harness package from the same tables
67
+ ${CLI} agents install write the Simt-Agent into the harnesses that read an
68
+ agents file (--harness <id> writes one, not all; dry-run
69
+ unless --yes; markdown with a frontmatter the harness
70
+ documents, TOML for Codex)
71
+ ${CLI} agents status what each harness carries, and what is not installed yet
72
+ ${CLI} plugin <claude|gemini|cursor|opencode> generate a harness package from the same tables
66
73
  (dry-run unless --yes; writes a self-contained runtime, the
67
74
  hooks and MCP registration, and for cursor the kernel rules;
68
75
  the skill registry is workspace data and is not shipped)
@@ -77,17 +84,24 @@ ${CLI} plan status count tracker rows
77
84
  ${CLI} plan next [<id>] one ready slice's section, or the one named; ~1 KB
78
85
  ${CLI} plan done <id> [<sha>] mark one row; dry-run unless --yes
79
86
  ${CLI} slice start <id> give a slice its own worktree and branch, preload the
80
- brief, and mark the row In progress (dry-run unless
81
- --yes; the worktree is ${STATE_DIR}/worktrees/<id> on
82
- branch slice/<id>, the brief ${STATE_DIR}/brief.md in it)
87
+ brief, and mark the row started (dry-run unless
88
+ --yes; the worktree is ${STATE_DIR}/local/worktrees/<id>
89
+ on branch slice/<id>, the brief ${STATE_DIR}/local/briefs/<id>.md
90
+ beside it; --session <id> leases the slice)
83
91
  ${CLI} slice finish <id> run the pre-commit gates, commit what is staged with a
84
92
  Conventional subject from the row's title, then record
85
93
  the SHA and the --test command on the row (dry-run
86
- unless --yes; --type picks the type, default feat)
94
+ unless --yes; --type picks the type, default feat;
95
+ refuses unless HEAD is the slice branch; gates the
96
+ ledger already proves for this tree are not re-run)
97
+ ${CLI} slice abandon <id> remove the worktree, delete the branch, and return
98
+ the row to ready (dry-run unless --yes; refuses from
99
+ inside the worktree)
87
100
  ${CLI} lease [<paths>...] claim files for one session and a TTL, or list the
88
101
  claims (dry-run unless --yes; the session is --session
89
102
  <id> or ${SESSION_ENV}; a pre-edit hook refuses an edit
90
- to a path another session holds — ADR-101)
103
+ to a path another session holds — ADR-101;
104
+ release and renew end and extend your own claims)
91
105
  ${CLI} graph lint check the skill registry against the skills on disk
92
106
  (byte counts, dangling edges, unresolvable variants)
93
107
  ${CLI} ci simt-dev's own .github files: create or refresh them
@@ -100,7 +114,8 @@ ${CLI} ignore [--yes] render the one .simtignore list into .cursorignore
100
114
  Claude settings file is merged, never replaced)
101
115
  ${CLI} compare org-wide matrix across every sibling repo
102
116
  (read-only; exits non-zero on defects, not on
103
- repos that have not adopted yet)
117
+ repos that have not adopted yet;
118
+ --concurrency <n> caps the rows built at once)
104
119
  ${CLI} index rebuild the local file index from git blob hashes
105
120
  ${CLI} why [path] what governs a file: a decision whose Affects line names
106
121
  it, a rule whose scope matches it, a skill, a gate
@@ -127,10 +142,11 @@ ${CLI} pr-body [--base <ref>] a pull request body: the evidence block for this
127
142
  ${CLI} evidence the runs recorded against the tree on disk
128
143
  ${CLI} evidence check <file> a commit message that claims a run this tree does not
129
144
  have: a warning, or a block when gates.evidence is "block"
130
- ${CLI} lessons list deterministic lesson candidates in .simt/lessons.jsonl
131
- and promoted lessons in docs/lessons/*.md
145
+ ${CLI} lessons list deterministic lesson candidates in .simt/local/lessons.jsonl
146
+ and promoted lessons in docs/lessons/*.md (reads nothing else)
147
+ ${CLI} lessons capture scan the ledger and git history for new candidates (the write half of list)
132
148
  ${CLI} lessons review [<id>] promote a lesson candidate to docs/lessons/NNNN-<slug>.md
133
- (dry-run unless --yes; writes Appendix A frontmatter)
149
+ (dry-run unless --yes; writes the lesson frontmatter)
134
150
  ${CLI} explain <CODE> what a check finding means: the decision behind it, how
135
151
  to fix it, and a concrete trigger (DRIFT, UNMANAGED,
136
152
  PACK-DIGEST-MISMATCH, ...; case-insensitive)
@@ -147,17 +163,17 @@ ${CLI} local doctor probe configured local model endpoints (chat and
147
163
  unset; refuses non-loopback URLs without --allow-remote)
148
164
  ${CLI} distill <skill|doc> a budgeted proposal summary of a skill body or doc,
149
165
  every claim citing the source line range it rests on
150
- (writes .simt/distilled/<digest>.md; a proposal a
166
+ (writes .simt/local/distilled/<digest>.md; a proposal a
151
167
  human merges - never projected or committed;
152
168
  deterministic outline by default; --local proposes
153
169
  from the configured chat model instead; the cache is
154
170
  keyed by source digest and budget, --refresh regenerates)
155
171
  ${CLI} review [--local] an ADR-008 verdict skeleton for the staged diff,
156
- the brief listing everything that governs it (R25)
172
+ the brief listing everything that governs it
157
173
  (deterministic without --local: the brief and its
158
174
  governing ids only; --local proposes from the
159
175
  configured chat model and writes
160
- .simt/reviews/<digest>.md — a proposal a human
176
+ .simt/local/reviews/<digest>.md — a proposal a human
161
177
  reads, verified against the verdict vocabulary and
162
178
  the brief's ids; advisory always, never a gate)
163
179
  ${CLI} shared plan what a workspace's repeated rules cost to carry
@@ -197,13 +213,23 @@ ${CLI} bench probes [--yes] 12 synthetic rule-compliance probes with determin
197
213
  (dry-run unless --yes; grades git state, transcript and ledger,
198
214
  and reports compliance per arm)
199
215
  ${CLI} bench variants [--yes] evaluate simt-variants rule slots on the canary suite and lock
200
- winning variants in simt-dev.lock.json (dry-run unless --yes;
216
+ winning variants in simt.lock.json (dry-run unless --yes;
201
217
  re-opens when model or harness changes)
202
218
  ${CLI} bench critic precision and recall of the local critic over a labelled
203
- set of historical diffs (.simt/critic-labels.jsonl;
219
+ set of historical diffs (.simt/local/critic-labels.jsonl;
204
220
  overrides --labels; labels are human-written verdicts
205
221
  under ADR-008; the critic is advisory until precision
206
222
  reaches 0.8 on 30 labelled diffs)
223
+ ${CLI} bench export <file> [--yes]
224
+ sealed export of the mined corpus: every task
225
+ descriptor and both hidden patches in one signed
226
+ JSON file, for import on another machine
227
+ (dry-run unless --yes; --root selects the repository)
228
+ ${CLI} bench import <file> [--yes]
229
+ import a sealed corpus: verify the seal, then write
230
+ the task descriptors and hidden patches into the
231
+ repository's vault (dry-run unless --yes; --root
232
+ selects the repository)
207
233
  ${CLI} pack new <name> --source <spec>
208
234
  scaffold a versioned pack: pack.json, an unmeasured
209
235
  bench.json, and one stack rule (dry-run unless --yes;
@@ -240,6 +266,12 @@ const FLAGS = `--json structured output (compact; one document,
240
266
  --workspace <path> where the skill sources live; "off" declines it
241
267
  (default: SIMT_DEV_WORKSPACE, then the user config
242
268
  file, then the sibling heuristic if it verifies)
269
+ --registry <path> which skill registry to read (skills commands and
270
+ graph lint; default: SIMT_REGISTRY, then the checkout's
271
+ own graph/skills.graph.json, then the one member of the
272
+ workspace that holds one)
273
+ --concurrency <n> compare: rows built at once (default 4, or
274
+ SIMT_CONCURRENCY; order-preserving whatever it is)
243
275
  --dry-run report what would change, change nothing
244
276
  --yes assume yes; required in automation, never prompts
245
277
  --check report drift and write nothing (sync, ci)
@@ -301,8 +333,14 @@ const FLAGS = `--json structured output (compact; one document,
301
333
  --embed index / map / brief: compute or query optional local vector embeddings
302
334
  (off by default unless recall@10 gains at least 10 points)
303
335
  --labels <path> bench critic: the labelled set of historical diffs to score
336
+ --cross-group bench ablate: compare across harness, model and experiment
337
+ groups instead of refusing the comparison
338
+ --expires <when> lessons capture: how long a captured candidate stays eligible
304
339
  (default .simt/critic-labels.jsonl; one human-written
305
340
  {sha, verdict} pair per line)
341
+ --replace bench mine: delete task files the current mine does not
342
+ reproduce (off by default: mining is append-only; a
343
+ re-mine adds, it does not replace the corpus)
306
344
  --refresh distill / review: regenerate a proposal that is already cached
307
345
  --local distill / review: propose the answer from the configured
308
346
  local chat model (local.chat); off by default, which
@@ -317,17 +355,69 @@ const PRINCIPLES = `design rules:
317
355
  zero dependencies a DX tool that needs an install step fails on a fresh machine
318
356
  bounded output one fact per line, fixed field order, explicit TRUNCATED lines`;
319
357
 
320
- export async function help(argv) {
321
- const parsed = parse(argv);
358
+ export async function help(argv, dispatched) {
359
+ // `dispatched` is what `dispatch` already parsed. Called from a test or another
360
+ // command with only argv, help parses it itself.
361
+ const parsed =
362
+ dispatched === undefined
363
+ ? parse(argv)
364
+ : dispatched.parseError === undefined
365
+ ? { ok: true, value: { flags: dispatched.flags, positional: dispatched.positional } }
366
+ : { ok: false, error: dispatched.parseError };
322
367
  if (!parsed.ok) {
323
368
  jsonFailMode(rawJsonFlag(argv));
324
369
  return fail(parsed.error.message, parsed.error.code);
325
370
  }
326
- const { flags } = parsed.value;
371
+ const { flags, positional } = parsed.value;
327
372
  if (flags.version) {
328
373
  emit((await version()).trim());
329
374
  return 0;
330
375
  }
376
+ // A reader who names one command gets that command, not the whole surface (review
377
+ // 0003 Q61): `simt help gate` and `simt gate --help` both printed 23 KB, so the one
378
+ // fact being asked for was 40 screens down.
379
+ const asked = commandFromHelp(positional);
380
+ if (asked !== null) {
381
+ emit(commandPage(asked));
382
+ return 0;
383
+ }
384
+ if (flags.json === true) {
385
+ emitJson(SPEC.map(specRow));
386
+ return 0;
387
+ }
388
+ // `--all` prints every command and every flag; the default page lists the core tier and
389
+ // says where the rest is. A reader asking "what can this do" needs the shape, not 23 KB
390
+ // of flag reference (review 0003 Q61).
391
+ if (flags.all === true) {
392
+ emit(
393
+ [
394
+ 'simt-dev - shared agent rules, skills and gates for a repo ecosystem',
395
+ '',
396
+ `usage: ${CLI} <command> [args] [flags]`,
397
+ '',
398
+ 'commands:',
399
+ COMMANDS,
400
+ '',
401
+ 'flags:',
402
+ FLAGS,
403
+ '',
404
+ PRINCIPLES,
405
+ '',
406
+ `rulings ${CLI} rules list the decisions behind the rules you just read`,
407
+ ].join('\n'),
408
+ );
409
+ return 0;
410
+ }
411
+ // One line per group and one per bare command: listing every subcommand with its
412
+ // summary does not fit in the 2 KB a reader will actually read (review 0003 Q61).
413
+ const groups = new Map();
414
+ const bare = [];
415
+ for (const row of SPEC) {
416
+ if (tierOf(row.path) !== 'core') continue;
417
+ const [head, sub] = row.path.split(' ');
418
+ if (sub === undefined) bare.push(row);
419
+ else groups.set(head, [...(groups.get(head) ?? []), sub]);
420
+ }
331
421
  emit(
332
422
  [
333
423
  'simt-dev - shared agent rules, skills and gates for a repo ecosystem',
@@ -335,19 +425,12 @@ export async function help(argv) {
335
425
  `usage: ${CLI} <command> [args] [flags]`,
336
426
  '',
337
427
  'commands:',
338
- COMMANDS,
339
- '',
340
- 'flags:',
341
- FLAGS,
428
+ ...[...groups].map(([group, subs]) => ` ${CLI} ${group} <${subs.join('|')}>`),
429
+ ...bare.map((row) => ` ${CLI} ${row.path.padEnd(8)} ${row.summary.length > 46 ? `${row.summary.slice(0, 43)}...` : row.summary}`),
342
430
  '',
343
- PRINCIPLES,
344
- '',
345
- // The plans ship inside the installed package but have no command that surfaces
346
- // them, so naming the path would again be advice the reader cannot take. They
347
- // are an implementation record; the rulings are the part a reader can act on.
348
- `rulings ${CLI} rules list the decisions behind the rules you just read`,
431
+ `more ${CLI} help --all (every command and flag), ${CLI} help <command>`,
432
+ `rulings ${CLI} rules list (the decisions behind the rules)`,
349
433
  ].join('\n'),
350
- { maxBytes: flags.maxBytes ?? 0 },
351
434
  );
352
435
  return 0;
353
436
  }
@@ -356,3 +439,49 @@ async function version() {
356
439
  const { describeVersion } = await import('../package-version.mjs');
357
440
  return `${describeVersion()}\n`;
358
441
  }
442
+
443
+ /** The command a help request names, or null when it names none. */
444
+ function commandFromHelp(positional) {
445
+ const words = positional[0] === 'help' ? positional.slice(1) : positional;
446
+ const two = words.slice(0, 2).join(' ');
447
+ if (SPEC_PATHS.includes(two)) return two;
448
+ return SPEC_PATHS.includes(words[0]) ? words[0] : null;
449
+ }
450
+
451
+ /** One spec row as the JSON a UI or a harness reads. */
452
+ function specRow(row) {
453
+ return { path: row.path, summary: row.summary, ...(row.defaultFor === undefined ? {} : { defaultFor: row.defaultFor }) };
454
+ }
455
+
456
+ const PAGE_WIDTH = 78;
457
+
458
+ /** The summary broken to the page width, so a narrow terminal keeps it readable. */
459
+ function wrapped(text) {
460
+ const lines = [];
461
+ let line = '';
462
+ for (const word of text.split(' ')) {
463
+ if (line !== '' && line.length + 1 + word.length > PAGE_WIDTH) {
464
+ lines.push(line);
465
+ line = word;
466
+ } else {
467
+ line = line === '' ? word : `${line} ${word}`;
468
+ }
469
+ }
470
+ if (line !== '') lines.push(line);
471
+ return lines;
472
+ }
473
+
474
+ /**
475
+ * One command's page: what it is, and where the rest of the answers are. The flags a
476
+ * single command declares arrive with the spec's `flags` field (Q62); until then the
477
+ * page points at the global list rather than inventing one per command.
478
+ */
479
+ export function commandPage(path) {
480
+ const row = SPEC.find((r) => r.path === path);
481
+ if (row === undefined) return '';
482
+ const lines = [`${CLI} ${path} [args] [flags]`, '', ...wrapped(row.summary), ''];
483
+ if (row.defaultFor !== undefined) lines.push(`default ${CLI} ${row.defaultFor} runs this command`);
484
+ lines.push(`flags --json --pretty --root --max-bytes, and every global flag`);
485
+ lines.push(`all ${CLI} --help every command, and every flag it takes`);
486
+ return `${lines.join('\n')}\n`;
487
+ }