@event4u/agent-config 14.9.0 → 14.11.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 (426) 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 +384 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
  45. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  46. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  47. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  52. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  53. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  54. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  55. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  56. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  57. package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
  58. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
  60. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  61. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  62. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  63. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  64. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  65. package/dist/agent-src/skills/design-review/SKILL.md +80 -2
  66. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  67. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  68. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  69. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  70. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  71. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  72. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  73. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  74. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  75. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  76. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  78. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  79. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  80. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  81. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  82. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  83. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  84. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  85. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  86. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  87. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  88. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  89. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  90. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  91. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  92. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  93. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  94. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  95. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  96. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  97. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  98. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  99. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  100. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  101. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  102. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  103. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  104. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  105. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  106. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  107. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  108. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  109. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  110. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  111. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  112. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  113. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
  114. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  115. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  116. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  117. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  118. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  119. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  120. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  121. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  122. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  123. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  124. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  125. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  126. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  127. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  128. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  129. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  130. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  131. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  132. package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
  133. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  134. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  135. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  136. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  137. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  138. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  139. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  140. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  141. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  142. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  143. package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
  144. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  145. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  146. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  147. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  148. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  149. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  150. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  151. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  152. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  153. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  154. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  155. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  156. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  157. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  158. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  159. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  160. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  161. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  162. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  163. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  164. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  165. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  166. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  167. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  168. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  169. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  170. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  171. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  172. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  173. package/dist/agent-src/templates/playbook.md +75 -0
  174. package/dist/agent-src/templates/roadmaps.md +37 -0
  175. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  176. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  177. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  178. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  179. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  180. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  181. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  182. package/dist/cli/commands/uiAudit.js +221 -0
  183. package/dist/cli/commands/uiAudit.js.map +1 -0
  184. package/dist/cli/commands/uiRender.js +206 -0
  185. package/dist/cli/commands/uiRender.js.map +1 -0
  186. package/dist/cli/main.js +22 -1
  187. package/dist/cli/main.js.map +1 -1
  188. package/dist/cli/mcp/content.js +64 -1
  189. package/dist/cli/mcp/content.js.map +1 -1
  190. package/dist/cli/mcp/dispatch.js +175 -10
  191. package/dist/cli/mcp/dispatch.js.map +1 -1
  192. package/dist/cli/registry.js +3 -0
  193. package/dist/cli/registry.js.map +1 -1
  194. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  195. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  196. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  197. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  198. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  199. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  200. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  201. package/dist/cli-delegate/cmd_conformance.js +3 -3
  202. package/dist/cli-delegate/cmd_doctor.js +3 -3
  203. package/dist/cli-delegate/cmd_export.js +4 -4
  204. package/dist/cli-delegate/cmd_handoff.js +1 -1
  205. package/dist/cli-delegate/cmd_preflight.js +2 -2
  206. package/dist/cli-delegate/cmd_prune.js +4 -4
  207. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  208. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  209. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  210. package/dist/cli-delegate/cmd_sync.js +4 -4
  211. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  212. package/dist/cli-delegate/cmd_validate.js +4 -4
  213. package/dist/discovery/deprecation-report.md +1 -1
  214. package/dist/discovery/discovery-manifest.json +380 -145
  215. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  216. package/dist/discovery/discovery-manifest.summary.md +4 -4
  217. package/dist/discovery/dormancy-report.md +1 -1
  218. package/dist/discovery/orphan-report.md +1 -1
  219. package/dist/discovery/packs.json +18 -8
  220. package/dist/discovery/trust-report.md +3 -3
  221. package/dist/discovery/workspaces.json +16 -6
  222. package/dist/hooks/dispatch.js +2337 -1436
  223. package/dist/install/install.mjs +795 -708
  224. package/dist/mcp/registry-manifest.json +3 -3
  225. package/dist/mcp/server.json +2 -2
  226. package/dist/mcp/server.mjs +1685 -1250
  227. package/dist/router.json +1 -1
  228. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  229. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  230. package/dist/scripts/_lib/scoped_projection.js +84 -0
  231. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  232. package/dist/scripts/_lib/ui_surface.js +89 -0
  233. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  234. package/dist/scripts/condense.js +6 -10
  235. package/dist/scripts/condense.js.map +1 -1
  236. package/dist/scripts/project_thin_rules.js +40 -2
  237. package/dist/scripts/project_thin_rules.js.map +1 -1
  238. package/dist/server/schemas/settings.js +3 -0
  239. package/dist/server/schemas/settings.js.map +1 -1
  240. package/dist/shared/skillRanking.js +159 -0
  241. package/dist/shared/skillRanking.js.map +1 -0
  242. package/docs/contracts/provider-lifecycle.md +47 -18
  243. package/docs/contracts/settings-classes.md +14 -4
  244. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  245. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  246. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  247. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  248. package/docs/guidelines/design-antipatterns.md +4 -2
  249. package/docs/guidelines/design-asset-discipline.md +38 -0
  250. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  251. package/docs/guidelines/design-handover-extraction.md +35 -5
  252. package/docs/guidelines/design-modes.md +41 -1
  253. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  254. package/llms.txt +50 -39
  255. package/package.json +1 -1
  256. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  257. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  258. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  259. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  260. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  261. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  262. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  263. package/src/config/agent-settings.template.yml +28 -0
  264. package/src/config/assurance-capability-registry.json +223 -0
  265. package/src/config/assurance-threshold-budget.json +65 -0
  266. package/src/config/ci-local-parity.yml +14 -0
  267. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  268. package/src/config/dispatch-economy-metrics.json +4 -2
  269. package/src/config/evaluator-budgets.json +14 -14
  270. package/src/config/gate-coverage.yml +227 -8
  271. package/src/config/gate-violation-baselines.json +76 -72
  272. package/src/config/hook-latency-budget.json +1 -0
  273. package/src/config/hook-token-budget.json +137 -125
  274. package/src/config/pack-size-budget.json +4 -2
  275. package/src/config/preamble-payload-budget.json +71 -67
  276. package/src/config/quorum-attendance-budget.json +12 -4
  277. package/src/config/rule-activation-census.json +7 -3
  278. package/src/config/rule-enforcement-baseline.json +0 -1
  279. package/src/config/rule-stub-ceilings.json +112 -91
  280. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  281. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  282. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  283. package/src/scripts/_dispatch.bash +17 -0
  284. package/src/scripts/_lib/as_of.ts +412 -0
  285. package/src/scripts/_lib/exec_evidence.ts +7 -0
  286. package/src/scripts/_lib/hook_settings.ts +33 -0
  287. package/src/scripts/_lib/host_listing_model.ts +192 -0
  288. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  289. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  290. package/src/scripts/_lib/review_independence.ts +62 -10
  291. package/src/scripts/_lib/rule_injection.ts +239 -0
  292. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  293. package/src/scripts/_lib/scoped_projection.ts +96 -0
  294. package/src/scripts/_lib/session_register.ts +44 -2
  295. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  296. package/src/scripts/_lib/transcript_reads.ts +207 -0
  297. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  298. package/src/scripts/_lib/ui_authority.ts +343 -0
  299. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  300. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  301. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  302. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  303. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  304. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  305. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  306. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  307. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  308. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  309. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  310. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  311. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  312. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  313. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  314. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  315. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  316. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  317. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  318. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  319. package/src/scripts/ai-video/stitch.sh +191 -10
  320. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  321. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  322. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  323. package/src/scripts/ai_council/clients.ts +3 -2
  324. package/src/scripts/ai_council/consensus.ts +63 -6
  325. package/src/scripts/ai_council/events_log.ts +46 -1
  326. package/src/scripts/ai_council/orchestrator.ts +103 -108
  327. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  328. package/src/scripts/ai_council/prompts.ts +12 -5
  329. package/src/scripts/ai_council/quorum.ts +82 -1
  330. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  331. package/src/scripts/ai_council/seating.ts +211 -0
  332. package/src/scripts/ai_council/stance_tally.ts +22 -0
  333. package/src/scripts/bench_hook_latency.ts +62 -0
  334. package/src/scripts/build_proof.ts +12 -1
  335. package/src/scripts/capture_skill_catalogue.ts +21 -0
  336. package/src/scripts/check_always_budget.ts +2 -1
  337. package/src/scripts/check_augmentignore.ts +2 -1
  338. package/src/scripts/check_beta_review_markers.ts +2 -1
  339. package/src/scripts/check_composite_arming.ts +364 -0
  340. package/src/scripts/check_corpus_staleness.ts +2 -1
  341. package/src/scripts/check_council_pin_staleness.ts +3 -1
  342. package/src/scripts/check_enforcement_coverage.ts +116 -3
  343. package/src/scripts/check_enforcement_denominator.ts +365 -0
  344. package/src/scripts/check_gate_coverage.ts +2 -1
  345. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  346. package/src/scripts/check_knowledge_cards.ts +2 -1
  347. package/src/scripts/check_knowledge_pages.ts +3 -2
  348. package/src/scripts/check_memory.ts +2 -1
  349. package/src/scripts/check_no_automerge_key.ts +287 -0
  350. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  351. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  352. package/src/scripts/check_proposal.ts +2 -1
  353. package/src/scripts/check_reach_staleness.ts +2 -1
  354. package/src/scripts/check_release_adjacent_health.ts +2 -1
  355. package/src/scripts/check_release_pr_shape.ts +5 -0
  356. package/src/scripts/check_review_schema.ts +34 -1
  357. package/src/scripts/check_roadmap_trackable.ts +48 -3
  358. package/src/scripts/check_source_size_budget.ts +2 -1
  359. package/src/scripts/check_standing_payload_delta.ts +505 -0
  360. package/src/scripts/check_trigger_evals.ts +2 -1
  361. package/src/scripts/classify_change_risk.ts +281 -0
  362. package/src/scripts/compute_skill_tiers.ts +186 -0
  363. package/src/scripts/condense.ts +8 -13
  364. package/src/scripts/council_attendance_metrics.ts +183 -0
  365. package/src/scripts/council_cli.ts +10 -8
  366. package/src/scripts/council_parse_rate.ts +116 -0
  367. package/src/scripts/derive_playbooks.ts +406 -0
  368. package/src/scripts/design_slop_rules.test.ts +7 -0
  369. package/src/scripts/design_slop_rules.ts +56 -2
  370. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  371. package/src/scripts/frontend_corpus_hash.ts +116 -0
  372. package/src/scripts/generate_pack_manifests.ts +78 -0
  373. package/src/scripts/grade_target_readiness.ts +478 -0
  374. package/src/scripts/hook_manifest.json +1 -1
  375. package/src/scripts/hook_manifest.yaml +97 -16
  376. package/src/scripts/hooks/concern_registry.ts +4 -0
  377. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  378. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  379. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  380. package/src/scripts/hot_context_hook.ts +74 -7
  381. package/src/scripts/install.ts +40 -89
  382. package/src/scripts/lint_adapter_tier.ts +663 -0
  383. package/src/scripts/lint_archived_skills.ts +93 -0
  384. package/src/scripts/lint_budget_ownership.ts +2 -1
  385. package/src/scripts/lint_deterministic_time.ts +470 -0
  386. package/src/scripts/lint_framework_leakage.ts +5 -0
  387. package/src/scripts/lint_handoffs.ts +525 -1
  388. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  389. package/src/scripts/lint_marketplace.ts +88 -0
  390. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  391. package/src/scripts/lint_one_off_age.ts +6 -1
  392. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  393. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  394. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  395. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  396. package/src/scripts/lint_spawn_payload.ts +42 -5
  397. package/src/scripts/lint_symptom_intake.ts +2 -1
  398. package/src/scripts/mcp_parity_smoke.ts +30 -4
  399. package/src/scripts/mcp_render.ts +66 -1
  400. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  401. package/src/scripts/measure_risk_class_drift.ts +140 -0
  402. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  403. package/src/scripts/media/lib/adapter-common.sh +211 -1
  404. package/src/scripts/media/lib/adapter-contract.md +74 -6
  405. package/src/scripts/model_rule_injection.ts +907 -0
  406. package/src/scripts/project_thin_rules.ts +45 -2
  407. package/src/scripts/release.ts +66 -860
  408. package/src/scripts/release_drill.ts +59 -3
  409. package/src/scripts/release_env.ts +298 -0
  410. package/src/scripts/release_publication.ts +720 -0
  411. package/src/scripts/roadmap_context.ts +778 -0
  412. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  413. package/src/scripts/schemas/pack.schema.json +62 -4
  414. package/src/scripts/schemas/rule.schema.json +24 -2
  415. package/src/scripts/schemas/skill.schema.json +41 -2
  416. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  417. package/src/scripts/session_register_hook.ts +29 -3
  418. package/src/scripts/sessions_cli.ts +67 -3
  419. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  420. package/src/scripts/tighten_skill_descriptions.ts +264 -0
  421. package/src/scripts/token_sink_report.ts +239 -0
  422. package/src/scripts/trigger_coverage.ts +122 -3
  423. package/src/scripts/update_counts.ts +7 -3
  424. package/src/scripts/validate_frontmatter.ts +66 -0
  425. package/src/server/schemas/settings.ts +5 -0
  426. package/src/shared/skillRanking.ts +183 -0
