@event4u/agent-config 14.6.0 → 14.8.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 (258) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +255 -145
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/inbox.md +264 -10
  5. package/dist/agent-src/commands/analyze.md +1 -1
  6. package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
  7. package/dist/agent-src/commands/evals/roadmap.json +17 -1
  8. package/dist/agent-src/commands/pr/create.md +14 -2
  9. package/dist/agent-src/commands/pr/merge.md +379 -0
  10. package/dist/agent-src/commands/roadmap/process-full.md +272 -32
  11. package/dist/agent-src/commands/roadmap.md +1 -1
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
  14. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
  16. package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
  17. package/dist/agent-src/rules/context-hygiene.md +2 -0
  18. package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
  19. package/dist/agent-src/rules/fix-what-you-see.md +108 -0
  20. package/dist/agent-src/rules/no-roadmap-references.md +0 -5
  21. package/dist/agent-src/rules/recurring-criticism.md +110 -0
  22. package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
  23. package/dist/agent-src/rules/rule-type-governance.md +0 -2
  24. package/dist/agent-src/rules/self-repair-loop.md +17 -0
  25. package/dist/agent-src/rules/skill-quality.md +0 -2
  26. package/dist/agent-src/rules/source-confidentiality.md +37 -7
  27. package/dist/agent-src/scripts/archival_sweep.ts +117 -0
  28. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  29. package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
  30. package/dist/agent-src/scripts/gate_budget.ts +282 -0
  31. package/dist/agent-src/scripts/gate_execute.ts +72 -31
  32. package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
  33. package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
  34. package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
  35. package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
  36. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
  37. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
  38. package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
  39. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
  40. package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
  41. package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
  42. package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
  43. package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
  44. package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
  45. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
  46. package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
  47. package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
  48. package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
  49. package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
  50. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
  51. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
  52. package/dist/agent-src/skills/security/SKILL.md +8 -0
  53. package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
  54. package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
  55. package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
  56. package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
  57. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
  58. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  59. package/dist/agent-src/templates/roadmaps.md +65 -4
  60. package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  61. package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  62. package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  63. package/dist/cli/registry.js +2 -1
  64. package/dist/cli/registry.js.map +1 -1
  65. package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
  66. package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
  67. package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
  68. package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
  69. package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
  70. package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
  71. package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
  72. package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
  73. package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
  74. package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
  75. package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
  76. package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
  77. package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
  78. package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
  79. package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
  80. package/dist/cli-delegate/cmd_conformance.js +466 -716
  81. package/dist/cli-delegate/cmd_converge.js +3 -3
  82. package/dist/cli-delegate/cmd_doctor.js +10 -10
  83. package/dist/cli-delegate/cmd_explain.js +1 -1
  84. package/dist/cli-delegate/cmd_export.js +10 -9
  85. package/dist/cli-delegate/cmd_handoff.js +10 -9
  86. package/dist/cli-delegate/cmd_memory_get.js +2 -1
  87. package/dist/cli-delegate/cmd_migrate.js +2 -2
  88. package/dist/cli-delegate/cmd_packs_active.js +1 -1
  89. package/dist/cli-delegate/cmd_preflight.js +5 -5
  90. package/dist/cli-delegate/cmd_prune.js +11 -10
  91. package/dist/cli-delegate/cmd_route_audit.js +6 -5
  92. package/dist/cli-delegate/cmd_route_explain.js +3 -3
  93. package/dist/cli-delegate/cmd_session_recycle.js +840 -30
  94. package/dist/cli-delegate/cmd_settings_get.js +3 -3
  95. package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
  96. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  97. package/dist/cli-delegate/cmd_sync.js +11 -10
  98. package/dist/cli-delegate/cmd_uninstall.js +13 -11
  99. package/dist/cli-delegate/cmd_update.js +4 -4
  100. package/dist/cli-delegate/cmd_upgrade.js +4 -4
  101. package/dist/cli-delegate/cmd_validate.js +11 -10
  102. package/dist/cli-delegate/cmd_versions.js +3 -3
  103. package/dist/discovery/deprecation-report.md +1 -1
  104. package/dist/discovery/discovery-manifest.json +111 -37
  105. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  106. package/dist/discovery/discovery-manifest.summary.md +5 -5
  107. package/dist/discovery/dormancy-report.md +1 -1
  108. package/dist/discovery/orphan-report.md +1 -1
  109. package/dist/discovery/packs.json +15 -12
  110. package/dist/discovery/trust-report.md +3 -3
  111. package/dist/discovery/workspaces.json +11 -8
  112. package/dist/hooks/dispatch.js +3165 -2192
  113. package/dist/install/claudePathsPlan.js +187 -0
  114. package/dist/install/claudePathsPlan.js.map +1 -0
  115. package/dist/install/globalRuleLayers.js +169 -0
  116. package/dist/install/globalRuleLayers.js.map +1 -0
  117. package/dist/install/hostLayerFingerprint.js +136 -0
  118. package/dist/install/hostLayerFingerprint.js.map +1 -0
  119. package/dist/install/install.mjs +553 -425
  120. package/dist/install/partitionEligibility.js +432 -0
  121. package/dist/install/partitionEligibility.js.map +1 -0
  122. package/dist/install/ruleLayerPartition.js +157 -0
  123. package/dist/install/ruleLayerPartition.js.map +1 -0
  124. package/dist/mcp/registry-manifest.json +3 -3
  125. package/dist/mcp/server.json +2 -2
  126. package/dist/mcp/server.mjs +1863 -1234
  127. package/dist/router.json +1 -1
  128. package/dist/scripts/_lib/agent_src.js +41 -0
  129. package/dist/scripts/_lib/agent_src.js.map +1 -1
  130. package/dist/scripts/_lib/install_layout.js +83 -0
  131. package/dist/scripts/_lib/install_layout.js.map +1 -0
  132. package/dist/scripts/_lib/installed_lock.js +444 -0
  133. package/dist/scripts/_lib/installed_lock.js.map +1 -0
  134. package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
  135. package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
  136. package/dist/scripts/condense.js +63 -251
  137. package/dist/scripts/condense.js.map +1 -1
  138. package/dist/server/schemas/settings.js +4 -0
  139. package/dist/server/schemas/settings.js.map +1 -1
  140. package/docs/contracts/settings-classes.md +12 -6
  141. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
  142. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
  143. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
  144. package/docs/guidelines/agent-infra/false-green.md +47 -1
  145. package/docs/guidelines/agent-infra/memory-access.md +17 -0
  146. package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  147. package/package.json +1 -1
  148. package/src/agent-src/scripts/archival_sweep.ts +117 -0
  149. package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  150. package/src/agent-src/scripts/dashboard_mode.ts +212 -0
  151. package/src/agent-src/scripts/gate_budget.ts +282 -0
  152. package/src/agent-src/scripts/gate_execute.ts +72 -31
  153. package/src/agent-src/scripts/roadmap_gates.ts +74 -3
  154. package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
  155. package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  156. package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  157. package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  158. package/src/config/agent-settings.template.yml +27 -2
  159. package/src/config/ci-local-parity.yml +39 -0
  160. package/src/config/estate-count-budget.json +612 -3
  161. package/src/config/evaluator-budgets.json +3 -3
  162. package/src/config/gate-coverage.yml +220 -12
  163. package/src/config/gate-violation-baselines.json +73 -63
  164. package/src/config/gitignore-block.txt +15 -2
  165. package/src/config/hook-latency-budget.json +5 -0
  166. package/src/config/rule-activation-census.json +14 -6
  167. package/src/config/rule-stub-ceilings.json +34 -7
  168. package/src/install/claudePathsPlan.ts +196 -0
  169. package/src/install/globalRuleLayers.ts +197 -0
  170. package/src/install/hostLayerFingerprint.ts +145 -0
  171. package/src/install/partitionEligibility.ts +502 -0
  172. package/src/install/ruleLayerPartition.ts +212 -0
  173. package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
  174. package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
  175. package/src/scripts/_dispatch.bash +17 -1
  176. package/src/scripts/_lib/ac_heading.ts +30 -0
  177. package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
  178. package/src/scripts/_lib/adr_frontmatter.ts +459 -0
  179. package/src/scripts/_lib/agent_src.ts +42 -0
  180. package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
  181. package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
  182. package/src/scripts/_lib/envelope_grounding.ts +49 -9
  183. package/src/scripts/_lib/installed_lock.ts +50 -5
  184. package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
  185. package/src/scripts/_lib/md_section.ts +129 -0
  186. package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
  187. package/src/scripts/_lib/scope_guard.sh +26 -3
  188. package/src/scripts/_lib/self_repair.ts +19 -0
  189. package/src/scripts/_lib/self_repair_class_b.ts +171 -0
  190. package/src/scripts/adr/evidence_census.ts +899 -0
  191. package/src/scripts/adr/regenerate_index.ts +12 -32
  192. package/src/scripts/adr_cite_check.ts +540 -39
  193. package/src/scripts/ai_council/cli_help.ts +55 -0
  194. package/src/scripts/audit_adr_coverage.ts +103 -17
  195. package/src/scripts/before_complete_hook.ts +153 -28
  196. package/src/scripts/bench_ab_v2_complexity.ts +27 -7
  197. package/src/scripts/bench_hook_latency.ts +204 -0
  198. package/src/scripts/build_archive_index.ts +34 -0
  199. package/src/scripts/check_adr_frontmatter.ts +574 -39
  200. package/src/scripts/check_hook_bundle_content.ts +219 -0
  201. package/src/scripts/check_new_adr_evidence.ts +529 -0
  202. package/src/scripts/check_references.ts +78 -0
  203. package/src/scripts/check_release_published.ts +32 -11
  204. package/src/scripts/check_rule_layer_partition.ts +531 -0
  205. package/src/scripts/check_single_delivery.ts +194 -17
  206. package/src/scripts/check_standing_rule_delivery.ts +8 -2
  207. package/src/scripts/ci_settle.ts +170 -0
  208. package/src/scripts/condense.ts +79 -259
  209. package/src/scripts/council_cli.ts +3 -3
  210. package/src/scripts/dispatch_r2_reviewer.ts +6 -14
  211. package/src/scripts/external_sources_denylist.json +4 -1
  212. package/src/scripts/hook_manifest.json +1 -1
  213. package/src/scripts/hook_manifest.yaml +77 -14
  214. package/src/scripts/hooks/block_no_verify.ts +61 -10
  215. package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
  216. package/src/scripts/hooks/concern_registry.ts +4 -0
  217. package/src/scripts/hooks/dispatch_hook.ts +154 -187
  218. package/src/scripts/hooks/dispatch_issues.ts +38 -19
  219. package/src/scripts/hooks/fallback_yaml.ts +66 -0
  220. package/src/scripts/hooks/host_semantics.ts +21 -0
  221. package/src/scripts/hooks/injection_budget.ts +13 -3
  222. package/src/scripts/hooks/py_json_dumps.ts +103 -0
  223. package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
  224. package/src/scripts/hooks/run_continuation_hook.ts +8 -5
  225. package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
  226. package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
  227. package/src/scripts/hooks/state_io.ts +870 -21
  228. package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
  229. package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
  230. package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
  231. package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
  232. package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
  233. package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
  234. package/src/scripts/injection_scan_hook.ts +137 -5
  235. package/src/scripts/install.ts +16 -11
  236. package/src/scripts/language_mirror_hook.ts +561 -25
  237. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  238. package/src/scripts/lint_hook_manifest.ts +49 -0
  239. package/src/scripts/lint_plan_risk_register.ts +4 -3
  240. package/src/scripts/lint_provenance_vocabulary.ts +472 -4
  241. package/src/scripts/lint_skill_router_head.ts +4 -4
  242. package/src/scripts/mcp_server/tools.ts +2 -2
  243. package/src/scripts/new_roadmap.ts +136 -0
  244. package/src/scripts/pack_mcp_content.ts +20 -5
  245. package/src/scripts/release.ts +37 -38
  246. package/src/scripts/report_layer_overlap.ts +6 -0
  247. package/src/scripts/routing_doctor.ts +5 -1
  248. package/src/scripts/self_repair_hook.ts +37 -5
  249. package/src/scripts/session_register_hook.ts +11 -0
  250. package/src/scripts/skill_usage_collect.ts +11 -1
  251. package/src/scripts/skill_usage_report.ts +278 -8
  252. package/src/scripts/sync_pr_branch.ts +170 -20
  253. package/src/server/schemas/settings.ts +8 -0
  254. package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
  255. package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
  256. package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
  257. package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
  258. package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-MMSGVZKV.js} +3 -3
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `json.dumps(obj, indent=n)` byte-for-byte, extracted from `dispatch_hook.ts`.
3
+ *
4
+ * Pure and self-contained: it recurses into itself and calls nothing else, which
5
+ * is what made it safe to move out of a dispatcher that runs on every tool call.
6
+ * Extracted because dispatch_hook.ts sits at the 1500-line source ceiling.
7
+ *
8
+ * For anyone tempted to share it: mcp_telemetry_store, mcp_telemetry_query and
9
+ * mcp_telemetry_health each define their OWN `_py_json_dumps`. They were left
10
+ * alone — unifying four copies is a change with its own risk, not a merge-time
11
+ * side effect.
12
+ */
13
+ // Python json.dumps(record, indent=2) byte-for-byte (ensure_ascii=True).
14
+ export function _py_json_dumps(value: unknown, indent: number): string {
15
+ const pad = " ".repeat(indent);
16
+ const escapeString = (s: string): string => {
17
+ let out = '"';
18
+ for (const ch of s) {
19
+ const code = ch.codePointAt(0) as number;
20
+ switch (ch) {
21
+ case '"':
22
+ out += '\\"';
23
+ break;
24
+ case "\\":
25
+ out += "\\\\";
26
+ break;
27
+ case "\n":
28
+ out += "\\n";
29
+ break;
30
+ case "\r":
31
+ out += "\\r";
32
+ break;
33
+ case "\t":
34
+ out += "\\t";
35
+ break;
36
+ case "\b":
37
+ out += "\\b";
38
+ break;
39
+ case "\f":
40
+ out += "\\f";
41
+ break;
42
+ default:
43
+ if (code < 0x20 || code > 0x7e) {
44
+ if (code > 0xffff) {
45
+ const c = code - 0x10000;
46
+ const hi = 0xd800 + (c >> 10);
47
+ const lo = 0xdc00 + (c & 0x3ff);
48
+ out +=
49
+ "\\u" +
50
+ hi.toString(16).padStart(4, "0") +
51
+ "\\u" +
52
+ lo.toString(16).padStart(4, "0");
53
+ } else {
54
+ out += "\\u" + code.toString(16).padStart(4, "0");
55
+ }
56
+ } else {
57
+ out += ch;
58
+ }
59
+ }
60
+ }
61
+ return out + '"';
62
+ };
63
+ const render = (v: unknown, depth: number): string => {
64
+ if (v === null || v === undefined) return "null";
65
+ if (typeof v === "boolean") return v ? "true" : "false";
66
+ if (typeof v === "number") {
67
+ if (!Number.isFinite(v)) {
68
+ if (Number.isNaN(v)) return "NaN";
69
+ return v > 0 ? "Infinity" : "-Infinity";
70
+ }
71
+ return String(v);
72
+ }
73
+ if (typeof v === "string") return escapeString(v);
74
+ const curPad = pad.repeat(depth + 1);
75
+ const closePad = pad.repeat(depth);
76
+ if (Array.isArray(v)) {
77
+ if (v.length === 0) return "[]";
78
+ return (
79
+ "[\n" +
80
+ v.map((item) => curPad + render(item, depth + 1)).join(",\n") +
81
+ "\n" +
82
+ closePad +
83
+ "]"
84
+ );
85
+ }
86
+ if (typeof v === "object") {
87
+ const obj = v as Record<string, unknown>;
88
+ const keys = Object.keys(obj);
89
+ if (keys.length === 0) return "{}";
90
+ return (
91
+ "{\n" +
92
+ keys
93
+ .map((k) => curPad + escapeString(k) + ": " + render(obj[k], depth + 1))
94
+ .join(",\n") +
95
+ "\n" +
96
+ closePad +
97
+ "}"
98
+ );
99
+ }
100
+ throw new TypeError(`Object of type ${typeof v} is not JSON serializable`);
101
+ };
102
+ return render(value, 0);
103
+ }
@@ -155,14 +155,19 @@ export function classify(command: string): Eligibility {
155
155
 
156
156
  let tokens: string[];
157
157
  try {
158
- tokens = shlexSplit(cmd);
158
+ tokens = shlexSplit(cmd, true);
159
159
  } catch (e) {
160
160
  if (e instanceof ShlexError) return { eligible: false, reason_skip: "parse-error" };
161
161
  throw e;
162
162
  }
163
163
 
164
164
  // Compound / piped → don't wrap (rtk wraps a single command). `_split_subcommands`
165
- // splits on && || ; | — more than one group means a pipeline/chain.
165
+ // splits on && || ; | & — more than one group means a pipeline/chain.
166
+ // `splitOperators` is REQUIRED here: POSIX shlex leaves a separator attached
167
+ // to the preceding word, so `foo; bar` tokenised to one group and read as a
168
+ // single command. Same construct as the block_no_verify defect measured
169
+ // 2026-08-20; found by the sibling search for that fix, 2 call sites, both
170
+ // in this file.
166
171
  const groups = _split_subcommands(tokens);
167
172
  if (groups.length !== 1) return { eligible: false, reason_skip: "compound-or-piped" };
168
173
 
@@ -256,7 +261,7 @@ export function classifyCap(
256
261
  if (!cmd) return null;
257
262
  let tokens: string[];
258
263
  try {
259
- tokens = shlexSplit(cmd);
264
+ tokens = shlexSplit(cmd, true);
260
265
  } catch (e) {
261
266
  if (e instanceof ShlexError) return null;
262
267
  throw e;
@@ -72,12 +72,15 @@
72
72
  * duplicate, whatever the counts say.
73
73
  * scope complete (0 open,
74
74
  * none blocked) → allow (event: complete; state cleared)
75
- * runnable work exhausted
76
- * (0 open, ≥1 blocked) → allow (event: blocked, ONCE per run, guarded by
75
+ * remaining work externally
76
+ * impossible (0 open, ≥1
77
+ * blocked) → allow (event: blocked, ONCE per run, guarded by
77
78
  * the LEDGER: the state is cleared like `complete`,
78
- * so no field there outlives the event. ADR-235's
79
- * terminal outcome — not a sixth halt, never in
80
- * `HALT_ACTIONS`, never stamped into `halted`)
79
+ * so no field there outlives the event. A terminal
80
+ * outcome — not a sixth halt, never in
81
+ * `HALT_ACTIONS`, never stamped into `halted`.
82
+ * ADR-237 narrows what may legally wear a
83
+ * `blocked-by:` marker; semantics unchanged.)
81
84
  * iterations ≥ MAX (25) → allow (event: halt-max-iterations)
82
85
  * wall clock ≥ cap (4 h) → allow (event: halt-wall-clock)
83
86
  * stall (3 engagements, no
@@ -33,6 +33,7 @@ import path from 'node:path';
33
33
  import { fileURLToPath, pathToFileURL } from 'node:url';
34
34
 
35
35
  import { readHookStdin } from './hook_stdin.js';
36
+ import { isPayloadStub } from './payload_stub.js';
36
37
  import { atomic_write_json } from './state_io.js';
37
38
 
38
39
  export const STATE_FILE = path.join('agents', 'state', 'ship-diff-volume.json');
@@ -89,31 +90,96 @@ export function correctedVolume(numstat: string): { volume: number; excluded: nu
89
90
  return { volume, excluded, files };
90
91
  }
91
92
 
92
- function extractCommand(envelope: Record<string, unknown>): string {
93
- const ti = envelope['tool_input'];
94
- if (ti !== null && typeof ti === 'object') {
95
- const c = (ti as Record<string, unknown>)['command'];
93
+ /**
94
+ * Tools that carry a shell command. Mirrors `block_unauthorized_git`'s set.
95
+ *
96
+ * `SHIP_PATTERNS` are unanchored, so without this gate a `grep` for the string
97
+ * `git push` spawns two `git` subprocesses on a blocking slot. ALLOW-list: a
98
+ * named tool outside it is declined, so an unlisted host shell goes dark until
99
+ * added — the trade that sibling already makes while BLOCKING. A payload naming
100
+ * no tool still reads; that is the bare-host shape, with nothing to gate on.
101
+ */
102
+ const COMMAND_TOOLS: ReadonlySet<string> = new Set([
103
+ 'launch-process',
104
+ 'launch_process',
105
+ 'Bash',
106
+ 'BashTool',
107
+ 'run-process',
108
+ 'runProcess',
109
+ 'shell',
110
+ 'execute_shell',
111
+ 'RunShellCommand',
112
+ ]);
113
+
114
+ function isObj(v: unknown): v is Record<string, unknown> {
115
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
116
+ }
117
+
118
+ /**
119
+ * Descend to the platform payload.
120
+ *
121
+ * The dispatcher nests the host-shaped tool fields under `payload`
122
+ * (`_build_envelope`); a bare host invocation puts them at the top level. This
123
+ * concern shipped reading the top level ONLY, so under the real dispatcher it
124
+ * found nothing and returned `''` every time — it ran, cost a dispatch, and
125
+ * could never fire.
126
+ *
127
+ * Deliberately NOT `envelope.ts`'s shared `unwrap`, which descends only when all
128
+ * four `ENVELOPE_KEYS` are present: a partial envelope would return this concern
129
+ * to its pre-fix dead state with no test noticing. `design-slop` and
130
+ * `ui-route-nudge` each carry their own local descent for the same reason.
131
+ */
132
+ function payloadOf(root: Record<string, unknown>): Record<string, unknown> {
133
+ const inner = root['payload'];
134
+ return isObj(inner) ? inner : root;
135
+ }
136
+
137
+ /** The ship command, or `''` when this payload does not carry one. */
138
+ function shipCommandFrom(payload: Record<string, unknown>): string {
139
+ const toolRaw = payload['tool_name'] ?? payload['toolName'] ?? payload['tool'];
140
+ if (typeof toolRaw === 'string' && !COMMAND_TOOLS.has(toolRaw)) return '';
141
+
142
+ const ti = payload['tool_input'] ?? payload['toolInput'];
143
+ if (isPayloadStub(ti)) {
144
+ // Body omitted despite `needs_payload_bodies: [input]`. A bare '' here is
145
+ // indistinguishable from "no ship verb" — the silent death this file was
146
+ // fixed to remove — so say so instead of dying quietly again.
147
+ process.stderr.write(
148
+ 'ship-diff-volume: tool_input arrived stubbed; the concern declares ' +
149
+ 'needs_payload_bodies: [input] and cannot read a command without it.\n',
150
+ );
151
+ return '';
152
+ }
153
+ if (isObj(ti)) {
154
+ const c = ti['command'];
96
155
  if (typeof c === 'string') return c;
97
156
  }
98
- const c = envelope['command'];
157
+ const c = payload['command'];
99
158
  return typeof c === 'string' ? c : '';
100
159
  }
101
160
 
161
+ /**
162
+ * The stdin boundary: raw concern stdin → the ship command. Exported so a
163
+ * regression drives the boundary rather than the extractor. Never throws.
164
+ */
165
+ export function commandFromStdin(raw: string): string {
166
+ if (!raw.trim()) return '';
167
+ let root: unknown;
168
+ try {
169
+ root = JSON.parse(raw);
170
+ } catch {
171
+ return '';
172
+ }
173
+ if (!isObj(root)) return '';
174
+ return shipCommandFrom(payloadOf(root));
175
+ }
176
+
102
177
  export function main(argv?: string[]): number {
103
178
  const args = argv ?? process.argv.slice(2);
104
179
  const idx = args.indexOf('--command');
105
180
  let cmd = idx >= 0 && args[idx + 1] !== undefined ? args[idx + 1]! : '';
106
181
  if (!cmd) {
107
- const raw = readHookStdin();
108
- let envelope: Record<string, unknown> = {};
109
- if (raw.trim()) {
110
- try {
111
- envelope = JSON.parse(raw) as Record<string, unknown>;
112
- } catch {
113
- envelope = {};
114
- }
115
- }
116
- cmd = extractCommand(envelope);
182
+ cmd = commandFromStdin(readHookStdin());
117
183
  }
118
184
  if (!isShipCommand(cmd)) return 0;
119
185