@event4u/agent-config 14.20.0 → 14.22.0

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 (242) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +441 -0
  5. package/CREDITS.md +37 -0
  6. package/README.md +1 -1
  7. package/agents/templates/.ai-council.yml.example +10 -0
  8. package/dist/agent-src/commands/analyze/inbox.md +381 -6
  9. package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +251 -34
  10. package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
  11. package/dist/agent-src/commands/analyze.md +12 -7
  12. package/dist/agent-src/commands/feature/plan.md +55 -16
  13. package/dist/agent-src/commands/feature/refactor.md +8 -3
  14. package/dist/agent-src/commands/humanize.md +48 -4
  15. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
  16. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
  17. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
  18. package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
  19. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
  20. package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
  21. package/dist/agent-src/rules/evaluator-independence.md +4 -4
  22. package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
  23. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  24. package/dist/agent-src/rules/token-efficiency.md +26 -3
  25. package/dist/agent-src/scripts/_cli_entry.ts +51 -0
  26. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
  27. package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
  28. package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
  29. package/dist/agent-src/scripts/stubs_due.ts +4 -18
  30. package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
  31. package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
  32. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  33. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
  34. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
  35. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  36. package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
  37. package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
  38. package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
  39. package/dist/agent-src/templates/agent-settings.md +0 -1
  40. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  41. package/dist/agent-src/templates/features.md +5 -0
  42. package/dist/agent-src/templates/roadmaps.md +15 -0
  43. package/dist/cli-delegate/_cli_entry.js +8 -0
  44. package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
  45. package/dist/cli-delegate/archival_sweep.js +8 -0
  46. package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
  47. package/dist/cli-delegate/check_update_banner.js +923 -0
  48. package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
  49. package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
  50. package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
  51. package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
  52. package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-BIXEDQPZ.js} +7 -1
  53. package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
  54. package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
  55. package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
  56. package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
  57. package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
  58. package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
  59. package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
  60. package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
  61. package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
  62. package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
  63. package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
  64. package/dist/cli-delegate/cmd_conformance.js +19 -5
  65. package/dist/cli-delegate/cmd_doctor.js +3 -3
  66. package/dist/cli-delegate/cmd_export.js +2 -2
  67. package/dist/cli-delegate/cmd_handoff.js +27 -3
  68. package/dist/cli-delegate/cmd_prune.js +2 -2
  69. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  70. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  71. package/dist/cli-delegate/cmd_sync.js +2 -2
  72. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  73. package/dist/cli-delegate/cmd_validate.js +2 -2
  74. package/dist/cli-delegate/dashboard_mode.js +16 -0
  75. package/dist/cli-delegate/gate_budget.js +24 -0
  76. package/dist/cli-delegate/gate_execute.js +25 -0
  77. package/dist/cli-delegate/guarded_baseline.js +18 -0
  78. package/dist/cli-delegate/resume_probe.js +22 -0
  79. package/dist/cli-delegate/roadmap_gates.js +638 -0
  80. package/dist/cli-delegate/roadmap_set_step.js +173 -0
  81. package/dist/cli-delegate/stub_queue.js +47 -0
  82. package/dist/cli-delegate/stubs_due.js +38 -0
  83. package/dist/cli-delegate/ticket_bundles.js +59 -0
  84. package/dist/cli-delegate/update_roadmap_progress.js +60 -0
  85. package/dist/discovery/deprecation-report.md +1 -1
  86. package/dist/discovery/discovery-manifest.json +60 -29
  87. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  88. package/dist/discovery/discovery-manifest.summary.md +3 -3
  89. package/dist/discovery/dormancy-report.md +1 -1
  90. package/dist/discovery/orphan-report.md +1 -1
  91. package/dist/discovery/packs.json +7 -6
  92. package/dist/discovery/trust-report.md +2 -2
  93. package/dist/discovery/workspaces.json +6 -5
  94. package/dist/hooks/dispatch.js +1522 -1008
  95. package/dist/install/claudeLayerCarriage.js +237 -0
  96. package/dist/install/claudePathsPlan.js +79 -0
  97. package/dist/install/hostLayerFingerprint.js +1 -1
  98. package/dist/install/install.mjs +10 -1
  99. package/dist/install/partitionEligibility.js +231 -117
  100. package/dist/install/ruleLayerPartition.js +10 -7
  101. package/dist/mcp/registry-manifest.json +3 -3
  102. package/dist/mcp/server.json +2 -2
  103. package/dist/mcp/server.mjs +39376 -38902
  104. package/dist/scripts/ai_council/content_ceiling.js +63 -0
  105. package/dist/scripts/ai_council/seat_policy.js +106 -0
  106. package/dist/scripts/ai_council/transport_resolver.js +21 -0
  107. package/dist/scripts/condense.js +32 -25
  108. package/docs/MIGRATION.md +49 -1
  109. package/docs/contracts/settings-classes.md +1 -1
  110. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
  111. package/llms.txt +14 -14
  112. package/package.json +7 -4
  113. package/src/agent-src/scripts/_cli_entry.ts +51 -0
  114. package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
  115. package/src/agent-src/scripts/roadmap_gates.ts +4 -20
  116. package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
  117. package/src/agent-src/scripts/stubs_due.ts +4 -18
  118. package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
  119. package/src/config/continuity-surface.json +509 -0
  120. package/src/config/estate-count-budget.json +1 -1
  121. package/src/config/gate-coverage.yml +29 -8
  122. package/src/config/gate-violation-baselines.json +8 -8
  123. package/src/config/gitignore-block.txt +19 -0
  124. package/src/config/host-injection-effect.json +73 -0
  125. package/src/config/packed-binary-manifest.json +33 -0
  126. package/src/config/preamble-payload-budget.json +112 -105
  127. package/src/config/publish-surface.json +4 -2
  128. package/src/config/release-gate-locality.yml +4 -3
  129. package/src/install/claudeLayerCarriage.ts +273 -0
  130. package/src/install/claudePathsPlan.ts +76 -0
  131. package/src/install/hostLayerFingerprint.ts +1 -1
  132. package/src/install/partitionEligibility.ts +261 -127
  133. package/src/install/ruleLayerPartition.ts +19 -23
  134. package/src/scripts/_dispatch.bash +77 -27
  135. package/src/scripts/_lib/auto_dispatch.ts +2 -2
  136. package/src/scripts/_lib/blocked_by_marker.ts +189 -0
  137. package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
  138. package/src/scripts/_lib/continuity_surface.ts +296 -0
  139. package/src/scripts/_lib/corpus_manifest.ts +26 -9
  140. package/src/scripts/_lib/hook_settings.ts +1 -1
  141. package/src/scripts/_lib/host_capability.ts +38 -1
  142. package/src/scripts/_lib/injection_effect.ts +151 -0
  143. package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
  144. package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
  145. package/src/scripts/_lib/pin_resolver.ts +13 -0
  146. package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
  147. package/src/scripts/_lib/release_highlights.ts +19 -211
  148. package/src/scripts/_lib/release_material.ts +27 -77
  149. package/src/scripts/_lib/retired_status.ts +74 -0
  150. package/src/scripts/_lib/skill_catalogue.ts +156 -21
  151. package/src/scripts/_lib/structured_ask.ts +117 -0
  152. package/src/scripts/_lib/subagent_capsule.ts +117 -8
  153. package/src/scripts/_lib/tty_prompt.ts +76 -0
  154. package/src/scripts/ai_council/api_transport.ts +168 -0
  155. package/src/scripts/ai_council/clients.ts +58 -63
  156. package/src/scripts/ai_council/config.ts +13 -0
  157. package/src/scripts/ai_council/content_ceiling.ts +79 -0
  158. package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
  159. package/src/scripts/ai_council/jury_aggregate.ts +178 -0
  160. package/src/scripts/ai_council/prompts.ts +9 -0
  161. package/src/scripts/ai_council/seat_policy.ts +137 -0
  162. package/src/scripts/ai_council/status_surface.ts +79 -0
  163. package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
  164. package/src/scripts/ai_council/transport_resolver.ts +59 -1
  165. package/src/scripts/ai_tells_rules.ts +446 -5
  166. package/src/scripts/ask_block_census.ts +499 -0
  167. package/src/scripts/bench_humanizer_eval.ts +264 -40
  168. package/src/scripts/bench_prose_tells_fp.ts +198 -0
  169. package/src/scripts/capture_skill_catalogue.ts +24 -11
  170. package/src/scripts/check_branch_work_committed.ts +234 -0
  171. package/src/scripts/check_candidate_lines.ts +499 -0
  172. package/src/scripts/check_continuity_surface.ts +475 -0
  173. package/src/scripts/check_dependency_floors.ts +13 -6
  174. package/src/scripts/check_estate_count.ts +40 -79
  175. package/src/scripts/check_pack_size.ts +263 -24
  176. package/src/scripts/check_release_highlights.ts +0 -157
  177. package/src/scripts/check_roadmap_trackable.ts +19 -16
  178. package/src/scripts/check_rule_layer_partition.ts +54 -34
  179. package/src/scripts/check_single_delivery.ts +97 -31
  180. package/src/scripts/check_update_banner.ts +13 -0
  181. package/src/scripts/code_graph/build.ts +138 -15
  182. package/src/scripts/code_graph/cli.ts +76 -8
  183. package/src/scripts/code_graph/extract.ts +11 -0
  184. package/src/scripts/code_graph/grammar_probe.ts +43 -0
  185. package/src/scripts/code_graph/loader.ts +88 -14
  186. package/src/scripts/code_graph/query.ts +121 -11
  187. package/src/scripts/code_graph/resolution_tiers.ts +238 -0
  188. package/src/scripts/code_graph/sqlite_store.ts +247 -6
  189. package/src/scripts/code_graph/types.ts +66 -1
  190. package/src/scripts/code_graph/validate.ts +23 -0
  191. package/src/scripts/condense.ts +34 -22
  192. package/src/scripts/corpus_manifest.ts +5 -5
  193. package/src/scripts/council_cli.ts +29 -39
  194. package/src/scripts/count_intent_disagreement.ts +426 -0
  195. package/src/scripts/detect_ai_tells.ts +142 -20
  196. package/src/scripts/handoff_context_hook.ts +79 -0
  197. package/src/scripts/harvest_reference_tokens.ts +698 -0
  198. package/src/scripts/hook_effect_doctor.ts +82 -1
  199. package/src/scripts/hook_manifest.json +1 -1
  200. package/src/scripts/hook_manifest.yaml +94 -12
  201. package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
  202. package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
  203. package/src/scripts/hooks/concern_registry.ts +6 -2
  204. package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
  205. package/src/scripts/hooks/host_semantics.ts +1 -1
  206. package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
  207. package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
  208. package/src/scripts/hooks/rule_inject_hook.ts +1 -1
  209. package/src/scripts/hooks/skill_route_hook.ts +11 -4
  210. package/src/scripts/inbox_source_census.ts +322 -0
  211. package/src/scripts/install-hooks.sh +134 -0
  212. package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
  213. package/src/scripts/lint_plan_risk_register.ts +13 -17
  214. package/src/scripts/lint_roadmap_blockers.ts +51 -1
  215. package/src/scripts/mcp_server/suggest_skill.ts +72 -0
  216. package/src/scripts/mcp_server/tools.ts +2 -56
  217. package/src/scripts/media/lib/adapter-contract.md +7 -2
  218. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  219. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  220. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  221. package/src/scripts/migrate_command_suggestions.ts +1 -1
  222. package/src/scripts/probe_unblocked_ask.ts +135 -2
  223. package/src/scripts/rdp_candidates_delta.ts +276 -0
  224. package/src/scripts/rdp_quality_eval.ts +217 -19
  225. package/src/scripts/release.ts +26 -71
  226. package/src/scripts/release_drill.ts +6 -10
  227. package/src/scripts/release_env.ts +1 -0
  228. package/src/scripts/release_mix_taxonomy.json +3 -2
  229. package/src/scripts/release_publication.ts +34 -29
  230. package/src/scripts/report_host_injection_effect.ts +189 -0
  231. package/src/scripts/report_layer_overlap.ts +41 -0
  232. package/src/scripts/report_skill_menu_census.ts +341 -0
  233. package/src/scripts/self_review_gate.ts +409 -15
  234. package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
  235. package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
  236. package/src/vendor/grammars/README.md +61 -0
  237. package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
  238. package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
  239. package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  240. package/src/scripts/_lib/carrier_status.ts +0 -118
  241. package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
  242. package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