@@ -57,6 +57,21 @@ export interface RunCheckpoint {
57
57
  readonly next_step: string | null;
58
58
  /** Commit the tree was on. A resume onto a different commit is normal, and visible. */
59
59
  readonly head: string | null;
60
+ /**
61
+ * The situational-awareness reading this run last took —
62
+ * `contextFingerprint()` over `origin/main` plus every open PR's head SHA.
63
+ *
64
+ * `road-to-roadmap-situational-awareness` § 5.6. Every other field here
65
+ * reports **roadmap** drift; none reported **repository** drift, so a run
66
+ * resumed after a long gap trusted a context reading it never re-took. A
67
+ * disagreement here forces a re-probe before the first step.
68
+ *
69
+ * Optional: a checkpoint written before this field existed, or by a run that
70
+ * could not reach the probe, is still a valid checkpoint. Absent means "not
71
+ * known", never "unchanged" — `verifyCheckpoint` treats a null on either
72
+ * side as unknown, the same way it already treats `head`.
73
+ */
74
+ readonly context_fingerprint?: string | null;
60
75
  readonly written_at: string;
61
76
  }
62
77
 
@@ -109,6 +124,16 @@ export interface BuildOptions {
109
124
  /** Injected so a test does not depend on the ambient git state. */
110
125
  readonly head?: string | null;
111
126
  readonly now?: () => Date;
127
+ /**
128
+ * The run's situational-awareness fingerprint
129
+ * (`roadmap:context --fingerprint`). Injected rather than read here, so this
130
+ * library keeps its no-subprocess property — `readHead` reads `.git`
131
+ * directly for the same reason, and the probe cannot.
132
+ *
133
+ * Omitted → the field is absent, and a resume reports "not known" rather
134
+ * than asserting the repository stood still.
135
+ */
136
+ readonly contextFingerprint?: string | null;
112
137
  }
