session-orchestrator 3.24.0 → 4.0.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 (435) hide show
  1. package/.agents/skills/architecture/SKILL.md +18 -0
  2. package/.agents/skills/autopilot/SKILL.md +17 -0
  3. package/.agents/skills/bootstrap/SKILL.md +20 -0
  4. package/.agents/skills/brainstorm/SKILL.md +22 -0
  5. package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
  6. package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
  7. package/.agents/skills/debug/SKILL.md +22 -0
  8. package/.agents/skills/discovery/SKILL.md +20 -0
  9. package/.agents/skills/dispatcher/SKILL.md +15 -0
  10. package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
  11. package/.agents/skills/ecosystem-health/SKILL.md +20 -0
  12. package/.agents/skills/eli5/SKILL.md +20 -0
  13. package/.agents/skills/eval/SKILL.md +21 -0
  14. package/.agents/skills/evolve/SKILL.md +21 -0
  15. package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
  16. package/.agents/skills/gitlab-ops/SKILL.md +20 -0
  17. package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
  18. package/.agents/skills/grill/SKILL.md +22 -0
  19. package/.agents/skills/hook-development/SKILL.md +15 -0
  20. package/.agents/skills/mcp-builder/SKILL.md +15 -0
  21. package/.agents/skills/memory-cleanup/SKILL.md +21 -0
  22. package/.agents/skills/mode-selector/SKILL.md +17 -0
  23. package/.agents/skills/npm-publish/SKILL.md +16 -0
  24. package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
  25. package/.agents/skills/persona-panel/SKILL.md +17 -0
  26. package/.agents/skills/plan/SKILL.md +20 -0
  27. package/.agents/skills/playwright-driver/SKILL.md +20 -0
  28. package/.agents/skills/quality-gates/SKILL.md +20 -0
  29. package/.agents/skills/reconcile/SKILL.md +21 -0
  30. package/.agents/skills/remote-offload/SKILL.md +20 -0
  31. package/.agents/skills/repo-audit/SKILL.md +16 -0
  32. package/.agents/skills/session-end/SKILL.md +20 -0
  33. package/.agents/skills/session-plan/SKILL.md +20 -0
  34. package/.agents/skills/session-start/SKILL.md +20 -0
  35. package/.agents/skills/spinout/SKILL.md +16 -0
  36. package/.agents/skills/sunset-review/SKILL.md +16 -0
  37. package/.agents/skills/test-runner/SKILL.md +20 -0
  38. package/.agents/skills/tmux-layout/SKILL.md +21 -0
  39. package/.agents/skills/using-orchestrator/SKILL.md +17 -0
  40. package/.agents/skills/vault-mirror/SKILL.md +15 -0
  41. package/.agents/skills/vault-sync/SKILL.md +15 -0
  42. package/.agents/skills/wave-executor/SKILL.md +20 -0
  43. package/.agents/skills/write-executable-plan/SKILL.md +22 -0
  44. package/.claude-plugin/marketplace.json +1 -1
  45. package/.claude-plugin/plugin.json +1 -1
  46. package/.codex-plugin/plugin.json +3 -2
  47. package/.codex-plugin/skills/architecture/SKILL.md +20 -0
  48. package/.codex-plugin/skills/autopilot/SKILL.md +21 -0
  49. package/.codex-plugin/skills/autopilot/agents/openai.yaml +5 -0
  50. package/.codex-plugin/skills/bootstrap/SKILL.md +22 -0
  51. package/.codex-plugin/skills/bootstrap/agents/openai.yaml +5 -0
  52. package/.codex-plugin/skills/brainstorm/SKILL.md +22 -0
  53. package/.codex-plugin/skills/brainstorm/agents/openai.yaml +5 -0
  54. package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +17 -0
  55. package/.codex-plugin/skills/close/SKILL.md +21 -0
  56. package/.codex-plugin/skills/close/agents/openai.yaml +5 -0
  57. package/.codex-plugin/skills/convergence-monitoring/SKILL.md +24 -0
  58. package/.codex-plugin/skills/debug/SKILL.md +21 -0
  59. package/.codex-plugin/skills/debug/agents/openai.yaml +5 -0
  60. package/.codex-plugin/skills/discovery/SKILL.md +21 -0
  61. package/.codex-plugin/skills/discovery/agents/openai.yaml +5 -0
  62. package/.codex-plugin/skills/dispatcher/SKILL.md +21 -0
  63. package/.codex-plugin/skills/dispatcher/agents/openai.yaml +5 -0
  64. package/.codex-plugin/skills/docs-orchestrator/SKILL.md +20 -0
  65. package/.codex-plugin/skills/ecosystem-health/SKILL.md +22 -0
  66. package/.codex-plugin/skills/eli5/SKILL.md +21 -0
  67. package/.codex-plugin/skills/eli5/agents/openai.yaml +5 -0
  68. package/.codex-plugin/skills/eval/SKILL.md +21 -0
  69. package/.codex-plugin/skills/eval/agents/openai.yaml +5 -0
  70. package/.codex-plugin/skills/evolve/SKILL.md +21 -0
  71. package/.codex-plugin/skills/evolve/agents/openai.yaml +5 -0
  72. package/.codex-plugin/skills/frontmatter-guard/SKILL.md +17 -0
  73. package/.codex-plugin/skills/gitlab-ops/SKILL.md +22 -0
  74. package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +17 -0
  75. package/.codex-plugin/skills/go/SKILL.md +22 -0
  76. package/.codex-plugin/skills/go/agents/openai.yaml +5 -0
  77. package/.codex-plugin/skills/grill/SKILL.md +21 -0
  78. package/.codex-plugin/skills/grill/agents/openai.yaml +5 -0
  79. package/.codex-plugin/skills/harness-audit/SKILL.md +19 -0
  80. package/.codex-plugin/skills/harness-audit/agents/openai.yaml +5 -0
  81. package/.codex-plugin/skills/hook-development/SKILL.md +17 -0
  82. package/.codex-plugin/skills/mcp-builder/SKILL.md +17 -0
  83. package/.codex-plugin/skills/memory-cleanup/SKILL.md +21 -0
  84. package/.codex-plugin/skills/memory-cleanup/agents/openai.yaml +5 -0
  85. package/.codex-plugin/skills/mode-selector/SKILL.md +19 -0
  86. package/.codex-plugin/skills/npm-publish/SKILL.md +18 -0
  87. package/.codex-plugin/skills/peekaboo-driver/SKILL.md +20 -0
  88. package/.codex-plugin/skills/persona-panel/SKILL.md +22 -0
  89. package/.codex-plugin/skills/persona-panel/agents/openai.yaml +5 -0
  90. package/.codex-plugin/skills/plan/SKILL.md +22 -0
  91. package/.codex-plugin/skills/plan/agents/openai.yaml +5 -0
  92. package/.codex-plugin/skills/playwright-driver/SKILL.md +22 -0
  93. package/.codex-plugin/skills/portfolio/SKILL.md +21 -0
  94. package/.codex-plugin/skills/portfolio/agents/openai.yaml +5 -0
  95. package/.codex-plugin/skills/quality-gates/SKILL.md +22 -0
  96. package/.codex-plugin/skills/reconcile/SKILL.md +21 -0
  97. package/.codex-plugin/skills/reconcile/agents/openai.yaml +5 -0
  98. package/.codex-plugin/skills/release/SKILL.md +22 -0
  99. package/.codex-plugin/skills/release/agents/openai.yaml +5 -0
  100. package/.codex-plugin/skills/remote-offload/SKILL.md +22 -0
  101. package/.codex-plugin/skills/repo-audit/SKILL.md +19 -0
  102. package/.codex-plugin/skills/repo-audit/agents/openai.yaml +5 -0
  103. package/.codex-plugin/skills/session/SKILL.md +21 -0
  104. package/.codex-plugin/skills/session/agents/openai.yaml +5 -0
  105. package/.codex-plugin/skills/session-end/SKILL.md +22 -0
  106. package/.codex-plugin/skills/session-plan/SKILL.md +22 -0
  107. package/.codex-plugin/skills/session-start/SKILL.md +22 -0
  108. package/.codex-plugin/skills/spinout/SKILL.md +21 -0
  109. package/.codex-plugin/skills/spinout/agents/openai.yaml +5 -0
  110. package/.codex-plugin/skills/sunset-review/SKILL.md +21 -0
  111. package/.codex-plugin/skills/sunset-review/agents/openai.yaml +5 -0
  112. package/.codex-plugin/skills/templates-ack/SKILL.md +21 -0
  113. package/.codex-plugin/skills/templates-ack/agents/openai.yaml +5 -0
  114. package/.codex-plugin/skills/test/SKILL.md +21 -0
  115. package/.codex-plugin/skills/test/agents/openai.yaml +5 -0
  116. package/.codex-plugin/skills/test-runner/SKILL.md +22 -0
  117. package/.codex-plugin/skills/tmux-layout/SKILL.md +23 -0
  118. package/.codex-plugin/skills/using-orchestrator/SKILL.md +19 -0
  119. package/.codex-plugin/skills/vault-mirror/SKILL.md +17 -0
  120. package/.codex-plugin/skills/vault-sync/SKILL.md +17 -0
  121. package/.codex-plugin/skills/wave-executor/SKILL.md +22 -0
  122. package/.codex-plugin/skills/write-executable-plan/SKILL.md +24 -0
  123. package/.cursor/commands/autopilot.md +2 -2
  124. package/.cursor/commands/bootstrap.md +1 -1
  125. package/.cursor/commands/brainstorm.md +1 -1
  126. package/.cursor/commands/debug.md +1 -1
  127. package/.cursor/commands/discovery.md +1 -1
  128. package/.cursor/commands/dispatcher.md +2 -2
  129. package/.cursor/commands/eli5.md +2 -2
  130. package/.cursor/commands/eval.md +2 -2
  131. package/.cursor/commands/evolve.md +1 -1
  132. package/.cursor/commands/go.md +1 -1
  133. package/.cursor/commands/grill.md +2 -2
  134. package/.cursor/commands/memory-cleanup.md +2 -2
  135. package/.cursor/commands/persona-panel.md +1 -1
  136. package/.cursor/commands/plan.md +1 -1
  137. package/.cursor/commands/portfolio.md +1 -1
  138. package/.cursor/commands/reconcile.md +2 -2
  139. package/.cursor/commands/release.md +2 -2
  140. package/.cursor/commands/session.md +2 -2
  141. package/.cursor/commands/spinout.md +2 -2
  142. package/.cursor/commands/sunset-review.md +2 -2
  143. package/.cursor/commands/templates-ack.md +2 -2
  144. package/.cursor/commands/test.md +2 -2
  145. package/.cursor/skills/brainstorm/SKILL.md +1 -1
  146. package/.cursor/skills/eval/SKILL.md +1 -1
  147. package/.cursor/skills/quality-gates/SKILL.md +1 -1
  148. package/.cursor/skills/remote-offload/SKILL.md +1 -1
  149. package/.cursor-plugin/plugin.json +30 -0
  150. package/.orchestrator/policy/blocked-commands.json +121 -0
  151. package/.orchestrator/policy/ecosystem.schema.json +66 -0
  152. package/.orchestrator/policy/quality-gates.example.json +16 -0
  153. package/.orchestrator/policy/quality-gates.schema.json +38 -0
  154. package/.orchestrator/policy/templates-policy.json +27 -0
  155. package/.orchestrator/policy/test-profiles.json +47 -0
  156. package/AGENTS.md +225 -0
  157. package/CHANGELOG.md +1314 -2
  158. package/NOTICE +11 -6
  159. package/README.md +135 -94
  160. package/agents/eval-judge.md +1 -1
  161. package/agents/skill-applied-judge.md +1 -1
  162. package/assets/wave-lifecycle.svg +98 -0
  163. package/commands/release.md +6 -3
  164. package/commands/session.md +18 -3
  165. package/docs/README.md +4 -0
  166. package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
  167. package/docs/baseline.md +67 -0
  168. package/docs/ci-setup.md +108 -62
  169. package/docs/codex-setup.md +107 -29
  170. package/docs/components.md +38 -16
  171. package/docs/cursor-setup.md +6 -2
  172. package/docs/events-schema.md +9 -6
  173. package/docs/instruction-delivery.md +69 -0
  174. package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
  175. package/docs/migration-v4.md +365 -0
  176. package/docs/pi-setup.md +6 -1
  177. package/docs/plugin-architecture-v3.md +1 -1
  178. package/docs/rule-authoring.md +85 -19
  179. package/docs/scope-collision-guard.md +5 -5
  180. package/docs/session-config-reference.md +57 -56
  181. package/docs/session-config-template.md +6 -29
  182. package/docs/telemetry.md +157 -3
  183. package/docs/vault-docs-architecture.md +50 -11
  184. package/hooks/_lib/hook-import-set.json +1488 -0
  185. package/hooks/_lib/subagent-transcript.mjs +562 -0
  186. package/hooks/config-protection.mjs +2 -2
  187. package/hooks/cwd-change-restore.mjs +2 -2
  188. package/hooks/enforce-commands.mjs +69 -0
  189. package/hooks/hooks-codex.json +1 -1
  190. package/hooks/hooks-cursor.json +10 -0
  191. package/hooks/hooks-pi.json +5 -0
  192. package/hooks/hooks.json +6 -1
  193. package/hooks/loop-guard.mjs +3 -3
  194. package/hooks/on-session-end.mjs +2 -2
  195. package/hooks/on-session-start.mjs +103 -2
  196. package/hooks/on-stop.mjs +60 -14
  197. package/hooks/operator-steer.mjs +2 -2
  198. package/hooks/post-bash-write-verify.mjs +85 -0
  199. package/hooks/post-edit-import-probe.mjs +344 -0
  200. package/hooks/post-subagent-discovery-validator.mjs +187 -431
  201. package/hooks/post-tool-batch-wave-signal.mjs +118 -4
  202. package/hooks/post-tool-failure-corrective-context.mjs +2 -2
  203. package/hooks/post-tooluse-frontend-slop.mjs +3 -3
  204. package/hooks/pre-bash-destructive-guard.mjs +39 -13
  205. package/hooks/skill-invocation-telemetry.mjs +17 -5
  206. package/hooks/subagent-telemetry.mjs +13 -4
  207. package/monitors/monitors.json +3 -3
  208. package/package.json +9 -1
  209. package/pi/prompts/session.md +2 -2
  210. package/scripts/backfill-abandoned-sessions.mjs +50 -4
  211. package/scripts/backfill-learnings-from-vault.mjs +9 -3
  212. package/scripts/dialectic-deriver.mjs +73 -8
  213. package/scripts/export-hw-learnings.mjs +113 -1
  214. package/scripts/generate-agents-skills.mjs +378 -0
  215. package/scripts/generate-codex-skills.mjs +246 -0
  216. package/scripts/generate-cursor-adapter.mjs +45 -8
  217. package/scripts/generate-hook-import-set.mjs +292 -0
  218. package/scripts/lib/agent-status.mjs +13 -2
  219. package/scripts/lib/auto-dream.mjs +38 -36
  220. package/scripts/lib/autonomy/suitability.mjs +6 -0
  221. package/scripts/lib/autopilot/loop.mjs +2 -2
  222. package/scripts/lib/ci-status-banner.mjs +220 -75
  223. package/scripts/lib/codex/plugin-contract.mjs +88 -6
  224. package/scripts/lib/config/auto-dream.mjs +2 -1
  225. package/scripts/lib/config/block-header.mjs +8 -0
  226. package/scripts/lib/config/block-preprocess.mjs +177 -0
  227. package/scripts/lib/config/broken-window.mjs +2 -1
  228. package/scripts/lib/config/cold-start.mjs +2 -1
  229. package/scripts/lib/config/config-protection.mjs +22 -2
  230. package/scripts/lib/config/context-coverage.mjs +2 -1
  231. package/scripts/lib/config/cross-repo.mjs +2 -1
  232. package/scripts/lib/config/custom-phases.mjs +2 -1
  233. package/scripts/lib/config/dialectic.mjs +2 -1
  234. package/scripts/lib/config/discovery-validator.mjs +2 -1
  235. package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
  236. package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
  237. package/scripts/lib/config/docs-orchestrator.mjs +2 -1
  238. package/scripts/lib/config/docs-staleness.mjs +2 -1
  239. package/scripts/lib/config/drift-check.mjs +2 -1
  240. package/scripts/lib/config/eval.mjs +2 -1
  241. package/scripts/lib/config/events-rotation.mjs +2 -1
  242. package/scripts/lib/config/evolve.mjs +8 -2
  243. package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
  244. package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
  245. package/scripts/lib/config/handover-gate.mjs +2 -1
  246. package/scripts/lib/config/health-endpoints.mjs +7 -2
  247. package/scripts/lib/config/host-paths.mjs +20 -4
  248. package/scripts/lib/config/issue-budget.mjs +2 -1
  249. package/scripts/lib/config/loop-guard.mjs +2 -1
  250. package/scripts/lib/config/memory.mjs +2 -1
  251. package/scripts/lib/config/moc-staleness.mjs +2 -1
  252. package/scripts/lib/config/persona-gate-wave.mjs +2 -1
  253. package/scripts/lib/config/private-config-dir.mjs +67 -0
  254. package/scripts/lib/config/reconcile.mjs +2 -1
  255. package/scripts/lib/config/remote-hosts.mjs +2 -1
  256. package/scripts/lib/config/section-extractor.mjs +7 -1
  257. package/scripts/lib/config/skill-evolution.mjs +2 -1
  258. package/scripts/lib/config/slopcheck.mjs +2 -1
  259. package/scripts/lib/config/state-md-lock.mjs +2 -1
  260. package/scripts/lib/config/templates-first.mjs +2 -1
  261. package/scripts/lib/config/test.mjs +2 -1
  262. package/scripts/lib/config/vault-integration.mjs +7 -1
  263. package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
  264. package/scripts/lib/config/vault-staleness.mjs +2 -1
  265. package/scripts/lib/config/vault-sync.mjs +2 -1
  266. package/scripts/lib/config/verification-auto-fix.mjs +2 -1
  267. package/scripts/lib/config/wave-reviewers.mjs +2 -1
  268. package/scripts/lib/config/worktree-orphans.mjs +2 -1
  269. package/scripts/lib/convergence-monitor.mjs +82 -16
  270. package/scripts/lib/dispatcher/rank.mjs +124 -48
  271. package/scripts/lib/ecosystem-health.mjs +16 -2
  272. package/scripts/lib/eval/engine.mjs +9 -1
  273. package/scripts/lib/eval/session-resolve.mjs +23 -4
  274. package/scripts/lib/events.mjs +22 -6
  275. package/scripts/lib/frontmatter-guard.mjs +131 -13
  276. package/scripts/lib/gates/gate-full.mjs +30 -0
  277. package/scripts/lib/gates/gate-helpers.mjs +76 -0
  278. package/scripts/lib/hardware-pattern-detector.mjs +18 -1
  279. package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
  280. package/scripts/lib/host-identity.mjs +50 -11
  281. package/scripts/lib/instruction-budget-guard.mjs +171 -5
  282. package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
  283. package/scripts/lib/learnings/io.mjs +60 -6
  284. package/scripts/lib/memory-proposals/store.mjs +30 -22
  285. package/scripts/lib/owner-config-banner.mjs +41 -6
  286. package/scripts/lib/owner-config-loader.mjs +21 -10
  287. package/scripts/lib/owner-interview.mjs +3 -3
  288. package/scripts/lib/owner-yaml.mjs +215 -15
  289. package/scripts/lib/platform.mjs +108 -15
  290. package/scripts/lib/plugin-update-banner.mjs +414 -0
  291. package/scripts/lib/project-hygiene.mjs +38 -2
  292. package/scripts/lib/qg-command-drift-banner.mjs +50 -12
  293. package/scripts/lib/quality-gate.mjs +133 -44
  294. package/scripts/lib/reconcile/emitter.mjs +68 -6
  295. package/scripts/lib/reconcile/engine.mjs +51 -11
  296. package/scripts/lib/reconcile/idempotency.mjs +37 -4
  297. package/scripts/lib/reconcile/writer.mjs +40 -18
  298. package/scripts/lib/session-close-backfill.mjs +67 -9
  299. package/scripts/lib/session-id.mjs +12 -23
  300. package/scripts/lib/session-identity/own-session.mjs +125 -10
  301. package/scripts/lib/session-lock-shape.mjs +43 -0
  302. package/scripts/lib/session-lock.mjs +5 -10
  303. package/scripts/lib/session-registry.mjs +25 -9
  304. package/scripts/lib/session-schema/constants.mjs +64 -3
  305. package/scripts/lib/session-schema/validator.mjs +38 -4
  306. package/scripts/lib/session-start-probes.mjs +30 -1
  307. package/scripts/lib/sessions-staleness-banner.mjs +18 -11
  308. package/scripts/lib/skill-health/join.mjs +17 -4
  309. package/scripts/lib/state-md.mjs +78 -0
  310. package/scripts/lib/sunset/walker.mjs +6 -0
  311. package/scripts/lib/telemetry/schema.mjs +202 -9
  312. package/scripts/lib/telemetry/sync.mjs +368 -12
  313. package/scripts/lib/telemetry-flush-health-banner.mjs +211 -0
  314. package/scripts/lib/validate/check-agents-skills.mjs +327 -0
  315. package/scripts/lib/validate/check-agents.mjs +3 -3
  316. package/scripts/lib/validate/check-codex-skills.mjs +191 -0
  317. package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
  318. package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
  319. package/scripts/lib/validate/check-owner-leakage.mjs +319 -22
  320. package/scripts/lib/validate/check-skill-links.mjs +193 -0
  321. package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
  322. package/scripts/lib/validate/check-test-git-config-target.mjs +192 -12
  323. package/scripts/lib/validate/check-unwired-features.mjs +163 -15
  324. package/scripts/lib/validate/check-validator-registration.mjs +10 -4
  325. package/scripts/lib/validate/confidential-names.mjs +95 -30
  326. package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
  327. package/scripts/lib/validate/repo-files.mjs +48 -14
  328. package/scripts/lib/vault-backfill/template.mjs +63 -6
  329. package/scripts/lib/vault-mirror/process.mjs +165 -42
  330. package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
  331. package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
  332. package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
  333. package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
  334. package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
  335. package/scripts/lib/wave-resource-gate.mjs +8 -2
  336. package/scripts/lib/wave-sizing.mjs +4 -1
  337. package/scripts/lib/wave-transcript-tail.mjs +118 -4
  338. package/scripts/materialize-wave-scope.mjs +12 -5
  339. package/scripts/memory-propose.mjs +19 -5
  340. package/scripts/migrate-cold-start-seed.mjs +4 -1
  341. package/scripts/parse-config.mjs +60 -3
  342. package/scripts/release.mjs +430 -31
  343. package/scripts/repair-invalid-sessions.mjs +3 -3
  344. package/scripts/run-quality-gate.mjs +128 -11
  345. package/scripts/site-numbers.mjs +344 -8
  346. package/scripts/sweep-expired-learnings.mjs +90 -0
  347. package/scripts/sync-vault-schema.mjs +3 -1
  348. package/scripts/telemetry.mjs +2 -2
  349. package/scripts/validate-plugin.mjs +164 -0
  350. package/scripts/validate-wave-scope.mjs +28 -8
  351. package/scripts/wave-scope-binding.mjs +215 -0
  352. package/skills/_shared/instruction-file-resolution.md +10 -0
  353. package/skills/_shared/parallel-aware-preamble.md +1 -0
  354. package/skills/_shared/platform-tools.md +1 -1
  355. package/skills/_shared/state-ownership.md +1 -1
  356. package/skills/architecture/SKILL.md +7 -5
  357. package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
  358. package/skills/autopilot/SKILL.md +4 -18
  359. package/skills/claude-md-drift-check/SKILL.md +5 -1
  360. package/skills/claude-md-drift-check/checker.mjs +62 -2
  361. package/skills/convergence-monitoring/SIGNALS.md +55 -0
  362. package/skills/discovery/probes/vault-staleness.mjs +37 -13
  363. package/skills/discovery/probes-arch.md +20 -18
  364. package/skills/dispatcher/SKILL.md +3 -2
  365. package/skills/evolve/SKILL.md +65 -26
  366. package/skills/frontmatter-guard/SKILL.md +11 -5
  367. package/skills/npm-publish/SKILL.md +1 -1
  368. package/skills/reconcile/SKILL.md +33 -0
  369. package/skills/remote-offload/SKILL.md +1 -1
  370. package/skills/session-end/SKILL.md +18 -905
  371. package/skills/session-end/phase-3-6-tail.md +10 -3
  372. package/skills/session-end/plan-verification.md +221 -155
  373. package/skills/session-end/references/phase-2-quality-gate.md +93 -0
  374. package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
  375. package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
  376. package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
  377. package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
  378. package/skills/session-end/references/session-summary-template.md +62 -0
  379. package/skills/session-plan/SKILL.md +49 -0
  380. package/skills/session-start/SKILL.md +22 -904
  381. package/skills/session-start/phase-8-5-express-path.md +1 -1
  382. package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
  383. package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
  384. package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
  385. package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
  386. package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
  387. package/skills/session-start/references/phase-4-ssot-environment-check.md +160 -0
  388. package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
  389. package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
  390. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
  391. package/skills/vault-sync/SKILL.md +10 -0
  392. package/skills/vault-sync/validator.mjs +21 -27
  393. package/skills/wave-executor/SKILL.md +15 -1
  394. package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
  395. package/skills/wave-executor/references/wave-loop-review.md +570 -0
  396. package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
  397. package/skills/wave-executor/wave-loop.md +14 -1309
  398. package/templates/_shared/journey-manifest.md +10 -6
  399. package/.cursor/commands/autopilot-multi.md +0 -14
  400. package/.cursor/commands/contract-version-bump.md +0 -14
  401. package/.cursor/commands/journey-audit.md +0 -14
  402. package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
  403. package/.cursor/skills/daily/SKILL.md +0 -12
  404. package/.cursor/skills/domain-model/SKILL.md +0 -13
  405. package/.cursor/skills/journey-audit/SKILL.md +0 -13
  406. package/.cursor/skills/skill-creator/SKILL.md +0 -13
  407. package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
  408. package/commands/autopilot-multi.md +0 -74
  409. package/commands/contract-version-bump.md +0 -28
  410. package/commands/journey-audit.md +0 -43
  411. package/pi/prompts/autopilot-multi.md +0 -12
  412. package/pi/prompts/contract-version-bump.md +0 -12
  413. package/pi/prompts/journey-audit.md +0 -12
  414. package/scripts/autopilot-multi.mjs +0 -885
  415. package/scripts/backfill-learnings-expires.mjs +0 -196
  416. package/scripts/backfill-learnings.mjs +0 -203
  417. package/scripts/fleet-instruction-scan.mjs +0 -141
  418. package/scripts/lib/autopilot/dep-graph.mjs +0 -417
  419. package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
  420. package/scripts/lib/webhook-url.mjs +0 -105
  421. package/scripts/lifecycle-sim-v6.mjs +0 -347
  422. package/scripts/migrate-learnings-jsonl.mjs +0 -189
  423. package/scripts/migrate-subagents-jsonl.mjs +0 -196
  424. package/scripts/upload-social-preview.mjs +0 -316
  425. package/skills/_shared/model-selection.md +0 -64
  426. package/skills/contract-version-bump/SKILL.md +0 -219
  427. package/skills/daily/SKILL.md +0 -222
  428. package/skills/daily/generate.sh +0 -92
  429. package/skills/daily/templates/daily.md.tpl +0 -36
  430. package/skills/journey-audit/SKILL.md +0 -270
  431. package/skills/skill-creator/SKILL.md +0 -168
  432. package/skills/ubiquitous-language/SKILL.md +0 -97
  433. package/skills/vault-sync/package-lock.json +0 -40
  434. /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
  435. /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * telemetry-flush-health-banner.mjs — #1255