@@ -32,7 +32,7 @@ import { audit, type PersonaRow } from './audit_persona_coverage.js';
32
32
  /** Sentinel for "no catalogue root resolved" — never a readable path. */
33
33
  const NO_CATALOGUE = '<no-skills-catalogue>';
34
34
 
35
- import { DEFAULT_SKILLS_DIR, rank } from './score_skill_relevance.js';
35
+ import { DEFAULT_SKILLS_DIR, DEFAULT_SKILLS_ROOTS, rank } from './score_skill_relevance.js';
36
36
 
37
37
  const _HERE = fileURLToPath(import.meta.url);
38
38
  // src/scripts/skill_tools/suggest_skill_for_task.ts → parents[3] of the .py
@@ -78,13 +78,27 @@ export interface Combo {
78
78
  why: string;
79
79
  }
80
80
 
81
+ /**
82
+ * @param skillsDir the root the PERSONA audit reads — one directory by necessity.
83
+ * @param rankRoots roots to RANK across; defaults to `[skillsDir]`.
84
+ *
85
+ * The two parameters are separate because the two questions are. Ranking wants
86
+ * every catalogue the session can reach (`resolveSkillCatalogueRoots`); the
87
+ * persona-coverage audit walks one tree and pairs it with one persona directory.
88
+ * Collapsing them was the state a neutral review found on 2026-09-07: the MCP
89
+ * handler ranked the union while this CLI ranked the first root, so a consumer
90
+ * carrying its own project skills got a different answer from each — exactly the
91
+ * two-resolvers-over-one-catalogue split the resolver's docstring warns about,
92
+ * reproduced by the change that claimed to fix it.
93
+ */
81
94
  export function suggest(
82
95
  task: string,
83
96
  skillsDir: string,
84
97
  personasDir: string,
85
98
  top = 3,
99
+ rankRoots: readonly string[] = [skillsDir],
86
100
  ): Combo[] {
87
- const ranked = rank(task, skillsDir).slice(0, top);
101
+ const ranked = rank(task, rankRoots).slice(0, top);
88
102
  const personaRows = audit(skillsDir, personasDir);
89
103
  const status = _persona_status(personaRows);
90
104
  return ranked.map(([name, score, personas]) => ({
@@ -193,6 +207,8 @@ const PROG = 'suggest_skill_for_task.py';
193
207
  interface Args {
194
208
  task: string;
195
209
  skills_dir: string;
210
+ /** Did the operator NAME the directory? One tree then, and only that tree. */
211
+ skills_dir_explicit: boolean;
196
212
  personas_dir: string;
197
213
  top: number;
198
214
  json: boolean;
@@ -219,6 +235,7 @@ export function parse_args(argv: string[]): Args {
219
235
  // DEFAULT_SKILLS_DIR removed. When no catalogue resolves, this reads
220
236
  // as the literal marker below and the caller's own check reports it.
221
237
  skills_dir: DEFAULT_SKILLS_DIR ?? NO_CATALOGUE,
238
+ skills_dir_explicit: false,
222
239
  personas_dir: DEFAULT_PERSONAS,
223
240
  top: 3,
224
241
  json: false,
@@ -244,8 +261,10 @@ export function parse_args(argv: string[]): Args {
244
261
  _argError('argument --skills-dir: expected one argument');
245
262
  }
246
263
  args.skills_dir = v;
264
+ args.skills_dir_explicit = true;
247
265
  } else if (a.startsWith('--skills-dir=')) {
248
266
  args.skills_dir = a.slice('--skills-dir='.length);
267
+ args.skills_dir_explicit = true;
249
268
  } else if (a === '--personas-dir') {
250
269
  const v = argv[++i];
251
270
  if (v === undefined) {
@@ -279,7 +298,22 @@ export function main(argv: string[] | null = null): number {
279
298
  if (!task) {
280
299
  _argError('--task is required (or pass --sample)');
281
300
  }
282
- const combos = suggest(task, args.skills_dir, args.personas_dir, args.top);
301
+ // Rank across every readable root; audit personas against the first. An
302
+ // explicit --skills-dir names one tree and is honoured as one tree.
303
+ //
304
+ // Keyed on the FLAG, not on value-equality against the default. Corrected
305
+ // after a second neutral review: comparing paths meant
306
+ // `--skills-dir <the resolved default>` silently ranked the union including
307
+ // `~/.claude/skills`, so the comment above was false for exactly the operator
308
+ // who spelled the default out. And when no catalogue resolves at all,
309
+ // `skills_dir` is the `NO_CATALOGUE` sentinel while `DEFAULT_SKILLS_DIR` is
310
+ // null, so the old comparison was false there too — the branch was dead in
311
+ // the state it was written for.
312
+ const rankRoots =
313
+ !args.skills_dir_explicit && DEFAULT_SKILLS_ROOTS.length > 0
314
+ ? DEFAULT_SKILLS_ROOTS
315
+ : [args.skills_dir];
316
+ const combos = suggest(task, args.skills_dir, args.personas_dir, args.top, rankRoots);
283
317
  if (args.json) {
284
318
  process.stdout.write(pyJsonDumpsIndent2({ task, suggestions: combos }));
285
319
  process.stdout.write('\n');
@@ -0,0 +1,61 @@
1
+ # Vendored tree-sitter grammars
2
+
3
+ Compiled WebAssembly grammars for the native code-graph engine
4
+ (`src/scripts/code_graph/`). They are vendored — carried in this repository and
5
+ published inside the npm tarball — so that a consumer who runs
6
+ `npm install @event4u/agent-config` can build a code graph on the next command
7
+ with no manual parser install (ADR-259).
8
+
9
+ ## Why vendored rather than a dependency
10
+
11
+ `tree-sitter-wasms` carries 36 grammars (49 MiB on disk). Depending on it at
12
+ runtime would hand every consumer all 36, which is the option ADR-259 §
13
+ Alternatives rejects on exactly that measurement. Only the three grammars below
14
+ are loadable by the engine — `GRAMMAR_WASM` in `src/scripts/code_graph/types.ts`
15
+ has three entries — so the other 33 would be payload with no reader.
16
+
17
+ **No grammar is ever fetched at runtime** (Kill register K1). These files are in
18
+ the tarball; the loader reads them from disk.
19
+
20
+ ## The set
21
+
22
+ | Language | File | SHA-256 |
23
+ |---|---|---|
24
+ | php | `tree-sitter-php.wasm` | `55bb617b6f01e14bab997861f0b20a2420cf6ba3199ffeb295b9ec398966d8a3` |
25
+ | typescript (also serves `.tsx`) | `tree-sitter-typescript.wasm` | `8515404dceed38e1ed86aa34b09fcf3379fff1b4ff9dd3967bcd6d1eb5ac3d8f` |
26
+ | javascript | `tree-sitter-javascript.wasm` | `63812b9e275d26851264734868d27a1656bd44a2ef6eb3e85e6b03728c595ab5` |
27
+
28
+ All three are built against **grammar ABI 14** (`EXPECTED_GRAMMAR_ABI` in
29
+ `src/scripts/code_graph/types.ts`). The loader asserts the ABI on every load, so
30
+ a drifted grammar fails loudly rather than parsing wrongly.
31
+
32
+ ## Provenance
33
+
34
+ Copied byte-for-byte from `tree-sitter-wasms@0.1.13`
35
+ (<https://github.com/Gregoor/tree-sitter-wasms>, Unlicense), which compiles them
36
+ from the upstream `tree-sitter/tree-sitter-{php,typescript,javascript}` grammars
37
+ (MIT). The compiled artifacts are derivatives of the MIT grammar sources, so
38
+ they are annotated MIT in `REUSE.toml` and credited in `CREDITS.md`.
39
+
40
+ `tree-sitter-wasms` remains a **devDependency**: it is the source these copies
41
+ are refreshed from, and the ABI smoke test in `tests/scripts/code_graph.test.ts`
42
+ runs against it. It is deliberately not a runtime dependency.
43
+
44
+ ## Refreshing
45
+
46
+ The pair is ABI-coupled and must move together:
47
+
48
+ ```bash
49
+ npm i -D web-tree-sitter@<v> tree-sitter-wasms@<v>
50
+ for g in php typescript javascript; do
51
+ cp node_modules/tree-sitter-wasms/out/tree-sitter-$g.wasm src/vendor/grammars/
52
+ done
53
+ shasum -a 256 src/vendor/grammars/*.wasm # update the table above
54
+ ```
55
+
56
+ Then update, in the same change:
57
+
58
+ - the three `BINARY_PAYLOAD_EXCEPTIONS` entries in `src/scripts/check_pack_size.ts`
59
+ (they are path-and-size-bound, so any size change invalidates them by design);
60
+ - `EXPECTED_GRAMMAR_ABI` in `src/scripts/code_graph/types.ts` if the ABI moved;
61
+ - the install-size line in `MIGRATION.md`.
@@ -1,118 +0,0 @@
1
- /**
2
- * The `status: carrier` predicate, and the reasoning five readers share.
3
- *
4
- * A carrier is a roadmap whose job is to hold obligations deferred out of a
5
- * parent that is already archived. Every item in it has an unmet resumption
6
- * trigger, and it has no `## Phase` headings, so it is skipped wherever a draft
7
- * is: the dashboard, `/roadmap:process-*`, the trackability check and the plan
8
- * risk register. It differs from a draft in what it costs to remove — a draft
9
- * may be deleted freely, a carrier may not.
10
- *
11
- * This module exists because the predicate had two implementations within one
12
- * change and five callers within one week. Two implementations of one frontmatter
13
- * test drift, and the one nobody runs locally drifts first — the same
14
- * memory-twin failure this repository has already recorded against a fixture
15
- * corpus. One test, one place, and the reasoning above lives beside it rather
16
- * than being restated in each caller.
17
- *
18
- * The read is frontmatter-scoped on purpose. A fenced `status: carrier` inside a
19
- * roadmap that documents the syntax is not a declaration, and a body-wide match
20
- * turned exactly that into a hard failure before a review caught it.
21
- */
22
-
23
- /** The frontmatter values that mean "not schedulable work, and not disposable". */
24
- export const CARRIER_VALUES: ReadonlySet<string> = new Set(['carrier']);
25
-
26
- /** The leading `---\n…\n---` block, or null when the text carries none. */
27
- export function frontmatterBlock(text: string): string | null {
28
- const m = /^---\n([\s\S]*?)\n---/.exec(text);
29
- return m === null ? null : (m[1] ?? '');
30
- }
31
-
32
- /** True when `text`'s FRONTMATTER declares one of {@link CARRIER_VALUES}. */
33
- export function declaresCarrier(text: string | null | undefined): boolean {
34
- if (text === null || text === undefined) {
35
- return false;
36
- }
37
- const fm = frontmatterBlock(text);
38
- if (fm === null) {
39
- return false;
40
- }
41
- const m = /^status:[ \t]*([A-Za-z-]+)[ \t]*$/m.exec(fm);
42
- return m !== null && CARRIER_VALUES.has((m[1] ?? '').toLowerCase());
43
- }
44
-
45
- /**
46
- * Count the top-level roadmaps in `roadmapRoot` that declare themselves carriers.
47
- *
48
- * Lives here rather than in the estate gate because it is the same fact as the
49
- * predicate above, read over a directory instead of a string, and the gate that
50
- * needs it is already at its source-size ceiling.
51
- *
52
- * @param isCandidate the caller's own roadmap-filename test, so this module does
53
- * not acquire a second opinion about what counts as a roadmap.
54
- */
55
- export function countDeclaredCarriers(
56
- roadmapRoot: string,
57
- isCandidate: (name: string) => boolean,
58
- fsLike: {
59
- readdirSync: (p: string) => string[];
60
- statSync: (p: string) => { isFile: () => boolean };
61
- readFileSync: (p: string, enc: 'utf-8') => string;
62
- },
63
- join: (...parts: string[]) => string,
64
- ): number {
65
- let names: string[];
66
- try {
67
- names = fsLike.readdirSync(roadmapRoot);
68
- } catch {
69
- return 0;
70
- }
71
- let n = 0;
72
- for (const name of names) {
73
- if (!name.endsWith('.md') || !isCandidate(name)) {
74
- continue;
75
- }
76
- const abs = join(roadmapRoot, name);
77
- try {
78
- if (!fsLike.statSync(abs).isFile()) {
79
- continue;
80
- }
81
- if (declaresCarrier(fsLike.readFileSync(abs, 'utf-8'))) {
82
- n += 1;
83
- }
84
- } catch {
85
- continue;
86
- }
87
- }
88
- return n;
89
- }
90
-
91
- /**
92
- * Fixture design — why two of the estate self-test cases are shaped as they are.
93
- *
94
- * The reasoning behind two of `check_estate_count`'s self-test cases, kept here
95
- * because the gate that holds them is at its source-size ceiling and because
96
- * both facts are about the carrier status rather than about that gate.
97
- *
98
- * **The deletion case's addition is a DRAFT, and that is the whole design.**
99
- * A carrier holds obligations lifted out of an already-archived parent, so
100
- * paying an offset for removing one rewards the loss: the deletion buys nothing
101
- * and the addition stays unpaid. With an *ordinary* addition the growth half and
102
- * the unpaid half coincide — growth iff `n > a`, unpaid iff `n > a` — so the
103
- * case rejected on two independent grounds and stayed red with the carrier
104
- * branch reverted, asserting nothing about the feature it named. A draft is
105
- * invisible to `collect()`, so the count half cannot move and only the offset
106
- * scoring decides.
107
- *
108
- * **The count case asserts the flip BACK, not the flip TO.** A reclassification
109
- * is not a disposal in either direction, but the flip *to* carrier cannot be
110
- * asserted by exit code: it lowers the count, a count below the floor is a
111
- * drawdown, and a drawdown is free whichever way carriers are counted. Its
112
- * consequence is assertable — with carriers uncounted the flip lowers the floor,
113
- * so flipping the same file back reads as growth and must be paid for. That is
114
- * the fixture's state, with the carrier already in the base tree, and it rejects
115
- * with the carrier term removed from the count. The neutrality of the forward
116
- * flip is asserted directly in `check_estate_count.test.ts`, where a count is
117
- * readable.
118
- */
@@ -1,72 +0,0 @@
1
- /**
2
- * The three `check_estate_count` self-test cases that exercise `status: carrier`.
3
- *
4
- * They live outside that gate because it sits at the 1,500-line source ceiling
5
- * `check_source_size_budget` enforces, and because the doctrine that gate states
6
- * for an oversized file is a split rather than a raised baseline. The reasoning
7
- * behind the fixture shapes is in `carrier_status.ts` § Fixture design.
8
- */
9
-
10
- import type { SelfTestCase } from './gate_self_test.js';
11
-
12
- /** What the caller must supply: its own fixture harness and roadmap bodies. */
13
- export interface EstateCaseDeps {
14
- fixture: (opts: {
15
- roadmaps: number;
16
- base?: string;
17
- before?: (dir: string) => void;
18
- after: (dir: string) => void;
19
- }) => number;
20
- write: (dir: string, rel: string, body: string) => void;
21
- remove: (dir: string, rel: string) => void;
22
- roadmap: (name: string, extra?: string) => string;
23
- CARRIER: string;
24
- DRAFT: string;
25
- }
26
-
27
- export function carrierEstateCases(d: EstateCaseDeps): SelfTestCase[] {
28
- const { fixture, write, remove, roadmap, CARRIER, DRAFT } = d;
29
- return [
30
- {
31
- name: 'deleting a carrier does not offset a new draft → reject',
32
- expect: 'reject',
33
- run: () =>
34
- fixture({
35
- roadmaps: 3,
36
- base: 'main',
37
- before: (dir) => write(dir, 'agents/roadmaps/road-to-carrier.md', CARRIER),
38
- after: (dir) => {
39
- remove(dir, 'agents/roadmaps/road-to-carrier.md');
40
- write(dir, 'agents/roadmaps/road-to-new.md', DRAFT);
41
- },
42
- }),
43
- },
44
- {
45
- // The control that proves the case above tests the STATUS rather
46
- // than the deletion: only the removed file's status differs.
47
- name: 'deleting an ordinary roadmap still offsets a new draft → accept',
48
- expect: 'accept',
49
- run: () =>
50
- fixture({
51
- roadmaps: 3,
52
- base: 'main',
53
- before: (dir) => write(dir, 'agents/roadmaps/road-to-spare.md', roadmap('S')),
54
- after: (dir) => {
55
- remove(dir, 'agents/roadmaps/road-to-spare.md');
56
- write(dir, 'agents/roadmaps/road-to-new.md', DRAFT);
57
- },
58
- }),
59
- },
60
- {
61
- name: 'flipping a carrier back to an ordinary roadmap costs nothing → accept',
62
- expect: 'accept',
63
- run: () =>
64
- fixture({
65
- roadmaps: 3,
66
- base: 'main',
67
- before: (dir) => write(dir, 'agents/roadmaps/road-to-2.md', CARRIER),
68
- after: (dir) => write(dir, 'agents/roadmaps/road-to-2.md', roadmap('R2')),
69
- }),
70
- },
71
- ];
72
- }
@@ -1,191 +0,0 @@
1
- #!/usr/bin/env tsx
2
- /**
3
- * PreToolUse code-graph nudge (ADR-124 Phase 4) — deterministic, warn-only.
4
- *
5
- * When a code-graph source is available and the agent is about to search or
6
- * read source files, surface a one-line reminder to query the graph first.
7
- * NEVER blocks (dispatcher contract: 0 allow · 2 warn) — Source G's strict
8
- * block-the-first-read mode is deliberately NOT ported (blocking reads on a
9
- * possibly-stale index violates the minimal-safe-diff posture). Once per
10
- * session (a latch keyed by session id), so it cannot nag.
11
- *
12
- * Default-OFF. Fires only when `hooks.code_graph.enabled: true`. Disabled /
13
- * missing / already-latched → no-op exit 0. fail_closed: false — any error
14
- * returns allow; a token optimisation must never break a tool call.
15
- *
16
- * Branches (first eligible search/read this session):
17
- * present + fresh → "query the graph first"
18
- * present + stale → "index N commits behind — rebuild"
19
- * absent → "no graph — build one?"
20
- */
21
- import fs from 'node:fs';
22
- import path from 'node:path';
23
- import { fileURLToPath, pathToFileURL } from 'node:url';
24
-
25
- import { detectSources, pickSource } from '../code_graph/detect.js';
26
- import { readHookStdin } from './hook_stdin.js';
27
-
28
- const SETTINGS_FILE = '.agent-settings.yml';
29
- const EXIT_ALLOW = 0;
30
- const EXIT_WARN = 2;
31
- const CODE_EXT = /\.(php|ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i;
32
-
33
- type JsonValue = string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue };
34
- type JsonObject = { [k: string]: JsonValue };
35
-
36
- function isObject(v: unknown): v is JsonObject {
37
- return typeof v === 'object' && v !== null && !Array.isArray(v);
38
- }
39
-
40
- /** `hooks.code_graph.enabled: true` mini-parser (mirrors rtk_wrap_hook). */
41
- export function enabled(root: string): boolean {
42
- const f = path.join(root, SETTINGS_FILE);
43
- let text: string;
44
- try {
45
- if (!fs.statSync(f).isFile()) return false;
46
- text = fs.readFileSync(f, 'utf-8');
47
- } catch {
48
- return false;
49
- }
50
- let inHooks = false;
51
- let inCg = false;
52
- for (const raw of text.split(/\r\n|\r|\n/)) {
53
- const line = raw.replace(/\s+$/, '');
54
- if (!line || line.replace(/^\s+/, '').startsWith('#')) continue;
55
- if (!(line.startsWith(' ') || line.startsWith('\t'))) {
56
- inHooks = /^hooks\s*:\s*$/.test(line);
57
- inCg = false;
58
- continue;
59
- }
60
- if (inHooks) {
61
- if (/^\s+code_graph\s*:\s*$/.test(line)) {
62
- inCg = true;
63
- continue;
64
- }
65
- if (inCg && /^\s{0,3}\S/.test(line)) inCg = false;
66
- }
67
- if (inCg && /^\s+enabled\s*:\s*true\b/.test(line)) return true;
68
- }
69
- return false;
70
- }
71
-
72
- interface ToolIntent {
73
- isSearch: boolean;
74
- isCodeRead: boolean;
75
- }
76
-
77
- /** Best-effort read of the intercepted tool + its target from the envelope. */
78
- export function classifyTool(envelope: JsonObject): ToolIntent {
79
- const payload = isObject(envelope['payload']) ? envelope['payload'] : envelope;
80
- const nameVal =
81
- payload['tool_name'] ?? payload['toolName'] ?? payload['tool'] ?? envelope['tool_name'] ?? envelope['tool'];
82
- const name = typeof nameVal === 'string' ? nameVal : '';
83
- const ti = (isObject(payload['tool_input']) ? payload['tool_input'] : envelope['tool_input']) as
84
- | JsonObject
85
- | undefined;
86
- const isSearch = name === 'Grep' || name === 'Glob';
87
- let isCodeRead = false;
88
- if (name === 'Read' && isObject(ti)) {
89
- const fp = ti['file_path'] ?? ti['path'];
90
- if (typeof fp === 'string') isCodeRead = CODE_EXT.test(fp);
91
- }
92
- return { isSearch, isCodeRead };
93
- }
94
-
95
- function sessionId(envelope: JsonObject): string {
96
- const s = envelope['session_id'] ?? envelope['sessionId'];
97
- return typeof s === 'string' && s ? s : 'default';
98
- }
99
-
100
- function latchFile(root: string): string {
101
- return path.join(root, 'agents', 'runtime', 'state', 'code-graph-nudge.json');
102
- }
103
- function alreadyNudged(root: string, session: string): boolean {
104
- try {
105
- const state = JSON.parse(fs.readFileSync(latchFile(root), 'utf-8')) as Record<string, boolean>;
106
- return state[session] === true;
107
- } catch {
108
- return false;
109
- }
110
- }
111
- function latch(root: string, session: string): void {
112
- try {
113
- const p = latchFile(root);
114
- let state: Record<string, boolean> = {};
115
- try {
116
- state = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, boolean>;
117
- } catch {
118
- /* fresh */
119
- }
120
- state[session] = true;
121
- fs.mkdirSync(path.dirname(p), { recursive: true });
122
- fs.writeFileSync(p, JSON.stringify(state));
123
- } catch {
124
- /* fail-open: a persistence failure must not break the tool call */
125
- }
126
- }
127
-
128
- const NATIVE_CACHE = path.join('agents', 'runtime', 'state', 'code-graph-v1.json');
129
-
130
- /** The nudge line for the current source state (≤40 tokens, one line). */
131
- export function nudgeReason(root: string): string {
132
- const verdicts = detectSources(root, path.join(root, NATIVE_CACHE));
133
- const picked = pickSource(verdicts);
134
- if (!picked) {
135
- return 'No code-graph found. For "who calls / where used / impact" run `agent-config code-graph refresh` once, then `agent-config code-graph query <symbol>` instead of grepping blind.';
136
- }
137
- if (picked.stale) {
138
- return `code-graph is ${picked.commits_behind ?? 'N'} commit(s) behind — rebuild with \`agent-config code-graph refresh\` before trusting relationship answers.`;
139
- }
140
- return 'A fresh code-graph is present — for structure questions run `agent-config code-graph query|affected <symbol>` first; grep is the fallback (say which answered).';
141
- }
142
-
143
- export function main(): number {
144
- let envelope: JsonValue;
145
- try {
146
- const raw = readHookStdin();
147
- envelope = raw.trim() ? (JSON.parse(raw) as JsonValue) : {};
148
- } catch {
149
- return EXIT_ALLOW;
150
- }
151
- if (!isObject(envelope)) return EXIT_ALLOW;
152
-
153
- const cwd = envelope['cwd'];
154
- const pr = envelope['workspace_root'] ?? envelope['project_root'];
155
- const root = typeof cwd === 'string' && cwd ? cwd : typeof pr === 'string' && pr ? pr : '.';
156
- if (!enabled(root)) return EXIT_ALLOW;
157
-
158
- const { isSearch, isCodeRead } = classifyTool(envelope);
159
- if (!isSearch && !isCodeRead) return EXIT_ALLOW;
160
-
161
- const session = sessionId(envelope);
162
- if (alreadyNudged(root, session)) return EXIT_ALLOW; // once per session
163
-
164
- let reason: string;
165
- try {
166
- reason = nudgeReason(root);
167
- } catch {
168
- return EXIT_ALLOW;
169
- }
170
- latch(root, session);
171
- process.stdout.write(`${JSON.stringify({ decision: 'warn', reason })}\n`);
172
- return EXIT_WARN;
173
- }
174
-
175
- // Bundle-safety: never auto-run when inlined into an esbuild bundle, where
176
- // every module shares the bundle's `import.meta.url` (see cmd_migrate.ts).
177
- declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
178
- function isCliEntry(): boolean {
179
- if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) {
180
- return false;
181
- }
182
- if (process.argv[1] === undefined) return false;
183
- const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
184
- if (import.meta.url === argvUrl) return true;
185
- try {
186
- return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(process.argv[1]));
187
- } catch {
188
- return false;
189
- }
190
- }
191
- if (isCliEntry()) process.exit(main());