113
138
 
114
139
  /**
@@ -204,6 +229,12 @@ export function buildCheckpoint(
204
229
  parked_steps: counts.parked,
205
230
  next_step: counts.next,
206
231
  head: opts.head ?? readHead(repoRoot),
232
+ // Absent rather than null when nothing was supplied: a checkpoint that
233
+ // predates this field and one written without a probe are the same
234
+ // state, and both must read as "not known".
235
+ ...(opts.contextFingerprint !== undefined
236
+ ? { context_fingerprint: opts.contextFingerprint }
237
+ : {}),
207
238
  written_at: (opts.now ?? ((): Date => new Date()))().toISOString(),
208
239
  };
209
240
  }
@@ -310,7 +341,16 @@ export interface VerifyResult {
310
341
  * 4, actual 3" tells it a step landed after the checkpoint was written and
311
342
  * which count to trust.
312
343
  */
313
- export function verifyCheckpoint(repoRoot: string, cp: RunCheckpoint): VerifyResult {
344
+ export function verifyCheckpoint(
345
+ repoRoot: string,
346
+ cp: RunCheckpoint,
347
+ /**
348
+ * The fingerprint a fresh `roadmap:context --fingerprint` returned, when the
349
+ * resumed run took one. Omitted → the field reports "not known" rather than
350
+ * asserting the repository stood still.
351
+ */
352
+ currentFingerprint?: string | null,
353
+ ): VerifyResult {
314
354
  let text: string;
315
355
  try {
316
356
  text = fs.readFileSync(roadmapPath(repoRoot, cp.roadmap), 'utf-8');
@@ -349,6 +389,19 @@ export function verifyCheckpoint(repoRoot: string, cp: RunCheckpoint): VerifyRes
349
389
  // than a missing one: it trains the reader to skip the line.
350
390
  agrees: cp.head === null || head === null || cp.head === head,
351
391
  },
392
+ // Repository drift, which no other field here reports. The `actual` is
393
+ // supplied by the caller (the probe is a subprocess and this library
394
+ // stays I/O-light) — absent means the caller did not re-probe, and an
395
+ // unknown on either side is never a disagreement, exactly as for `head`.
396
+ {
397
+ field: 'context_fingerprint',
398
+ claimed: cp.context_fingerprint ?? null,
399
+ actual: currentFingerprint ?? null,
400
+ agrees:
401
+ (cp.context_fingerprint ?? null) === null ||
402
+ (currentFingerprint ?? null) === null ||
403
+ cp.context_fingerprint === currentFingerprint,
404
+ },
352
405
  ];
353
406
  return { readable: true, fields, agrees: fields.every((f) => f.agrees) };
354
407
  }