3
+ *
4
+ * Surfaces the ONE telemetry-flush outcome nobody could see: a flush the
5
+ * sandbox guard refused.
6
+ *
7
+ * ## Why this module exists
8
+ *
9
+ * `scripts/lib/telemetry/sync.mjs` fails CLOSED since 4.0.0 — when its
10
+ * environment probe cannot complete its checks it returns
11
+ * `{ sandbox: true, reason: 'sandbox:probe-failed' }` and sends nothing. That
12
+ * is the right default, and `sync.mjs` stays deliberately SILENT about it (it
13
+ * runs inside the SessionEnd teardown budget, where a banner has no reader).
14
+ *
15
+ * The refusal therefore reaches exactly one sink: `hooks/on-session-end.mjs`
16
+ * emits `orchestrator.telemetry.flush` with `{ outcome, reason }` into
17
+ * `.orchestrator/metrics/events.jsonl`. Measured 2026-09-07 (W1-D6 census):
18
+ * NO consumer read that reason back out — a repo could refuse every flush for
19
+ * weeks and the only visible difference from a healthy repo was an absence.
20
+ * That is `.claude/rules/host-resources.md` § HR-105 ("a rule you cannot
21
+ * falsify is not a rule") applied to the flush path.
22
+ *
23
+ * This probe closes the loop on the NEXT session start, which is the first
24
+ * moment a human is actually looking: it reads the LAST flush record and warns
25
+ * when that record is a sandbox refusal. Newest-wins by construction — a later
26
+ * successful flush silently clears the warning, so the banner reports the
27
+ * CURRENT state of the channel, never its history.
28
+ *
29
+ * Never throws. Never mutates input. No network.
30
+ *
31
+ * @module scripts/lib/telemetry-flush-health-banner
32
+ */
33
+
34
+ import { existsSync, openSync, readSync, fstatSync, closeSync } from 'node:fs';
35
+ import path from 'node:path';
36
+
37
+ /**
38
+ * Bytes of `events.jsonl` read from the END of the file.
39
+ *
40
+ * The ledger is append-only and grows without bound (33k+ records in this repo
41
+ * alone), so a full `readFileSync` on a SessionStart hook's critical path is
42
+ * the wrong shape — this probe shares a 2s budget with ~18 siblings.
43
+ *
44
+ * NAMED CEILING (BV-004): a flush record older than the last 64 KB of the
45
+ * ledger is invisible to this probe and reads as "no flush recorded" → `null`
46
+ * (silent). At the observed record width (~120-400 bytes) that window holds
47
+ * roughly 150-500 events, and a flush is emitted once per session close — so
48
+ * the window covers the last flush unless ~150+ events landed after it without
49
+ * one, which cannot happen inside a single session's teardown.
50
+ * REVISIT TRIGGER: if the per-session event volume ever exceeds ~150 records
51
+ * between two session ends, raise this or index the ledger — do not silently
52
+ * accept the truncation.
53
+ */
54
+ export const TAIL_BYTES = 64 * 1024;
55
+
56
+ /** The event name `hooks/on-session-end.mjs` emits for every flush attempt. */
57
+ const FLUSH_EVENT = 'orchestrator.telemetry.flush';
58
+
59
+ /**
60
+ * Bound an untrusted ledger `reason` before it is interpolated into a terminal
61
+ * banner: strip C0/DEL control bytes (a record could carry an ANSI escape) and
62
+ * cap the length. Mirrors the sibling bound in
63
+ * `scripts/lib/session-start-probes.mjs` (`.slice(0, 200)` on a probe error).
64
+ *
65
+ * @param {unknown} reason
66
+ * @returns {string}
67
+ */
68
+ function sanitizeReason(reason) {
69
+ // eslint-disable-next-line no-control-regex -- stripping control bytes IS the job
70
+ return String(reason).replace(/[\x00-\x1f\x7f]/g, '').slice(0, 120);
71
+ }
72
+
73
+ /**
74
+ * Read the last `TAIL_BYTES` of a file as UTF-8.
75
+ *
76
+ * Returns a DISCRIMINATED result rather than `string|null`, because "the
77
+ * ledger could not be read" and "the ledger holds nothing alarming" are the
78
+ * two states this probe exists to keep apart (HR-105): collapsing an EACCES
79
+ * onto `null` renders an unreadable channel as a healthy one.
80
+ *
81
+ * @param {string} file
82
+ * @returns {{text: string} | {missing: true} | {error: string}}
83
+ */
84
+ function readTail(file) {
85
+ let fd;
86
+ try {
87
+ fd = openSync(file, 'r');
88
+ const { size } = fstatSync(fd);
89
+ const length = Math.min(size, TAIL_BYTES);
90
+ const start = size - length;
91
+ const buf = Buffer.allocUnsafe(length);
92
+ let read = 0;
93
+ while (read < length) {
94
+ const n = readSync(fd, buf, read, length - read, start + read);
95
+ if (n <= 0) break;
96
+ read += n;
97
+ }
98
+ return { text: buf.subarray(0, read).toString('utf8') };
99
+ } catch (err) {
100
+ if (err?.code === 'ENOENT') return { missing: true };
101
+ return { error: typeof err?.code === 'string' ? err.code : 'EUNKNOWN' };
102
+ } finally {
103
+ if (fd !== undefined) {
104
+ try {
105
+ closeSync(fd);
106
+ } catch {
107
+ /* best effort */
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Classify the telemetry-flush channel from the LAST recorded flush event.
115
+ *
116
+ * Scans the ledger tail BACKWARDS and stops at the first
117
+ * `orchestrator.telemetry.flush` record it can parse — newest wins, so an
118
+ * older refusal followed by a successful flush produces no banner.
119
+ *
120
+ * Returns `null` (silent) when: `repoRoot` is not a string, the ledger is
121
+ * ABSENT, no flush record sits in the tail window, or the newest flush
122
+ * record's `reason` does not start with `sandbox:`. A malformed line is
123
+ * SKIPPED (it is not a flush record we can read), never a throw — the ledger's
124
+ * last line is routinely a partial write when another process is appending.
125
+ *
126
+ * An UNREADABLE ledger (EACCES/EIO/…, i.e. anything other than "not there") is
127
+ * NOT silent: it returns a `ledger-unreadable` warning. "The guard state
128
+ * cannot be confirmed" must not display like "the last flush was fine" — that
129
+ * collapse is the exact HR-105 defect this module's header cites as its reason
130
+ * to exist, one layer down.
131
+ *
132
+ * This probe reports the LAST outcome regardless of its age (no clock seam):
133
+ * a refusal does not expire on its own — only a later successful flush clears
134
+ * it.
135
+ *
136
+ * @param {{repoRoot: string}} opts
137
+ * @returns {null | {severity: 'warn', reason: string, message: string}}
138
+ */
139
+ export function checkTelemetryFlushHealth({ repoRoot } = {}) {
140
+ try {
141
+ if (!repoRoot || typeof repoRoot !== 'string') return null;
142
+
143
+ const file = path.join(repoRoot, '.orchestrator', 'metrics', 'events.jsonl');
144
+ if (!existsSync(file)) return null;
145
+
146
+ const tail = readTail(file);
147
+ if (tail.missing) return null;
148
+ if (tail.error) {
149
+ return {
150
+ severity: 'warn',
151
+ reason: 'ledger-unreadable',
152
+ message:
153
+ '⚠ Telemetry: flush-health unknown — .orchestrator/metrics/events.jsonl ' +
154
+ `could not be read (${sanitizeReason(tail.error)}); ` +
155
+ 'the sandbox guard state cannot be confirmed.',
156
+ };
157
+ }
158
+ if (!tail.text) return null;
159
+
160
+ const lines = tail.text.split('\n');
161
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
162
+ const line = lines[i].trim();
163
+ if (!line) continue;
164
+
165
+ let record;
166
+ try {
167
+ record = JSON.parse(line);
168
+ } catch {
169
+ // Malformed (or a tail-truncated first line) — not readable as a flush
170
+ // record, so it cannot be the newest one. Keep scanning backwards.
171
+ continue;
172
+ }
173
+ if (!record || typeof record !== 'object') continue;
174
+ if (record.event !== FLUSH_EVENT) continue;
175
+
176
+ // First flush record found scanning backwards = the newest one.
177
+ const rawReason = record.reason;
178
+ if (typeof rawReason !== 'string' || !rawReason.startsWith('sandbox:')) return null;
179
+
180
+ // The ledger is an untrusted string source for banner purposes: bound the
181
+ // reason on BOTH surfaces (the field a consumer may render itself, and
182
+ // the message we render) rather than only on the one we happen to own.
183
+ const reason = sanitizeReason(rawReason);
184
+
185
+ return {
186
+ severity: 'warn',
187
+ reason,
188
+ message:
189
+ `⚠ Telemetry: last flush refused by the sandbox guard (${reason}) — ` +
190
+ 'the guard could not complete its checks and failed closed; no ping was sent. ' +
191
+ 'See docs/telemetry.md § Sandbox guard.',
192
+ };
193
+ }
194
+
195
+ return null;
196
+ } catch {
197
+ // Defensive catch-all — a banner probe must never throw (fail-open).
198
+ return null;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Convenience renderer: the banner message string, or `''` when silent.
204
+ *
205
+ * @param {{repoRoot: string}} opts
206
+ * @returns {string}
207
+ */
208
+ export function renderBanner({ repoRoot } = {}) {
209
+ const result = checkTelemetryFlushHealth({ repoRoot });
210
+ return result ? result.message : '';
211
+ }
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * check-agents-skills.mjs — validate the CROSS-HARNESS PORTABLE SURFACE.
4
+ *
5
+ * Scope note on the name: "agents-skills" is the `.agents`/`AGENTS`-family
6
+ * surface, not just the skill mirror. Three artefacts are checked, because
7
+ * they are one feature — the set of files a NON-Claude harness (Codex CLI,
8
+ * Cursor, Amp, Copilot CLI, OpenCode, Kiro) reads when it opens this repo:
9
+ *
10
+ * A. root `AGENTS.md` — must be an alias of `CLAUDE.md` (symlink to the same
11
+ * inode, or byte-identical).
12
+ * B. root `plugin.json` — the agent-plugins.org 1.0.0 manifest; its `version`
13
+ * must equal `package.json`'s and the two existing plugin manifests'.
14
+ * C. `.agents/skills/<name>/SKILL.md` — one spec-legal mirror per source
15
+ * skill, no orphans, agentskills.io field list only, description ≤ 1024.
16
+ *
17
+ * INDEPENDENCE (load-bearing). This checker deliberately imports NOTHING from
18
+ * `scripts/generate-agents-skills.mjs` and re-derives the spec field list, the
19
+ * 1024 cap and the alias rule from the spec, not from the generator. A
20
+ * generator-vs-generator check only proves the generator is self-consistent —
21
+ * that is how the Cursor `argument-hint` defect survived. The oracles here are
22
+ * the repository's own filesystem (`skills/`, `CLAUDE.md`, `package.json`) and
23
+ * the published field list.
24
+ *
25
+ * Usage: node scripts/lib/validate/check-agents-skills.mjs <plugin-root>
26
+ * Exit codes: 0 — all checks passed · 1 — one or more failures.
27
+ */
28
+
29
+ import { existsSync, readFileSync, readdirSync, statSync, lstatSync, realpathSync } from 'node:fs';
30
+ import { join } from 'node:path';
31
+ import yaml from 'js-yaml';
32
+
33
+ const pluginRoot = process.argv[2];
34
+ if (!pluginRoot) {
35
+ console.error('Usage: check-agents-skills.mjs <plugin-root>');
36
+ process.exit(1);
37
+ }
38
+
39
+ let passed = 0;
40
+ let failed = 0;
41
+ const pass = (msg) => { console.log(` PASS: ${msg}`); passed += 1; };
42
+ const fail = (msg) => { console.log(` FAIL: ${msg}`); failed += 1; };
43
+
44
+ /**
45
+ * The complete agentskills.io field list permitted outside Claude Code.
46
+ * Re-derived from the spec here on purpose — see INDEPENDENCE above.
47
+ */
48
+ const SPEC_KEYS = new Set(['name', 'description', 'license', 'compatibility', 'metadata', 'allowed-tools']);
49
+ const DESCRIPTION_MAX = 1024;
50
+
51
+ /**
52
+ * Per-mirror byte ceiling. A mirror is a frontmatter block plus a 6-line
53
+ * pointer; the largest one measured 2026-09-06 was ~1.9 KB (its description is
54
+ * 1012 chars). The ceiling exists to catch the failure this design prevents —
55
+ * a mirror that starts duplicating the canonical body, which would double the
56
+ * instruction corpus a foreign harness loads. Revisit trigger: raise it only
57
+ * together with a re-measurement of `.agents/skills/` total bytes, never to
58
+ * accommodate a body that was pasted in.
59
+ */
60
+ const MIRROR_MAX_BYTES = 4096;
61
+
62
+ const AGENT_PLUGINS_SCHEMA = 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json';
63
+
64
+ /** Read + parse a JSON file; null on missing or malformed. */
65
+ function readJson(absPath) {
66
+ if (!existsSync(absPath)) return null;
67
+ try { return JSON.parse(readFileSync(absPath, 'utf8')); } catch { return null; }
68
+ }
69
+
70
+ /** Split a Markdown file into `{ frontmatter, body }`; frontmatter null when absent/unparseable. */
71
+ function splitFrontmatter(content) {
72
+ const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(content);
73
+ if (!m) return { frontmatter: null, body: content };
74
+ try {
75
+ const parsed = yaml.load(m[1]);
76
+ return {
77
+ frontmatter: parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null,
78
+ body: m[2],
79
+ };
80
+ } catch {
81
+ return { frontmatter: null, body: m[2] };
82
+ }
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // A. root AGENTS.md is an alias of CLAUDE.md
87
+ // ---------------------------------------------------------------------------
88
+
89
+ console.log('--- Check A: root AGENTS.md ↔ CLAUDE.md alias ---');
90
+ {
91
+ const claudePath = join(pluginRoot, 'CLAUDE.md');
92
+ const agentsPath = join(pluginRoot, 'AGENTS.md');
93
+ if (!existsSync(claudePath)) {
94
+ pass('no CLAUDE.md at plugin root — alias check not applicable');
95
+ } else if (!existsSync(agentsPath)) {
96
+ fail('CLAUDE.md exists but AGENTS.md does not — Codex CLI, OpenCode, Kiro and Amp '
97
+ + 'read AGENTS.md and never CLAUDE.md, so this repo would ship without a Session Config for them '
98
+ + '(remedy: node scripts/generate-agents-skills.mjs)');
99
+ } else {
100
+ let aliased = false;
101
+ let how = '';
102
+ try {
103
+ if (lstatSync(agentsPath).isSymbolicLink()) {
104
+ aliased = realpathSync(agentsPath) === realpathSync(claudePath);
105
+ how = 'symlink';
106
+ } else {
107
+ const a = statSync(agentsPath);
108
+ const c = statSync(claudePath);
109
+ if (a.ino !== 0 && a.ino === c.ino && a.dev === c.dev) { aliased = true; how = 'same inode'; }
110
+ }
111
+ } catch { /* fall through to the byte comparison */ }
112
+ if (!aliased) {
113
+ aliased = readFileSync(agentsPath).equals(readFileSync(claudePath));
114
+ how = 'byte-identical';
115
+ }
116
+ if (aliased) {
117
+ pass(`AGENTS.md is an alias of CLAUDE.md (${how})`);
118
+ } else {
119
+ fail('AGENTS.md and CLAUDE.md are two independent files that disagree — AGENTS.md is GENERATED; '
120
+ + 'put the change in CLAUDE.md and run `node scripts/generate-agents-skills.mjs`');
121
+ }
122
+ }
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // B. root plugin.json (agent-plugins.org 1.0.0)
127
+ // ---------------------------------------------------------------------------
128
+
129
+ console.log('');
130
+ console.log('--- Check B: root plugin.json (agent-plugins.org 1.0.0) ---');
131
+ {
132
+ const rootManifest = readJson(join(pluginRoot, 'plugin.json'));
133
+ if (rootManifest === null) {
134
+ if (existsSync(join(pluginRoot, 'plugin.json'))) {
135
+ fail('plugin.json exists but is not valid JSON');
136
+ } else {
137
+ pass('no root plugin.json — vendor-neutral manifest not shipped');
138
+ }
139
+ } else {
140
+ if (rootManifest.$schema === AGENT_PLUGINS_SCHEMA) {
141
+ pass('plugin.json declares the agent-plugins.org 1.0.0 $schema');
142
+ } else {
143
+ fail(`plugin.json $schema is '${rootManifest.$schema ?? '(unset)'}', expected '${AGENT_PLUGINS_SCHEMA}'`);
144
+ }
145
+
146
+ for (const field of ['name', 'version', 'description', 'license']) {
147
+ if (typeof rootManifest[field] === 'string' && rootManifest[field].trim()) {
148
+ pass(`plugin.json has a non-empty '${field}'`);
149
+ } else {
150
+ fail(`plugin.json is missing required field '${field}'`);
151
+ }
152
+ }
153
+
154
+ // Version parity across ALL manifests. Three manifests now carry a version
155
+ // and only `package.json` is bumped by the release script — a third copy
156
+ // that drifts silently is the whole reason this case exists.
157
+ const pkg = readJson(join(pluginRoot, 'package.json'));
158
+ const pkgVersion = pkg?.version ?? null;
159
+ if (!pkgVersion) {
160
+ fail('package.json has no version — cannot verify manifest version parity');
161
+ } else if (rootManifest.version === pkgVersion) {
162
+ pass(`plugin.json version matches package.json (${pkgVersion})`);
163
+ } else {
164
+ fail(`plugin.json version '${rootManifest.version}' != package.json version '${pkgVersion}' `
165
+ + '— the release script bumps package.json only');
166
+ }
167
+
168
+ const claudeManifest = readJson(join(pluginRoot, '.claude-plugin', 'plugin.json'));
169
+ if (claudeManifest?.version && rootManifest.version !== claudeManifest.version) {
170
+ fail(`plugin.json version '${rootManifest.version}' != .claude-plugin/plugin.json version '${claudeManifest.version}'`);
171
+ } else if (claudeManifest?.version) {
172
+ pass('plugin.json version matches .claude-plugin/plugin.json');
173
+ }
174
+
175
+ // Declared component paths must exist — a manifest may not advertise a
176
+ // component this repo does not ship.
177
+ let componentFail = 0;
178
+ for (const [key, value] of Object.entries(rootManifest)) {
179
+ if (typeof value !== 'string' || !value.startsWith('./')) continue;
180
+ if (key === '$schema') continue;
181
+ if (!existsSync(join(pluginRoot, value))) {
182
+ fail(`plugin.json component '${key}' points at '${value}' which does not exist`);
183
+ componentFail += 1;
184
+ }
185
+ }
186
+ if (componentFail === 0) pass('all plugin.json component paths resolve on disk');
187
+ }
188
+ }
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // C. .agents/skills/ portable mirror
192
+ // ---------------------------------------------------------------------------
193
+
194
+ console.log('');
195
+ console.log('--- Check C: .agents/skills/ portable mirror ---');
196
+ {
197
+ const skillsDir = join(pluginRoot, 'skills');
198
+ const mirrorDir = join(pluginRoot, '.agents', 'skills');
199
+
200
+ const sourceSkills = existsSync(skillsDir) && statSync(skillsDir).isDirectory()
201
+ ? readdirSync(skillsDir, { withFileTypes: true })
202
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !e.name.startsWith('_'))
203
+ .map((e) => e.name)
204
+ .filter((n) => existsSync(join(skillsDir, n, 'SKILL.md')))
205
+ .sort()
206
+ : [];
207
+
208
+ if (!existsSync(mirrorDir)) {
209
+ if (sourceSkills.length === 0) {
210
+ pass('no skills/ and no .agents/skills/ — nothing to mirror');
211
+ } else {
212
+ fail(`.agents/skills/ is missing but ${sourceSkills.length} source skill(s) exist `
213
+ + '(remedy: node scripts/generate-agents-skills.mjs)');
214
+ }
215
+ } else {
216
+ const mirrored = readdirSync(mirrorDir, { withFileTypes: true })
217
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.'))
218
+ .map((e) => e.name)
219
+ .sort();
220
+
221
+ const missing = sourceSkills.filter((n) => !mirrored.includes(n));
222
+ const orphans = mirrored.filter((n) => !sourceSkills.includes(n));
223
+ if (missing.length === 0) pass(`every source skill has a mirror (${sourceSkills.length})`);
224
+ else fail(`${missing.length} source skill(s) have no mirror: ${missing.join(', ')}`);
225
+ if (orphans.length === 0) pass('no orphan mirrors');
226
+ else fail(`${orphans.length} orphan mirror(s) with no source skill: ${orphans.join(', ')}`);
227
+
228
+ let fmFail = 0;
229
+ let keyFail = 0;
230
+ let descFail = 0;
231
+ let sizeFail = 0;
232
+ let pointerFail = 0;
233
+ let totalBytes = 0;
234
+
235
+ for (const name of mirrored) {
236
+ const file = join(mirrorDir, name, 'SKILL.md');
237
+ if (!existsSync(file)) {
238
+ fail(`.agents/skills/${name}/ has no SKILL.md`);
239
+ fmFail += 1;
240
+ continue;
241
+ }
242
+ const raw = readFileSync(file, 'utf8');
243
+ totalBytes += Buffer.byteLength(raw, 'utf8');
244
+
245
+ if (Buffer.byteLength(raw, 'utf8') > MIRROR_MAX_BYTES) {
246
+ fail(`.agents/skills/${name}/SKILL.md is ${Buffer.byteLength(raw, 'utf8')} bytes `
247
+ + `(> ${MIRROR_MAX_BYTES}) — the mirror must point at the canonical body, never duplicate it`);
248
+ sizeFail += 1;
249
+ }
250
+
251
+ const { frontmatter, body } = splitFrontmatter(raw);
252
+ if (!frontmatter) {
253
+ fail(`.agents/skills/${name}/SKILL.md has no parseable YAML frontmatter`);
254
+ fmFail += 1;
255
+ continue;
256
+ }
257
+
258
+ for (const key of Object.keys(frontmatter)) {
259
+ if (!SPEC_KEYS.has(key)) {
260
+ fail(`.agents/skills/${name}/SKILL.md frontmatter carries non-spec key '${key}' `
261
+ + `(agentskills.io permits only: ${[...SPEC_KEYS].join(', ')})`);
262
+ keyFail += 1;
263
+ }
264
+ }
265
+
266
+ if (frontmatter.name !== name) {
267
+ fail(`.agents/skills/${name}/SKILL.md declares name '${frontmatter.name}' != directory '${name}'`);
268
+ keyFail += 1;
269
+ } else if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) {
270
+ fail(`.agents/skills/${name}/SKILL.md name is not kebab-case`);
271
+ keyFail += 1;
272
+ }
273
+
274
+ const desc = frontmatter.description;
275
+ if (typeof desc !== 'string' || !desc.trim()) {
276
+ fail(`.agents/skills/${name}/SKILL.md has an empty description`);
277
+ descFail += 1;
278
+ } else if (desc.length > DESCRIPTION_MAX) {
279
+ fail(`.agents/skills/${name}/SKILL.md description is ${desc.length} chars (> ${DESCRIPTION_MAX})`);
280
+ descFail += 1;
281
+ }
282
+
283
+ if ('metadata' in frontmatter) {
284
+ const md = frontmatter.metadata;
285
+ if (!md || typeof md !== 'object' || Array.isArray(md)) {
286
+ fail(`.agents/skills/${name}/SKILL.md metadata is not a string map`);
287
+ keyFail += 1;
288
+ } else {
289
+ for (const [mk, mv] of Object.entries(md)) {
290
+ if (typeof mv !== 'string') {
291
+ fail(`.agents/skills/${name}/SKILL.md metadata.${mk} is ${typeof mv}, expected string`);
292
+ keyFail += 1;
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ if ('allowed-tools' in frontmatter) {
299
+ const tools = frontmatter['allowed-tools'];
300
+ if (!Array.isArray(tools) || tools.some((t) => typeof t !== 'string' || !t.trim())) {
301
+ fail(`.agents/skills/${name}/SKILL.md allowed-tools is not an array of non-empty strings`);
302
+ keyFail += 1;
303
+ }
304
+ }
305
+
306
+ if (!body.includes(`skills/${name}/SKILL.md`)) {
307
+ fail(`.agents/skills/${name}/SKILL.md body does not cite the canonical skills/${name}/SKILL.md `
308
+ + '— the mirror is a pointer, and a pointer with no target is dead weight');
309
+ pointerFail += 1;
310
+ }
311
+ }
312
+
313
+ if (fmFail === 0 && mirrored.length > 0) pass(`all ${mirrored.length} mirrors have parseable frontmatter`);
314
+ if (keyFail === 0 && mirrored.length > 0) pass('all mirrors use only agentskills.io spec fields');
315
+ if (descFail === 0 && mirrored.length > 0) pass(`all mirror descriptions are non-empty and ≤ ${DESCRIPTION_MAX} chars`);
316
+ if (sizeFail === 0 && mirrored.length > 0) pass(`all mirrors ≤ ${MIRROR_MAX_BYTES} bytes (total ${totalBytes} bytes)`);
317
+ if (pointerFail === 0 && mirrored.length > 0) pass('all mirrors cite their canonical SKILL.md');
318
+ }
319
+ }
320
+
321
+ // ---------------------------------------------------------------------------
322
+ // Results
323
+ // ---------------------------------------------------------------------------
324
+
325
+ console.log('');
326
+ console.log(`Results: ${passed} passed, ${failed} failed`);
327
+ process.exit(failed > 0 ? 1 : 0);
@@ -547,7 +547,7 @@ if (existsSync(agentsDir)) {
547
547
  // agents at a glance). With more than 9 agents, colors are deliberately
548
548
  // shared — but two DISPATCHABLE agents sharing a color is a likely
549
549
  // same-wave collision and surfaces as a WARN (not FAIL: deliberate
550
- // cross-phase shares are legitimate — see agents/AGENTS.md § Color
550
+ // cross-phase shares are legitimate — see docs/agent-authoring.md § Color
551
551
  // Allocation Strategy).
552
552
  // Non-dispatchable reference docs (description contains "NOT a dispatchable"
553
553
  // or "Reference documentation") are excluded from the aggregation: they
@@ -581,7 +581,7 @@ if (existsSync(agentsDir)) {
581
581
  for (const [color, names] of colorMap) {
582
582
  if (names.length > 1) {
583
583
  collisions++;
584
- warn(`color collision: ${color} shared by dispatchable agents ${names.sort().join(', ')} — confirm they never co-run in one wave (agents/AGENTS.md § Color Allocation Strategy)`);
584
+ warn(`color collision: ${color} shared by dispatchable agents ${names.sort().join(', ')} — confirm they never co-run in one wave (docs/agent-authoring.md § Color Allocation Strategy)`);
585
585
  }
586
586
  }
587
587
  if (collisions === 0) {
@@ -632,7 +632,7 @@ if (existsSync(agentsDir)) {
632
632
  if (missing.length > 0) {
633
633
  fail(
634
634
  `${agentFile}: repo-write agent (Edit+Write) is missing ${missing.join(' and ')} ` +
635
- '— see .claude/rules/parallel-sessions.md § PSA-007 and agents/AGENTS.md § Authoring Convention',
635
+ '— see .claude/rules/parallel-sessions.md § PSA-007 and docs/agent-authoring.md § Git-Write Ban Requirement',
636
636
  );
637
637
  } else {
638
638
  pass(`${agentFile}: PSA-007 git-write ban present`);