@@ -260,3 +260,99 @@ export function scoped_projection_stats(
260
260
  workspaces: [...SCOPED_ACTIVE_WORKSPACES].sort(),
261
261
  };
262
262
  }
263
+
264
+ /** The installer's deploy tuple: [written, skipped, status, paths]. */
265
+ export type DeployTuple = [number, number, string, string[]];
266
+
267
+ // --- Deploy-tree prune mechanics ------------------------------------------
268
+ // Moved here WHOLE from `install.ts` (a pure move, names forwarded there) so
269
+ // the prune sits beside `is_pruned_under_scoped`, the predicate it consumes —
270
+ // which is what this module's header says it exists for. It also pays for the
271
+ // `check_source_size_budget` ratchet, which counts every line in a file above
272
+ // 1,500 and which `install.ts` at 5,466 lines cannot grow past.
273
+
274
+ /**
275
+ * Remove skills/commands matching `is_pruned` from a completed deploy.
276
+ *
277
+ * road-to-install-contract-stability Phase 2 Step 2 (generalized in
278
+ * road-to-credible-install Phase 2 to back both the core-only lab prune and
279
+ * the scoped-projection pack prune off the same mechanics). Skills are
280
+ * pruned by whole directory (tier decided by the skill's `SKILL.md`
281
+ * frontmatter); commands by file (own frontmatter). Rules / personas /
282
+ * contexts / templates are core/shared and left intact. Returns
283
+ * `[pruned_count, adjusted_results]` with the pruned paths removed from each
284
+ * tool's `written_paths` so the manifest never records them.
285
+ */
286
+ export function prune_modules_by(
287
+ deploy_results: Record<string, DeployTuple>,
288
+ is_pruned: (md_path: string) => boolean,
289
+ ): [number, Record<string, DeployTuple>] {
290
+ let pruned = 0;
291
+ const adjusted: Record<string, DeployTuple> = {};
292
+ for (const tool_id of Object.keys(deploy_results)) {
293
+ const [written, skipped, status, paths] = deploy_results[tool_id] as DeployTuple;
294
+ const pruned_skill_dirs = new Set<string>();
295
+ for (const p of paths) {
296
+ const parts = p.split(path.sep);
297
+ if (parts.includes('skills')) {
298
+ const i = parts.indexOf('skills');
299
+ if (i + 1 < parts.length) {
300
+ const skill_root = parts.slice(0, i + 2).join(path.sep);
301
+ if (!pruned_skill_dirs.has(skill_root)) {
302
+ const skillmd = path.join(skill_root, 'SKILL.md');
303
+ if (fs.existsSync(skillmd) && is_pruned(skillmd)) {
304
+ pruned_skill_dirs.add(skill_root);
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ const keep: string[] = [];
311
+ const delete_files: string[] = [];
312
+ for (const p of paths) {
313
+ const parts = p.split(path.sep);
314
+ let is_target = false;
315
+ if (parts.includes('skills')) {
316
+ const i = parts.indexOf('skills');
317
+ if (i + 1 < parts.length && pruned_skill_dirs.has(parts.slice(0, i + 2).join(path.sep))) {
318
+ is_target = true;
319
+ }
320
+ } else if (
321
+ parts.includes('commands') &&
322
+ path.extname(p) === '.md' &&
323
+ is_pruned(p)
324
+ ) {
325
+ is_target = true;
326
+ }
327
+ (is_target ? delete_files : keep).push(p);
328
+ }
329
+ for (const d of pruned_skill_dirs) {
330
+ fs.rmSync(d, { recursive: true, force: true });
331
+ }
332
+ for (const p of delete_files) {
333
+ if (p.split(path.sep).includes('commands') && fs.existsSync(p)) {
334
+ try {
335
+ fs.unlinkSync(p);
336
+ } catch {
337
+ // OSError → swallow, mirroring the .py.
338
+ }
339
+ }
340
+ }
341
+ pruned += delete_files.length;
342
+ adjusted[tool_id] = [Math.max(0, written - delete_files.length), skipped, status, keep];
343
+ }
344
+ return [pruned, adjusted];
345
+ }
346
+
347
+ /**
348
+ * Remove lab-tier skills/commands from a completed deploy (core-only).
349
+ *
350
+ * road-to-install-contract-stability Phase 2 Step 2. Thin wrapper over
351
+ * `_prune_modules_by` — see that function for the mechanics.
352
+ */
353
+ export function prune_lab_modules(
354
+ deploy_results: Record<string, DeployTuple>,
355
+ lab_ids: Set<string>,
356
+ ): [number, Record<string, DeployTuple>] {
357
+ return prune_modules_by(deploy_results, (p) => surface_tiers.is_lab_artefact(p, lab_ids));
358
+ }
@@ -226,6 +226,22 @@ export interface SessionRecord {
226
226
  * session that was never refused, is still a valid record.
227
227
  */
228
228
  turn_end_refusals?: Record<string, number>;
229
+ /**
230
+ * Repo-relative paths this session has declared it owns, or absent when it
231
+ * has declared none.
232
+ *
233
+ * `road-to-roadmap-situational-awareness` § 3.1. The register compared slugs
234
+ * and branch names and nothing else, so two sessions on two different
235
+ * roadmaps that happen to edit the same file were invisible to each other —
236
+ * the axis that actually predicts a merge conflict was the one axis nobody
237
+ * carried.
238
+ *
239
+ * Additive on exactly the terms `turn_end_refusals` established: a session
240
+ * that declared no paths leaves the record **byte-identical** to what it was
241
+ * before this field existed, so a reader that does not know the field is
242
+ * unaffected and a diff of two records still shows only what changed.
243
+ */
244
+ owned_paths?: string[];
229
245
  }
230
246
 
231
247
  /**
@@ -430,11 +446,21 @@ export function foreign_live_records(
430
446
  * so the branch comparison was silent by construction while a whole
431
447
  * implementation was duplicated. One of the two PRs is thrown away either way.
432
448
  */
433
- export type CollisionKind = 'roadmap' | 'branch';
449
+ /**
450
+ * `path` is the third axis, added last and cheapest to be wrong about: two
451
+ * sessions can be on different roadmaps and different branches and still be
452
+ * editing one file. It is reported SEPARATELY rather than folded into either of
453
+ * the two above, because the response differs — a roadmap collision means stop,
454
+ * a branch collision means coordinate, a path collision means take the disjoint
455
+ * steps first and say so.
456
+ */
457
+ export type CollisionKind = 'roadmap' | 'branch' | 'path';
434
458
 
435
459
  export interface Collision {
436
460
  kind: CollisionKind;
437
461
  record: SessionRecord;
462
+ /** For `kind: 'path'`, the shared paths. Absent on the other two kinds. */
463
+ paths?: string[];
438
464
  }
439
465
 
440
466
  /**
@@ -451,7 +477,11 @@ export interface Collision {
451
477
  */
452
478
  export function classify_collisions(
453
479
  others: readonly SessionRecord[],
454
- here: { branch: string | null; roadmap_slug: string | null },
480
+ here: {
481
+ branch: string | null;
482
+ roadmap_slug: string | null;
483
+ owned_paths?: readonly string[];
484
+ },
455
485
  ): Collision[] {
456
486
  const out: Collision[] = [];
457
487
  for (const r of others) {
@@ -468,5 +498,17 @@ export function classify_collisions(
468
498
  out.push({ kind: 'branch', record: r });
469
499
  }
470
500
  }
501
+ // Last, and last on purpose: the two axes above are unchanged in meaning and
502
+ // in order, so a consumer that filters on `roadmap` or `branch` sees exactly
503
+ // what it saw before this axis existed.
504
+ const mine = new Set(here.owned_paths ?? []);
505
+ if (mine.size > 0) {
506
+ for (const r of others) {
507
+ const shared = (r.owned_paths ?? []).filter((p) => mine.has(p)).sort();
508
+ if (shared.length > 0) {
509
+ out.push({ kind: 'path', record: r, paths: shared });
510
+ }
511
+ }
512
+ }
471
513
  return out;
472
514
  }
@@ -730,6 +730,17 @@ export interface ProjectionModeCounts {
730
730
  prunedUnderScoped: number;
731
731
  /** Pack ids active by default under `scoped`, sorted. */
732
732
  activePacks: string[];
733
+ /**
734
+ * Skills a `projection.mode: tiered` install deploys natively — those
735
+ * predicted to reach the model WITH their description. `null` when no tier
736
+ * split exists on this machine, which is the common case and is NOT zero.
737
+ */
738
+ tierACount: number | null;
739
+ /**
740
+ * Skills `tiered` withholds from the native catalogue; still served by the
741
+ * MCP recovery tools. `null` under the same condition as `tierACount`.
742
+ */
743
+ tierBCount: number | null;
733
744
  }
734
745
 
735
746
  /**
@@ -791,8 +802,26 @@ export function formatProjectionModes(
791
802
  ` legacy-all: ${counts.legacyAll}`,
792
803
  ` pruned under scoped: ${counts.prunedUnderScoped}`,
793
804
  ` active packs under scoped: ${counts.activePacks.length}`,
794
- '',
795
805
  ];
806
+ if (counts.tierACount === null || counts.tierBCount === null) {
807
+ lines.push(
808
+ ' tiered: no split on this machine (agents/runtime/state/skill-tiers.json',
809
+ ' absent) — not zero, unmeasured. `tiered` ships the full surface',
810
+ ' until a split exists.',
811
+ );
812
+ } else {
813
+ lines.push(
814
+ ` tiered: tier A ${counts.tierACount} native + tier B ${counts.tierBCount} ` +
815
+ `MCP-only = ${counts.tierACount + counts.tierBCount}`,
816
+ );
817
+ if (counts.tierACount + counts.tierBCount !== counts.legacyAll) {
818
+ lines.push(
819
+ ` ⚠️ tier A + tier B (${counts.tierACount + counts.tierBCount}) != ` +
820
+ `legacy-all (${counts.legacyAll}) — the split is STALE, recompute it.`,
821
+ );
822
+ }
823
+ }
824
+ lines.push('');
796
825
  if (rows.length === 0) {
797
826
  lines.push(
798
827
  'No host root measured. Pass --host-root <dir> (repeatable) to read what',
@@ -959,6 +988,7 @@ export interface MigrationEligibility {
959
988
  | 'observation-not-comparable'
960
989
  | 'no-truncation-observed'
961
990
  | 'already-scoped'
991
+ | 'already-tiered'
962
992
  | 'below-observed-skill-volume';
963
993
  /** Populated only when eligible, for the message. */
964
994
  droppedEntries?: number;
@@ -981,11 +1011,17 @@ export interface MigrationEligibility {
981
1011
  */
982
1012
  export function migrationEligibility(
983
1013
  host: string,
984
- resolvedMode: 'scoped' | 'legacy-all',
1014
+ resolvedMode: 'scoped' | 'legacy-all' | 'tiered',
985
1015
  currentSkillCount: number,
986
1016
  limits: ReadonlyMap<string, KnownHostLimit>,
987
1017
  ): MigrationEligibility {
988
1018
  if (resolvedMode === 'scoped') return { eligible: false, reason: 'already-scoped' };
1019
+ // `tiered` already withholds skills from the native catalogue, so an offer to
1020
+ // migrate to `scoped` has nothing to buy. It gets its OWN reason rather than
1021
+ // reusing `already-scoped`: the two narrow on different axes (packs vs the
1022
+ // host's listing budget), and a caller reading the reason should be able to
1023
+ // tell which one the install is actually on.
1024
+ if (resolvedMode === 'tiered') return { eligible: false, reason: 'already-tiered' };
989
1025
  const limit = limits.get(host);
990
1026
  if (limit === undefined) return { eligible: false, reason: 'no-observation-for-host' };
991
1027
  if (limit.projectedSkills === null) return { eligible: false, reason: 'observation-not-comparable' };
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Re-read measurement over Claude Code transcript legs.
3
+ *
4
+ * `road-to-role-scoped-spawn-profiles` Phase 3 Step 3 requires a re-read
5
+ * measurement BEFORE any suppression: *"A suppression built without this
6
+ * measurement is suppressing whatever the author happened to notice."* This
7
+ * module is that measurement, and it is deliberately a sibling of
8
+ * `cc_transcript.ts` rather than a widening of it — `scanTranscripts` keeps
9
+ * only `type: "assistant"` records carrying a `usage` block, which is exactly
10
+ * the population a `tool_result` is NOT in.
11
+ *
12
+ * **What a "leg" is, stated because every count below depends on it.** One
13
+ * `.jsonl` file is one leg. Claude Code writes the main session to
14
+ * `<project>/<sessionId>.jsonl` and each subagent to
15
+ * `<project>/<sessionId>/subagents/agent-*.jsonl`, so a leg is a single
16
+ * conversation whose context window is its own. A file read once in the main
17
+ * leg and once inside a subagent is NOT a re-read — the second leg never had
18
+ * the first one's context — and {@link computeRereads} counts per leg for that
19
+ * reason.
20
+ *
21
+ * **The wasted-token figure is a `chars / 4` PROXY, not a measurement.** The
22
+ * transcript records no per-tool-result token count, so the size of a
23
+ * duplicate read is estimated from the JSON-encoded length of its
24
+ * `tool_result` body. Every caller must label it as a proxy; this module names
25
+ * the field `wasted_tokens_proxy` so the label cannot be dropped by accident.
26
+ *
27
+ * Read-only: no writes, no network, no spawn.
28
+ */
29
+
30
+ import * as fs from 'node:fs';
31
+
32
+ /**
33
+ * Tools whose result is a file body the model already holds after the first
34
+ * call. Deliberately narrow: `Edit` and `Write` also carry a `file_path`, but
35
+ * re-reading after a mutation is correct behaviour, not waste, so counting
36
+ * them would manufacture "re-reads" out of the loop this measurement exists to
37
+ * distinguish from.
38
+ */
39
+ export const READ_SHAPED_TOOLS: ReadonlySet<string> = new Set(['Read', 'NotebookRead']);
40
+
41
+ /** One observed read-shaped tool call inside one leg. */
42
+ export interface ReadEvent {
43
+ filePath: string;
44
+ /** JSON-encoded length of the matching `tool_result` body; 0 when no result was recorded. */
45
+ resultChars: number;
46
+ }
47
+
48
+ /** Per-path rollup across every leg that read it more than once. */
49
+ export interface RereadFile {
50
+ file_path: string;
51
+ /** Total read-shaped calls on this path, across all legs. */
52
+ total_reads: number;
53
+ /** Reads beyond the first WITHIN a leg, summed over legs. */
54
+ duplicate_reads: number;
55
+ /** Number of legs in which this path was read at least twice. */
56
+ legs_with_reread: number;
57
+ /** `chars / 4` proxy over the duplicate reads only — never a measurement. */
58
+ wasted_tokens_proxy: number;
59
+ }
60
+
61
+ export interface RereadResult {
62
+ legs_scanned: number;
63
+ total_reads: number;
64
+ duplicate_reads: number;
65
+ wasted_tokens_proxy: number;
66
+ /** Only paths with at least one duplicate, ranked by `wasted_tokens_proxy` desc. */
67
+ files: RereadFile[];
68
+ }
69
+
70
+ function parseLine(line: string): Record<string, unknown> | null {
71
+ const t = line.trim();
72
+ if (t.length === 0) return null;
73
+ try {
74
+ const v = JSON.parse(t) as unknown;
75
+ return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ function contentBlocks(rec: Record<string, unknown>): Array<Record<string, unknown>> {
82
+ const msg = rec.message;
83
+ if (!msg || typeof msg !== 'object' || Array.isArray(msg)) return [];
84
+ const content = (msg as Record<string, unknown>).content;
85
+ if (!Array.isArray(content)) return [];
86
+ return content.filter((c): c is Record<string, unknown> => !!c && typeof c === 'object' && !Array.isArray(c));
87
+ }
88
+
89
+ function readPathOf(input: unknown): string | null {
90
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return null;
91
+ const o = input as Record<string, unknown>;
92
+ for (const key of ['file_path', 'filePath', 'notebook_path', 'notebookPath']) {
93
+ const v = o[key];
94
+ if (typeof v === 'string' && v.length > 0) return v;
95
+ }
96
+ return null;
97
+ }
98
+
99
+ /**
100
+ * Every read-shaped call in one leg, in call order, each paired with the size
101
+ * of its result.
102
+ *
103
+ * Two passes on purpose: a `tool_result` can only be matched by
104
+ * `tool_use_id`, and nothing guarantees it appears after its `tool_use` in
105
+ * file order once a transcript has been resumed or replayed. An unreadable or
106
+ * malformed file yields `[]` — this is instrumentation, and a measurement that
107
+ * throws on one bad leg reports nothing about the other two thousand.
108
+ */
109
+ export function collectReadEvents(file: string): ReadEvent[] {
110
+ let text: string;
111
+ try {
112
+ text = fs.readFileSync(file, 'utf-8');
113
+ } catch {
114
+ return [];
115
+ }
116
+
117
+ const order: Array<{ id: string | null; filePath: string }> = [];
118
+ const sizes = new Map<string, number>();
119
+
120
+ for (const line of text.split('\n')) {
121
+ const rec = parseLine(line);
122
+ if (!rec) continue;
123
+ for (const block of contentBlocks(rec)) {
124
+ if (block.type === 'tool_use') {
125
+ if (typeof block.name !== 'string' || !READ_SHAPED_TOOLS.has(block.name)) continue;
126
+ const filePath = readPathOf(block.input);
127
+ if (filePath === null) continue;
128
+ order.push({ id: typeof block.id === 'string' ? block.id : null, filePath });
129
+ } else if (block.type === 'tool_result') {
130
+ const id = block.tool_use_id ?? block.toolUseId;
131
+ if (typeof id !== 'string') continue;
132
+ let chars = 0;
133
+ try {
134
+ chars = JSON.stringify(block.content ?? '').length;
135
+ } catch {
136
+ chars = 0;
137
+ }
138
+ sizes.set(id, chars);
139
+ }
140
+ }
141
+ }
142
+
143
+ return order.map((o) => ({
144
+ filePath: o.filePath,
145
+ resultChars: o.id === null ? 0 : (sizes.get(o.id) ?? 0),
146
+ }));
147
+ }
148
+
149
+ const CHARS_PER_TOKEN_PROXY = 4;
150
+
151
+ /**
152
+ * Roll every leg up into a per-path re-read ranking.
153
+ *
154
+ * The duplicate count is per leg: the first read of a path in a leg is the
155
+ * cost of knowing it, every later read in the SAME leg is the waste. Legs are
156
+ * never joined, so a path read once in each of two legs contributes zero
157
+ * duplicates — see the module doc.
158
+ */
159
+ export function computeRereads(files: readonly string[]): RereadResult {
160
+ const acc = new Map<string, RereadFile>();
161
+ let legsScanned = 0;
162
+ let totalReads = 0;
163
+
164
+ for (const file of files) {
165
+ const events = collectReadEvents(file);
166
+ legsScanned += 1;
167
+ if (events.length === 0) continue;
168
+
169
+ const seenInLeg = new Map<string, number>();
170
+ for (const ev of events) {
171
+ totalReads += 1;
172
+ const priorInLeg = seenInLeg.get(ev.filePath) ?? 0;
173
+ seenInLeg.set(ev.filePath, priorInLeg + 1);
174
+
175
+ const row = acc.get(ev.filePath) ?? {
176
+ file_path: ev.filePath,
177
+ total_reads: 0,
178
+ duplicate_reads: 0,
179
+ legs_with_reread: 0,
180
+ wasted_tokens_proxy: 0,
181
+ };
182
+ row.total_reads += 1;
183
+ if (priorInLeg >= 1) {
184
+ row.duplicate_reads += 1;
185
+ row.wasted_tokens_proxy += Math.round(ev.resultChars / CHARS_PER_TOKEN_PROXY);
186
+ if (priorInLeg === 1) row.legs_with_reread += 1;
187
+ }
188
+ acc.set(ev.filePath, row);
189
+ }
190
+ }
191
+
192
+ const withDuplicates = [...acc.values()].filter((r) => r.duplicate_reads > 0);
193
+ withDuplicates.sort(
194
+ (a, b) =>
195
+ b.wasted_tokens_proxy - a.wasted_tokens_proxy ||
196
+ b.duplicate_reads - a.duplicate_reads ||
197
+ a.file_path.localeCompare(b.file_path),
198
+ );
199
+
200
+ return {
201
+ legs_scanned: legsScanned,
202
+ total_reads: totalReads,
203
+ duplicate_reads: withDuplicates.reduce((s, r) => s + r.duplicate_reads, 0),
204
+ wasted_tokens_proxy: withDuplicates.reduce((s, r) => s + r.wasted_tokens_proxy, 0),
205
+ files: withDuplicates,
206
+ };
207
+ }