@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -1,8 +1,15 @@
1
1
  import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
2
  import {
3
- DeadScopeError,
4
- assertScanned
5
- } from "./chunk-I3EIN2YG.js";
3
+ LexicalIndex,
4
+ loadSqliteSync,
5
+ probeFts5Sync,
6
+ readUserVersion,
7
+ stampUserVersion,
8
+ tokenize
9
+ } from "./chunk-ZBZV2G5R.js";
10
+ import {
11
+ _classify
12
+ } from "./chunk-JQOM7TTM.js";
6
13
  import {
7
14
  require_dist
8
15
  } from "./chunk-OY53J6II.js";
@@ -12,1102 +19,11 @@ import {
12
19
 
13
20
  // src/scripts/memory_lookup.ts
14
21
  var import_yaml2 = __toESM(require_dist(), 1);
15
- import * as fs7 from "node:fs";
16
- import * as path7 from "node:path";
22
+ import * as fs5 from "node:fs";
23
+ import * as path5 from "node:path";
17
24
  import process5 from "node:process";
18
- import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL4 } from "node:url";
19
- import { createRequire as createRequire2 } from "node:module";
20
-
21
- // src/scripts/lint_hidden_unicode.ts
22
- import * as fs2 from "node:fs";
23
- import * as path2 from "node:path";
24
- import { spawnSync } from "node:child_process";
25
- import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
26
-
27
- // src/scripts/_lib/security_lint.ts
28
- import { createHash } from "node:crypto";
29
- import * as fs from "node:fs";
30
- import * as path from "node:path";
31
- import { fileURLToPath } from "node:url";
32
- var _HERE = path.dirname(fileURLToPath(import.meta.url));
33
- var ROOT = path.resolve(_HERE, "..", "..", "..");
34
- var EXAMPLE_FENCE_LANG = "security-example";
35
- var GUIDELINE = "docs/guidelines/agent-infra/security-lint-containment.md";
36
- var GUIDELINE_EPILOG = `False-positive containment (fenced security-example block, confidence weighting, per-file \`security-lint: allow\` pragma \u2014 no global allowlist): see ${GUIDELINE}.`;
37
- var DEFAULT_SCAN_ROOTS = [
38
- "src/skills",
39
- "src/rules",
40
- "src/agent-src",
41
- "src/domains",
42
- "dist/agent-src"
43
- ];
44
- var _EXAMPLE_PATH = /(^|\/)(docs|evals|tests?|fixtures?)(\/|$)|example|template|sample|\/_template/i;
45
- var _PRAGMA = /<!--\s*security-lint:\s*allow\s+([A-Za-z0-9_-]+)\s+"([^"]+)"((?:\s+sha256:[0-9a-f]{64})*)\s*-->/g;
46
- var _FENCE = /^(\s*)(`{3,}|~{3,})\s*([A-Za-z0-9_-]*)\s*$/;
47
- var SEVERITY_RANK = { LOW: 1, MED: 2, HIGH: 3 };
48
- var Finding = class {
49
- path;
50
- // repo-relative
51
- line;
52
- // 1-based; 0 = file-level
53
- check;
54
- // stable check id, also the pragma key
55
- severity;
56
- // HIGH | MED | LOW
57
- message;
58
- weight;
59
- // 1.0 or 0.25
60
- constructor(pathArg, line, check, severity, message, weight = 1) {
61
- this.path = pathArg;
62
- this.line = line;
63
- this.check = check;
64
- this.severity = severity;
65
- this.message = message;
66
- this.weight = weight;
67
- }
68
- /** A HIGH-severity, full-weight finding fails the build. */
69
- get is_fail() {
70
- return this.severity === "HIGH" && this.weight >= 1;
71
- }
72
- /** Mirror Python dataclass `__dict__` key order for `--json` parity. */
73
- toDict() {
74
- return {
75
- path: this.path,
76
- line: this.line,
77
- check: this.check,
78
- severity: this.severity,
79
- message: this.message,
80
- // float in Python → render integer-valued (1.0) as `1.0`.
81
- weight: new PyFloat(this.weight)
82
- };
83
- }
84
- };
85
- function is_example_path(rel_path) {
86
- return _EXAMPLE_PATH.test(rel_path);
87
- }
88
- function path_weight(rel_path) {
89
- return is_example_path(rel_path) ? 0.25 : 1;
90
- }
91
- var PROJECTION_PREFIX = "dist/agent-src/";
92
- var PROJECTION_SOURCE_LANES = [
93
- // The guidelines lane copies `docs/guidelines/` (`_lib/guidelines_lane.ts`),
94
- // not `src/guidelines/`, which does not exist. Without this row a projected
95
- // guideline gets a DIFFERENT identity from its own source, so a bound
96
- // pragma written in the source cannot suppress the projection's copy of the
97
- // very same line — measured on
98
- // `untrusted-input-spotlighting.md`, a defense guideline that quotes
99
- // role-takeover phrases to teach refusal and carries three accepted
100
- // fingerprints for exactly that.
101
- ["guidelines/", "docs/guidelines/"]
102
- ];
103
- function source_identity(rel) {
104
- if (!rel.startsWith(PROJECTION_PREFIX)) return rel;
105
- const tail = rel.slice(PROJECTION_PREFIX.length);
106
- for (const [lane, sourceRoot] of PROJECTION_SOURCE_LANES) {
107
- if (tail.startsWith(lane)) return sourceRoot + tail.slice(lane.length);
108
- }
109
- return `src/${tail}`;
110
- }
111
- var ScannedFile = class {
112
- path;
113
- // absolute or as-given path
114
- rel;
115
- lines;
116
- // per-line flags (1-based index → flag); index 0 unused
117
- in_example_fence;
118
- in_any_fence;
119
- pragmas;
120
- // check id → reason
121
- /** check id → the sha256 fingerprints that pragma accepts. Empty = unbound. */
122
- pragma_hashes;
123
- weight;
124
- constructor(pathArg, rel, lines, in_example_fence, in_any_fence, pragmas, weight, pragma_hashes = {}) {
125
- this.path = pathArg;
126
- this.rel = rel;
127
- this.lines = lines;
128
- this.in_example_fence = in_example_fence;
129
- this.in_any_fence = in_any_fence;
130
- this.pragmas = pragmas;
131
- this.weight = weight;
132
- this.pragma_hashes = pragma_hashes;
133
- }
134
- pragma_allows(check) {
135
- return Object.prototype.hasOwnProperty.call(this.pragmas, check);
136
- }
137
- /**
138
- * Which form of pragma this file carries for `check`.
139
- *
140
- * `absent` — no pragma; scan normally.
141
- * `legacy` — a pragma with no fingerprint. Suppresses the whole file, as it
142
- * always did, and says so: a key lookup accepts whatever the
143
- * line is later changed to say, which is the defect being paid
144
- * down, not a mode anybody should choose.
145
- * `bound` — a pragma listing fingerprints. Suppresses exactly the matches
146
- * whose evidence hashes to one of them; anything else survives.
147
- */
148
- pragma_form(check) {
149
- if (!this.pragma_allows(check)) return "absent";
150
- return (this.pragma_hashes[check] ?? []).length > 0 ? "bound" : "legacy";
151
- }
152
- /**
153
- * The fingerprint one finding's evidence hashes to.
154
- *
155
- * WHAT IS HASHED, and each part earns its place:
156
- * check id — the same line may legitimately be accepted for one check
157
- * and not another.
158
- * rel path — the location IDENTITY. Not the line NUMBER: an unrelated
159
- * insert above would otherwise break every pragma in the
160
- * file, and a suppression that breaks on unrelated edits
161
- * gets deleted rather than re-derived. A `dist/agent-src/`
162
- * path folds to its `src/` original first, because the
163
- * projection is byte-exact by contract (ADR-201) and
164
- * carries the pragma verbatim — without the fold, every
165
- * migrated pragma would suppress the source and leave its
166
- * own copy flagged, which is a gate that reds on a file
167
- * nobody may edit.
168
- * matched line, — the evidence. ASCII whitespace runs collapse so a reflow
169
- * normalized does not invalidate the binding, `sha256:<hex>` tokens are
170
- * removed, and NOTHING else is stripped: the zero-width
171
- * characters this suite hunts are exactly what a wider
172
- * normalizer would erase.
173
- *
174
- * WHY THE HASH TOKENS COME OUT. A pragma's own reason string can be the
175
- * matched evidence — two in this tree are, because they quote the very
176
- * phrases their check detects in order to explain themselves. Without this
177
- * the fingerprint would have to be computed over a line that already
178
- * contains it, which has no fixed point: writing the hash changes the line,
179
- * which changes the hash. Stripping the tokens makes the fingerprint stable
180
- * under the act of recording it, and costs nothing elsewhere — a `sha256:`
181
- * token in ordinary prose is not evidence of anything a linter here checks.
182
- *
183
- * Deliberately NOT the whole file: incidental content elsewhere would make
184
- * every unrelated edit re-fire the pragma, which is the noise that produces
185
- * blanket suppressions.
186
- */
187
- evidence_fingerprint(finding) {
188
- const text = finding.line > 0 ? this.lines[finding.line - 1] ?? "" : "";
189
- const normalized = text.replace(/\s*sha256:[0-9a-f]{64}/g, "").replace(/[ \t]+/g, " ").trim();
190
- return createHash("sha256").update(`${finding.check}
191
- ${source_identity(this.rel)}
192
- ${normalized}`, "utf-8").digest("hex");
193
- }
194
- /** Drop the findings this file's BOUND pragma for `check` accepts. */
195
- filter_bound_pragma(check, findings) {
196
- const accepted = new Set(this.pragma_hashes[check] ?? []);
197
- if (accepted.size === 0) return [...findings];
198
- return findings.filter((f) => !accepted.has(this.evidence_fingerprint(f)));
199
- }
200
- /** Yield [lineno, text] honouring the fence masks (mirrors iter_lines). */
201
- *iter_lines(opts = {}) {
202
- const skip_example_fence = opts.skip_example_fence ?? true;
203
- const skip_any_fence = opts.skip_any_fence ?? false;
204
- for (let i = 1; i <= this.lines.length; i++) {
205
- if (skip_example_fence && this.in_example_fence[i]) {
206
- continue;
207
- }
208
- if (skip_any_fence && this.in_any_fence[i]) {
209
- continue;
210
- }
211
- yield [i, this.lines[i - 1]];
212
- }
213
- }
214
- };
215
- function _splitlines(text) {
216
- const out = [];
217
- let buf = "";
218
- const n = text.length;
219
- let i = 0;
220
- while (i < n) {
221
- const ch = text[i];
222
- const code = text.charCodeAt(i);
223
- if (code === 13) {
224
- out.push(buf);
225
- buf = "";
226
- if (i + 1 < n && text.charCodeAt(i + 1) === 10) {
227
- i += 2;
228
- } else {
229
- i += 1;
230
- }
231
- continue;
232
- }
233
- if (code === 10 || code === 11 || code === 12 || code === 28 || code === 29 || code === 30 || code === 133 || code === 8232 || code === 8233) {
234
- out.push(buf);
235
- buf = "";
236
- i += 1;
237
- continue;
238
- }
239
- buf += ch;
240
- i += 1;
241
- }
242
- if (buf !== "") {
243
- out.push(buf);
244
- }
245
- return out;
246
- }
247
- function scan_file(filePath) {
248
- let rel;
249
- if (path.isAbsolute(filePath)) {
250
- const root = path.resolve(ROOT);
251
- const resolved = path.resolve(filePath);
252
- if (resolved === root || resolved.startsWith(root + path.sep)) {
253
- rel = path.relative(root, resolved).split(path.sep).join("/");
254
- } else {
255
- rel = path.basename(filePath);
256
- }
257
- } else {
258
- rel = filePath.split(path.sep).join("/");
259
- }
260
- const raw = fs.readFileSync(filePath, "utf-8");
261
- const lines = _splitlines(raw);
262
- const n = lines.length;
263
- const in_example = new Array(n + 1).fill(false);
264
- const in_any = new Array(n + 1).fill(false);
265
- let fence_open = false;
266
- let fence_marker = "";
267
- let fence_is_example = false;
268
- for (let idx = 1; idx <= n; idx++) {
269
- const text = lines[idx - 1];
270
- const m = _FENCE.exec(text);
271
- if (m && !fence_open) {
272
- fence_open = true;
273
- fence_marker = m[2][0];
274
- fence_is_example = m[3] === EXAMPLE_FENCE_LANG;
275
- in_any[idx] = true;
276
- in_example[idx] = fence_is_example;
277
- continue;
278
- }
279
- if (fence_open) {
280
- in_any[idx] = true;
281
- in_example[idx] = fence_is_example;
282
- const cm = _FENCE.exec(text);
283
- if (cm && cm[2][0] === fence_marker && cm[3] === "") {
284
- fence_open = false;
285
- fence_is_example = false;
286
- }
287
- }
288
- }
289
- const pragmas = {};
290
- const pragmaHashes = {};
291
- for (const text of lines) {
292
- _PRAGMA.lastIndex = 0;
293
- let m;
294
- while ((m = _PRAGMA.exec(text)) !== null) {
295
- const check = m[1];
296
- pragmas[check] = m[2];
297
- const hashes = (m[3] ?? "").match(/sha256:([0-9a-f]{64})/g) ?? [];
298
- pragmaHashes[check] = [
299
- ...pragmaHashes[check] ?? [],
300
- ...hashes.map((h) => h.slice("sha256:".length))
301
- ];
302
- if (m.index === _PRAGMA.lastIndex) {
303
- _PRAGMA.lastIndex += 1;
304
- }
305
- }
306
- }
307
- return new ScannedFile(
308
- filePath,
309
- rel,
310
- lines,
311
- in_example,
312
- in_any,
313
- pragmas,
314
- path_weight(rel),
315
- pragmaHashes
316
- );
317
- }
318
- function scan_path(filePath, base) {
319
- const sf = scan_file(filePath);
320
- let rel;
321
- const baseResolved = path.resolve(base);
322
- const fileResolved = path.resolve(filePath);
323
- if (fileResolved === baseResolved || fileResolved.startsWith(baseResolved + path.sep)) {
324
- rel = path.relative(baseResolved, fileResolved).split(path.sep).join("/");
325
- } else {
326
- rel = path.basename(filePath);
327
- }
328
- return new ScannedFile(
329
- sf.path,
330
- rel,
331
- sf.lines,
332
- sf.in_example_fence,
333
- sf.in_any_fence,
334
- sf.pragmas,
335
- path_weight(rel),
336
- sf.pragma_hashes
337
- );
338
- }
339
- function _rglobSorted(base) {
340
- const found = [];
341
- const walk = (dir) => {
342
- let entries;
343
- try {
344
- entries = fs.readdirSync(dir, { withFileTypes: true });
345
- } catch {
346
- return;
347
- }
348
- for (const e of entries) {
349
- const full = path.join(dir, e.name);
350
- found.push(full);
351
- if (e.isDirectory()) {
352
- walk(full);
353
- }
354
- }
355
- };
356
- walk(base);
357
- found.sort((a, b) => _pathCmp(a, b));
358
- return found;
359
- }
360
- function _pathCmp(a, b) {
361
- const pa = a.split(path.sep);
362
- const pb = b.split(path.sep);
363
- const len = Math.min(pa.length, pb.length);
364
- for (let i = 0; i < len; i++) {
365
- if (pa[i] < pb[i]) return -1;
366
- if (pa[i] > pb[i]) return 1;
367
- }
368
- if (pa.length < pb.length) return -1;
369
- if (pa.length > pb.length) return 1;
370
- return 0;
371
- }
372
- function* iter_corpus(roots = DEFAULT_SCAN_ROOTS, exts = [".md"], scanBase = ROOT) {
373
- for (const root of roots) {
374
- const base = path.join(scanBase, root);
375
- if (!fs.existsSync(base)) {
376
- continue;
377
- }
378
- for (const p of _rglobSorted(base)) {
379
- let st;
380
- try {
381
- st = fs.statSync(p);
382
- } catch {
383
- continue;
384
- }
385
- if (st.isFile() && exts.includes(path.extname(p))) {
386
- yield scan_path(p, scanBase);
387
- }
388
- }
389
- }
390
- }
391
- var LEGACY_PRAGMA_CHECK = "legacy-pragma";
392
- function legacy_pragma_finding(sf, check) {
393
- return new Finding(
394
- sf.rel,
395
- 0,
396
- LEGACY_PRAGMA_CHECK,
397
- "LOW",
398
- `unbound \`security-lint: allow ${check}\` pragma suppresses this whole file \u2014 it accepts whatever the matched text is later changed to say. Bind it with \`sha256:<hex>\` per accepted match.`,
399
- sf.weight
400
- );
401
- }
402
- var CHILD_SCANNED_PREFIX = "scanned: ";
403
- function report_child_scanned(count) {
404
- process.stderr.write(`${CHILD_SCANNED_PREFIX}${String(count)}
405
- `);
406
- }
407
- function report(findings, opts) {
408
- const check_label = opts.check_label;
409
- if (findings.length === 0) {
410
- process.stdout.write(`\u2705 ${check_label}: clean (${_corpus_note(opts.scanned_roots)}).
411
- `);
412
- return 0;
413
- }
414
- const fails = findings.filter((f) => f.is_fail);
415
- const warns = findings.filter((f) => !f.is_fail);
416
- const sorted = [...findings].sort((a, b) => {
417
- const ra = -(SEVERITY_RANK[a.severity] ?? 0);
418
- const rb = -(SEVERITY_RANK[b.severity] ?? 0);
419
- if (ra !== rb) return ra - rb;
420
- if (a.path < b.path) return -1;
421
- if (a.path > b.path) return 1;
422
- return a.line - b.line;
423
- });
424
- for (const f of sorted) {
425
- const glyph = f.is_fail ? "\u{1F534}" : "\u26A0\uFE0F";
426
- const loc = f.line ? `${f.path}:${f.line}` : f.path;
427
- const wnote = f.weight >= 1 ? "" : ` (weight ${_pyG(f.weight)})`;
428
- process.stdout.write(` ${glyph} [${f.severity}] ${f.check} \u2014 ${loc}${wnote}: ${f.message}
429
- `);
430
- }
431
- process.stdout.write("\n");
432
- if (fails.length > 0) {
433
- process.stdout.write(
434
- `\u274C ${check_label}: ${fails.length} blocking finding(s), ${warns.length} warning(s). Fix, or mark a true teaching example with a \`\`\`${EXAMPLE_FENCE_LANG} fence or a \`security-lint: allow\` pragma.
435
- `
436
- );
437
- return 1;
438
- }
439
- process.stdout.write(`\u26A0\uFE0F ${check_label}: ${warns.length} warning(s), 0 blocking.
440
- `);
441
- return 0;
442
- }
443
- function _pyG(n) {
444
- let s = n.toPrecision(6);
445
- if (s.includes(".")) {
446
- s = s.replace(/0+$/, "").replace(/\.$/, "");
447
- }
448
- return s;
449
- }
450
- function _corpus_note(scanned_roots) {
451
- const roots = scanned_roots && scanned_roots.length > 0 ? scanned_roots : DEFAULT_SCAN_ROOTS;
452
- return "scanned " + roots.join(", ");
453
- }
454
- var PyFloat = class {
455
- constructor(value) {
456
- this.value = value;
457
- }
458
- value;
459
- };
460
- function _isPlainObject(v) {
461
- return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyFloat);
462
- }
463
- function _pyJsonStr(s) {
464
- let out = '"';
465
- for (const ch of s) {
466
- const code = ch.codePointAt(0);
467
- switch (ch) {
468
- case '"':
469
- out += '\\"';
470
- break;
471
- case "\\":
472
- out += "\\\\";
473
- break;
474
- case "\n":
475
- out += "\\n";
476
- break;
477
- case "\r":
478
- out += "\\r";
479
- break;
480
- case " ":
481
- out += "\\t";
482
- break;
483
- case "\b":
484
- out += "\\b";
485
- break;
486
- case "\f":
487
- out += "\\f";
488
- break;
489
- default:
490
- if (code < 32) {
491
- out += `\\u${code.toString(16).padStart(4, "0")}`;
492
- } else {
493
- out += ch;
494
- }
495
- }
496
- }
497
- return out + '"';
498
- }
499
- function _pyJsonFloat(n) {
500
- if (!Number.isFinite(n)) {
501
- if (Number.isNaN(n)) return "NaN";
502
- return n > 0 ? "Infinity" : "-Infinity";
503
- }
504
- return Number.isInteger(n) ? `${n}.0` : String(n);
505
- }
506
- function _pyJsonScalar(value) {
507
- if (value === null || value === void 0) return "null";
508
- if (typeof value === "boolean") return value ? "true" : "false";
509
- if (value instanceof PyFloat) return _pyJsonFloat(value.value);
510
- if (typeof value === "number") {
511
- if (!Number.isFinite(value)) {
512
- if (Number.isNaN(value)) return "NaN";
513
- return value > 0 ? "Infinity" : "-Infinity";
514
- }
515
- return String(value);
516
- }
517
- if (typeof value === "string") return _pyJsonStr(value);
518
- return null;
519
- }
520
- function py_json_dumps_indent2(value, depth = 0) {
521
- return _dumpIndent2(value, depth, true);
522
- }
523
- function _dumpIndent2(value, depth, ensureAscii) {
524
- const scalar = ensureAscii ? _pyJsonScalarAscii(value) : _pyJsonScalar(value);
525
- if (scalar !== null) {
526
- return scalar;
527
- }
528
- const pad = " ".repeat(2 * (depth + 1));
529
- const closePad = " ".repeat(2 * depth);
530
- if (Array.isArray(value)) {
531
- if (value.length === 0) return "[]";
532
- const items = value.map((v) => pad + _dumpIndent2(v, depth + 1, ensureAscii));
533
- return `[
534
- ${items.join(",\n")}
535
- ${closePad}]`;
536
- }
537
- if (_isPlainObject(value)) {
538
- const keys = Object.keys(value);
539
- if (keys.length === 0) return "{}";
540
- const items = keys.map(
541
- (k) => `${pad}${ensureAscii ? _pyJsonStrAscii(k) : _pyJsonStr(k)}: ${_dumpIndent2(value[k], depth + 1, ensureAscii)}`
542
- );
543
- return `{
544
- ${items.join(",\n")}
545
- ${closePad}}`;
546
- }
547
- return ensureAscii ? _pyJsonStrAscii(String(value)) : _pyJsonStr(String(value));
548
- }
549
- function _pyJsonStrAscii(s) {
550
- let out = '"';
551
- for (const ch of s) {
552
- const code = ch.codePointAt(0);
553
- switch (ch) {
554
- case '"':
555
- out += '\\"';
556
- break;
557
- case "\\":
558
- out += "\\\\";
559
- break;
560
- case "\n":
561
- out += "\\n";
562
- break;
563
- case "\r":
564
- out += "\\r";
565
- break;
566
- case " ":
567
- out += "\\t";
568
- break;
569
- case "\b":
570
- out += "\\b";
571
- break;
572
- case "\f":
573
- out += "\\f";
574
- break;
575
- default:
576
- if (code < 32) {
577
- out += `\\u${code.toString(16).padStart(4, "0")}`;
578
- } else if (code < 127) {
579
- out += ch;
580
- } else if (code <= 65535) {
581
- out += `\\u${code.toString(16).padStart(4, "0")}`;
582
- } else {
583
- const cp = code - 65536;
584
- const hi = 55296 + (cp >> 10);
585
- const lo = 56320 + (cp & 1023);
586
- out += `\\u${hi.toString(16).padStart(4, "0")}\\u${lo.toString(16).padStart(4, "0")}`;
587
- }
588
- }
589
- }
590
- return out + '"';
591
- }
592
- function _pyJsonScalarAscii(value) {
593
- if (typeof value === "string") return _pyJsonStrAscii(value);
594
- return _pyJsonScalar(value);
595
- }
596
-
597
- // src/scripts/lint_hidden_unicode.ts
598
- var CHECK = "hidden-unicode";
599
- var _BIDI = /* @__PURE__ */ new Set([
600
- 8234,
601
- 8235,
602
- 8236,
603
- 8237,
604
- 8238,
605
- 8294,
606
- 8295,
607
- 8296,
608
- 8297,
609
- 8206,
610
- 8207,
611
- 1564
612
- ]);
613
- var _ZERO_WIDTH = /* @__PURE__ */ new Set([8203, 8204, 8205, 8288, 65279, 173]);
614
- var _DEPRECATED = /* @__PURE__ */ new Set([
615
- 8298,
616
- 8299,
617
- 8300,
618
- 8301,
619
- 8302,
620
- 8303,
621
- 65529,
622
- 65530,
623
- 65531
624
- ]);
625
- function _classify(cp) {
626
- if (_BIDI.has(cp)) {
627
- return "bidi-control";
628
- }
629
- if (_ZERO_WIDTH.has(cp)) {
630
- return "zero-width";
631
- }
632
- if (cp >= 917504 && cp <= 917631) {
633
- return "unicode-tag";
634
- }
635
- if (_DEPRECATED.has(cp)) {
636
- return "deprecated-format";
637
- }
638
- if (cp >= 57344 && cp <= 63743 || cp >= 983040 && cp <= 1048573 || cp >= 1048576 && cp <= 1114109) {
639
- return "private-use-area";
640
- }
641
- if ((cp >= 0 && cp <= 31 || cp >= 127 && cp <= 159) && cp !== 9 && cp !== 10 && cp !== 13) {
642
- return "control-char";
643
- }
644
- return null;
645
- }
646
- var _VS_LO = 917760;
647
- var _VS_HI = 917999;
648
- function _isVS(cp) {
649
- return cp >= _VS_LO && cp <= _VS_HI;
650
- }
651
- var _CP_NAME = {
652
- 173: "SOFT HYPHEN",
653
- 1564: "ARABIC LETTER MARK",
654
- 8203: "ZERO WIDTH SPACE",
655
- 8204: "ZERO WIDTH NON-JOINER",
656
- 8205: "ZERO WIDTH JOINER",
657
- 8206: "LEFT-TO-RIGHT MARK",
658
- 8207: "RIGHT-TO-LEFT MARK",
659
- 8234: "LEFT-TO-RIGHT EMBEDDING",
660
- 8235: "RIGHT-TO-LEFT EMBEDDING",
661
- 8236: "POP DIRECTIONAL FORMATTING",
662
- 8237: "LEFT-TO-RIGHT OVERRIDE",
663
- 8238: "RIGHT-TO-LEFT OVERRIDE",
664
- 8288: "WORD JOINER",
665
- 8294: "LEFT-TO-RIGHT ISOLATE",
666
- 8295: "RIGHT-TO-LEFT ISOLATE",
667
- 8296: "FIRST STRONG ISOLATE",
668
- 8297: "POP DIRECTIONAL ISOLATE",
669
- 8298: "INHIBIT SYMMETRIC SWAPPING",
670
- 8299: "ACTIVATE SYMMETRIC SWAPPING",
671
- 8300: "INHIBIT ARABIC FORM SHAPING",
672
- 8301: "ACTIVATE ARABIC FORM SHAPING",
673
- 8302: "NATIONAL DIGIT SHAPES",
674
- 8303: "NOMINAL DIGIT SHAPES",
675
- 65279: "ZERO WIDTH NO-BREAK SPACE",
676
- 65529: "INTERLINEAR ANNOTATION ANCHOR",
677
- 65530: "INTERLINEAR ANNOTATION SEPARATOR",
678
- 65531: "INTERLINEAR ANNOTATION TERMINATOR",
679
- 917505: "LANGUAGE TAG",
680
- 917536: "TAG SPACE",
681
- 917537: "TAG EXCLAMATION MARK",
682
- 917538: "TAG QUOTATION MARK",
683
- 917539: "TAG NUMBER SIGN",
684
- 917540: "TAG DOLLAR SIGN",
685
- 917541: "TAG PERCENT SIGN",
686
- 917542: "TAG AMPERSAND",
687
- 917543: "TAG APOSTROPHE",
688
- 917544: "TAG LEFT PARENTHESIS",
689
- 917545: "TAG RIGHT PARENTHESIS",
690
- 917546: "TAG ASTERISK",
691
- 917547: "TAG PLUS SIGN",
692
- 917548: "TAG COMMA",
693
- 917549: "TAG HYPHEN-MINUS",
694
- 917550: "TAG FULL STOP",
695
- 917551: "TAG SOLIDUS",
696
- 917552: "TAG DIGIT ZERO",
697
- 917553: "TAG DIGIT ONE",
698
- 917554: "TAG DIGIT TWO",
699
- 917555: "TAG DIGIT THREE",
700
- 917556: "TAG DIGIT FOUR",
701
- 917557: "TAG DIGIT FIVE",
702
- 917558: "TAG DIGIT SIX",
703
- 917559: "TAG DIGIT SEVEN",
704
- 917560: "TAG DIGIT EIGHT",
705
- 917561: "TAG DIGIT NINE",
706
- 917562: "TAG COLON",
707
- 917563: "TAG SEMICOLON",
708
- 917564: "TAG LESS-THAN SIGN",
709
- 917565: "TAG EQUALS SIGN",
710
- 917566: "TAG GREATER-THAN SIGN",
711
- 917567: "TAG QUESTION MARK",
712
- 917568: "TAG COMMERCIAL AT",
713
- 917569: "TAG LATIN CAPITAL LETTER A",
714
- 917570: "TAG LATIN CAPITAL LETTER B",
715
- 917571: "TAG LATIN CAPITAL LETTER C",
716
- 917572: "TAG LATIN CAPITAL LETTER D",
717
- 917573: "TAG LATIN CAPITAL LETTER E",
718
- 917574: "TAG LATIN CAPITAL LETTER F",
719
- 917575: "TAG LATIN CAPITAL LETTER G",
720
- 917576: "TAG LATIN CAPITAL LETTER H",
721
- 917577: "TAG LATIN CAPITAL LETTER I",
722
- 917578: "TAG LATIN CAPITAL LETTER J",
723
- 917579: "TAG LATIN CAPITAL LETTER K",
724
- 917580: "TAG LATIN CAPITAL LETTER L",
725
- 917581: "TAG LATIN CAPITAL LETTER M",
726
- 917582: "TAG LATIN CAPITAL LETTER N",
727
- 917583: "TAG LATIN CAPITAL LETTER O",
728
- 917584: "TAG LATIN CAPITAL LETTER P",
729
- 917585: "TAG LATIN CAPITAL LETTER Q",
730
- 917586: "TAG LATIN CAPITAL LETTER R",
731
- 917587: "TAG LATIN CAPITAL LETTER S",
732
- 917588: "TAG LATIN CAPITAL LETTER T",
733
- 917589: "TAG LATIN CAPITAL LETTER U",
734
- 917590: "TAG LATIN CAPITAL LETTER V",
735
- 917591: "TAG LATIN CAPITAL LETTER W",
736
- 917592: "TAG LATIN CAPITAL LETTER X",
737
- 917593: "TAG LATIN CAPITAL LETTER Y",
738
- 917594: "TAG LATIN CAPITAL LETTER Z",
739
- 917595: "TAG LEFT SQUARE BRACKET",
740
- 917596: "TAG REVERSE SOLIDUS",
741
- 917597: "TAG RIGHT SQUARE BRACKET",
742
- 917598: "TAG CIRCUMFLEX ACCENT",
743
- 917599: "TAG LOW LINE",
744
- 917600: "TAG GRAVE ACCENT",
745
- 917601: "TAG LATIN SMALL LETTER A",
746
- 917602: "TAG LATIN SMALL LETTER B",
747
- 917603: "TAG LATIN SMALL LETTER C",
748
- 917604: "TAG LATIN SMALL LETTER D",
749
- 917605: "TAG LATIN SMALL LETTER E",
750
- 917606: "TAG LATIN SMALL LETTER F",
751
- 917607: "TAG LATIN SMALL LETTER G",
752
- 917608: "TAG LATIN SMALL LETTER H",
753
- 917609: "TAG LATIN SMALL LETTER I",
754
- 917610: "TAG LATIN SMALL LETTER J",
755
- 917611: "TAG LATIN SMALL LETTER K",
756
- 917612: "TAG LATIN SMALL LETTER L",
757
- 917613: "TAG LATIN SMALL LETTER M",
758
- 917614: "TAG LATIN SMALL LETTER N",
759
- 917615: "TAG LATIN SMALL LETTER O",
760
- 917616: "TAG LATIN SMALL LETTER P",
761
- 917617: "TAG LATIN SMALL LETTER Q",
762
- 917618: "TAG LATIN SMALL LETTER R",
763
- 917619: "TAG LATIN SMALL LETTER S",
764
- 917620: "TAG LATIN SMALL LETTER T",
765
- 917621: "TAG LATIN SMALL LETTER U",
766
- 917622: "TAG LATIN SMALL LETTER V",
767
- 917623: "TAG LATIN SMALL LETTER W",
768
- 917624: "TAG LATIN SMALL LETTER X",
769
- 917625: "TAG LATIN SMALL LETTER Y",
770
- 917626: "TAG LATIN SMALL LETTER Z",
771
- 917627: "TAG LEFT CURLY BRACKET",
772
- 917628: "TAG VERTICAL LINE",
773
- 917629: "TAG RIGHT CURLY BRACKET",
774
- 917630: "TAG TILDE",
775
- 917631: "CANCEL TAG"
776
- };
777
- function _cpName(cp) {
778
- const n = _CP_NAME[cp];
779
- return n !== void 0 ? n : "<unnamed>";
780
- }
781
- function _scan(sf) {
782
- const pragmaForm = sf.pragma_form(CHECK);
783
- if (pragmaForm === "legacy") {
784
- return [legacy_pragma_finding(sf, CHECK)];
785
- }
786
- const finish = (hits) => pragmaForm === "bound" ? sf.filter_bound_pragma(CHECK, hits) : hits;
787
- const out = [];
788
- for (const [lineno, text] of sf.iter_lines({ skip_example_fence: true })) {
789
- let vs_run = 0;
790
- for (const ch of text) {
791
- const cp = ch.codePointAt(0);
792
- if (_isVS(cp)) {
793
- vs_run += 1;
794
- continue;
795
- }
796
- const kind = _classify(cp);
797
- if (kind) {
798
- const name = _cpName(cp);
799
- out.push(
800
- new Finding(
801
- sf.rel,
802
- lineno,
803
- CHECK,
804
- "HIGH",
805
- `${kind} U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${name})`,
806
- sf.weight
807
- )
808
- );
809
- }
810
- }
811
- if (vs_run >= 3) {
812
- out.push(
813
- new Finding(
814
- sf.rel,
815
- lineno,
816
- CHECK,
817
- "HIGH",
818
- `variation-selector run x${vs_run} (steganography signature)`,
819
- sf.weight
820
- )
821
- );
822
- }
823
- }
824
- return finish(out);
825
- }
826
- var _BINARY_EXT = /* @__PURE__ */ new Set([
827
- "png",
828
- "jpg",
829
- "jpeg",
830
- "gif",
831
- "ico",
832
- "webp",
833
- "avif",
834
- "bmp",
835
- "tiff",
836
- "pdf",
837
- "woff",
838
- "woff2",
839
- "ttf",
840
- "otf",
841
- "eot",
842
- "zip",
843
- "gz",
844
- "tgz",
845
- "bz2",
846
- "xz",
847
- "zst",
848
- "7z",
849
- "rar",
850
- "wasm",
851
- "mp3",
852
- "mp4",
853
- "wav",
854
- "ogg",
855
- "webm",
856
- "mov",
857
- "avi",
858
- "so",
859
- "dylib",
860
- "dll",
861
- "node",
862
- "a",
863
- "o",
864
- "exe",
865
- "jar",
866
- "class",
867
- "pyc",
868
- "pyo",
869
- "db",
870
- "sqlite",
871
- "sqlite3",
872
- "bin",
873
- "dat",
874
- "docx",
875
- "xlsx",
876
- "pptx",
877
- "doc",
878
- "xls",
879
- "ppt",
880
- "odt",
881
- "ods",
882
- "odp"
883
- ]);
884
- var _GENERATED_PREFIXES = [
885
- "dist/",
886
- ".augment/",
887
- ".claude/",
888
- ".cursor/",
889
- ".clinerules/",
890
- // Verbatim captured tool output, not authored text. These are RECORDINGS:
891
- // escaping a control byte inside one would falsify the record it exists to
892
- // preserve. If a capture needs to stay grep-readable, the fix is to escape
893
- // at capture time — never to edit the artifact afterwards.
894
- "agents/evidence/analysis/",
895
- // The frozen hostile-encoding corpus. Its positives ARE raw control
896
- // characters — that is the thing under test, so flagging them would demand
897
- // escaping the fixtures and thereby delete the test. The corpus has its own,
898
- // stronger integrity controls: a sha256 freeze plus a scope-guard test that
899
- // is itself falsified by an out-of-scope fixture. Excluded here because it
900
- // is DATA, on the same principle as the captures above.
901
- "internal/bench/corpora/encoding-channels/"
902
- ];
903
- function _isGenerated(rel) {
904
- return _GENERATED_PREFIXES.some((p) => rel.startsWith(p));
905
- }
906
- function _isRawControlCodepoint(cp) {
907
- if (cp === 9 || cp === 10 || cp === 13) return false;
908
- return cp <= 31 || cp >= 127 && cp <= 159;
909
- }
910
- function _extensionOf(rel) {
911
- const base = rel.slice(rel.lastIndexOf("/") + 1);
912
- const dot = base.lastIndexOf(".");
913
- return dot <= 0 ? "" : base.slice(dot + 1).toLowerCase();
914
- }
915
- function _trackedFiles() {
916
- const res = spawnSync("git", ["ls-files", "-z"], {
917
- cwd: ROOT,
918
- maxBuffer: 64 * 1024 * 1024
919
- });
920
- if (res.status !== 0 || res.stdout === null) return null;
921
- return res.stdout.toString("utf-8").split("\0").filter((p) => p !== "");
922
- }
923
- function _eligibleSourceFiles() {
924
- const tracked = _trackedFiles();
925
- if (tracked === null) return null;
926
- return tracked.filter((rel) => !_isGenerated(rel) && !_BINARY_EXT.has(_extensionOf(rel)));
927
- }
928
- function _scanSourceControlBytes(files = null) {
929
- const tracked = files ?? _eligibleSourceFiles();
930
- if (tracked === null) return [];
931
- const out = [];
932
- for (const rel of tracked) {
933
- if (_isGenerated(rel)) continue;
934
- if (_BINARY_EXT.has(_extensionOf(rel))) continue;
935
- let data;
936
- try {
937
- data = fs2.readFileSync(path2.join(ROOT, rel));
938
- } catch {
939
- continue;
940
- }
941
- try {
942
- new TextDecoder("utf-8", { fatal: true }).decode(data);
943
- } catch {
944
- continue;
945
- }
946
- let line = 1;
947
- for (const ch of data.toString("utf-8")) {
948
- const cp = ch.codePointAt(0);
949
- if (cp === void 0) continue;
950
- if (cp === 10) {
951
- line += 1;
952
- continue;
953
- }
954
- if (!_isRawControlCodepoint(cp)) continue;
955
- const hex = cp.toString(16).toUpperCase().padStart(2, "0");
956
- out.push(
957
- new Finding(
958
- rel,
959
- line,
960
- CHECK,
961
- "HIGH",
962
- `raw control character U+${hex} in a text source \u2014 makes grep/file(1)/git treat this file as binary and skip it silently; use the language escape (e.g. \\0, \\x7F) instead`,
963
- 1
964
- )
965
- );
966
- }
967
- }
968
- return out;
969
- }
970
- function _sanitize(filePath) {
971
- const raw = fs2.readFileSync(filePath, "utf-8");
972
- let cleaned = "";
973
- for (const ch of raw) {
974
- const cp = ch.codePointAt(0);
975
- if (_classify(cp) === null && !_isVS(cp)) {
976
- cleaned += ch;
977
- }
978
- }
979
- cleaned = cleaned.normalize("NFKC");
980
- const out = filePath + ".sanitized";
981
- fs2.writeFileSync(out, cleaned, "utf-8");
982
- return out;
983
- }
984
- function _argError(msg) {
985
- process.stderr.write("usage: lint_hidden_unicode [-h] [--json] [--fix] [--root DIR]\n");
986
- process.stderr.write(`lint_hidden_unicode: error: ${msg}
987
- `);
988
- process.exit(2);
989
- }
990
- function parse_args(argv) {
991
- const out = { json: false, fix: false };
992
- const extra = [];
993
- let rootPending = false;
994
- for (const a of argv) {
995
- if (rootPending) {
996
- out.root = a;
997
- rootPending = false;
998
- continue;
999
- }
1000
- if (a === "-h" || a === "--help") {
1001
- process.stdout.write("usage: lint_hidden_unicode [-h] [--json] [--fix] [--root DIR]\n");
1002
- process.exit(0);
1003
- } else if (a === "--json") {
1004
- out.json = true;
1005
- } else if (a === "--fix") {
1006
- out.fix = true;
1007
- } else if (a === "--root" || a.startsWith("--root=")) {
1008
- const eq = a.indexOf("=");
1009
- if (eq !== -1) {
1010
- out.root = a.slice(eq + 1);
1011
- } else {
1012
- rootPending = true;
1013
- }
1014
- } else {
1015
- extra.push(a);
1016
- }
1017
- }
1018
- if (extra.length > 0) {
1019
- _argError(`unrecognized arguments: ${extra.join(" ")}`);
1020
- }
1021
- return out;
1022
- }
1023
- function main(argv = null) {
1024
- const args = parse_args(argv ?? process.argv.slice(2));
1025
- const scanBase = args.root === void 0 ? ROOT : path2.resolve(args.root);
1026
- const findings = [];
1027
- const flagged = /* @__PURE__ */ new Set();
1028
- let corpusFiles = 0;
1029
- for (const sf of iter_corpus(DEFAULT_SCAN_ROOTS, [".md"], scanBase)) {
1030
- corpusFiles += 1;
1031
- const hits = _scan(sf);
1032
- for (const h of hits) {
1033
- findings.push(h);
1034
- }
1035
- if (hits.length > 0) {
1036
- flagged.add(sf.path);
1037
- }
1038
- }
1039
- try {
1040
- assertScanned({
1041
- gate: "lint_hidden_unicode",
1042
- scanned: corpusFiles,
1043
- units: "corpus file(s)",
1044
- roots: DEFAULT_SCAN_ROOTS
1045
- });
1046
- } catch (exc) {
1047
- if (exc instanceof DeadScopeError) {
1048
- process.stderr.write(`\u274C ${exc.message}
1049
- `);
1050
- return 1;
1051
- }
1052
- throw exc;
1053
- }
1054
- const sourceFiles = args.root === void 0 ? _eligibleSourceFiles() : [];
1055
- for (const h of _scanSourceControlBytes(sourceFiles)) {
1056
- findings.push(h);
1057
- }
1058
- if (!args.json) {
1059
- const n = sourceFiles === null ? 0 : sourceFiles.length;
1060
- process.stdout.write(
1061
- sourceFiles === null ? " source pass: SKIPPED \u2014 `git ls-files` unavailable, 0 files read for raw control bytes\n" : ` source pass: ${n} tracked text file(s) read for raw C0 control bytes (generated trees + verbatim captures excluded)
1062
- `
1063
- );
1064
- }
1065
- if (args.fix) {
1066
- for (const p of [...flagged].sort()) {
1067
- const sanitized = _sanitize(p);
1068
- const rel = path2.relative(path2.resolve(ROOT), path2.resolve(sanitized));
1069
- process.stdout.write(` fixed \u2192 ${rel.split(path2.sep).join("/")}
1070
- `);
1071
- }
1072
- }
1073
- if (args.json) {
1074
- report_child_scanned(corpusFiles + (sourceFiles === null ? 0 : sourceFiles.length));
1075
- }
1076
- if (args.json) {
1077
- const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
1078
- process.stdout.write(payload + "\n");
1079
- return findings.some((f) => f.is_fail) ? 1 : 0;
1080
- }
1081
- return report(findings, { check_label: "hidden-unicode" });
1082
- }
1083
- function _isCliEntry() {
1084
- if (true) {
1085
- return false;
1086
- }
1087
- if (process.argv[1] === void 0) {
1088
- return false;
1089
- }
1090
- const argvUrl = pathToFileURL(path2.resolve(process.argv[1])).href;
1091
- if (import.meta.url === argvUrl) {
1092
- return true;
1093
- }
1094
- try {
1095
- const here = fs2.realpathSync(fileURLToPath2(import.meta.url));
1096
- const argv = fs2.realpathSync(path2.resolve(process.argv[1]));
1097
- return here === argv;
1098
- } catch {
1099
- return false;
1100
- }
1101
- }
1102
- if (_isCliEntry()) {
1103
- process.exitCode = main();
1104
- }
1105
-
1106
- // src/scripts/_lib/confusables.ts
1107
- var _LATIN = new RegExp("\\p{Script=Latin}", "u");
1108
- var _CYRILLIC = new RegExp("\\p{Script=Cyrillic}", "u");
1109
- var _GREEK = new RegExp("\\p{Script=Greek}", "u");
1110
- var _LETTER = new RegExp("\\p{L}", "u");
25
+ import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
26
+ import { createRequire } from "node:module";
1111
27
 
1112
28
  // src/scripts/_lib/retrieval_sanitize.ts
1113
29
  var MAX_FIELD_CHARS = 8192;
@@ -1154,212 +70,22 @@ function _sanitize_value(v) {
1154
70
  return v;
1155
71
  }
1156
72
 
1157
- // src/scripts/_lib/lexical_index.ts
1158
- var BM25_K1 = 1.5;
1159
- var BM25_B = 0.75;
1160
- var MIN_TOKEN_LEN = 2;
1161
- function tokenize(s) {
1162
- const out = [];
1163
- for (const raw of s.toLowerCase().split(/[^a-z0-9]+/)) {
1164
- if (raw.length >= MIN_TOKEN_LEN) out.push(raw);
1165
- }
1166
- return out;
1167
- }
1168
- function trigrams(s) {
1169
- const norm = s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1170
- const out = /* @__PURE__ */ new Set();
1171
- if (norm.length < 3) {
1172
- if (norm.length > 0) out.add(norm);
1173
- return out;
1174
- }
1175
- for (let i = 0; i + 3 <= norm.length; i++) {
1176
- out.add(norm.slice(i, i + 3));
1177
- }
1178
- return out;
1179
- }
1180
- var LexicalIndex = class {
1181
- docs = /* @__PURE__ */ new Map();
1182
- df = /* @__PURE__ */ new Map();
1183
- n;
1184
- avgdl;
1185
- constructor(documents) {
1186
- let totalLen = 0;
1187
- for (const d of documents) {
1188
- const tokens = tokenize(d.text);
1189
- const tf = /* @__PURE__ */ new Map();
1190
- for (const t of tokens) tf.set(t, (tf.get(t) ?? 0) + 1);
1191
- for (const t of tf.keys()) this.df.set(t, (this.df.get(t) ?? 0) + 1);
1192
- this.docs.set(d.id, { id: d.id, len: tokens.length, tf, trigrams: trigrams(d.text) });
1193
- totalLen += tokens.length;
1194
- }
1195
- this.n = documents.length;
1196
- this.avgdl = this.n > 0 ? totalLen / this.n : 0;
1197
- }
1198
- /** Robertson–Spärck-Jones IDF, floored at 0 so common terms never subtract. */
1199
- idf(term) {
1200
- const df = this.df.get(term) ?? 0;
1201
- if (df === 0) return 0;
1202
- return Math.max(0, Math.log(1 + (this.n - df + 0.5) / (df + 0.5)));
1203
- }
1204
- /**
1205
- * BM25 score of one document against the tokenised query terms, weighted by
1206
- * **term coverage**: the raw BM25 sum is multiplied by `coverage²`, where
1207
- * `coverage = distinct-query-terms-matched / distinct-query-terms`. This
1208
- * stops a single generic, high-TF term match from outranking a document that
1209
- * matches several rarer query terms once each (Phase 1 refinement). A
1210
- * single-term query has `coverage == 1`, so its score is unchanged — the
1211
- * weighting only re-orders genuinely multi-term queries.
1212
- */
1213
- score(queryTerms, docId) {
1214
- const doc = this.docs.get(docId);
1215
- if (doc === void 0 || doc.len === 0) return 0;
1216
- let s = 0;
1217
- const matched = /* @__PURE__ */ new Set();
1218
- for (const term of queryTerms) {
1219
- const tf = doc.tf.get(term);
1220
- if (tf === void 0) continue;
1221
- const idf = this.idf(term);
1222
- if (idf === 0) continue;
1223
- const denom = tf + BM25_K1 * (1 - BM25_B + BM25_B * doc.len / (this.avgdl || 1));
1224
- s += idf * (tf * (BM25_K1 + 1)) / denom;
1225
- matched.add(term);
1226
- }
1227
- if (s === 0) return 0;
1228
- const distinctQuery = new Set(queryTerms).size;
1229
- const coverage = distinctQuery > 0 ? matched.size / distinctQuery : 1;
1230
- return s * coverage * coverage;
1231
- }
1232
- /**
1233
- * Rank documents for a set of query keys (phrases are tokenised). A
1234
- * character-trigram prefilter selects candidates cheaply; the guard keeps
1235
- * any doc sharing a query TOKEN even when no trigram overlaps, so the
1236
- * prefilter never drops a genuine lexical match. Ties break by id for a
1237
- * stable, deterministic order.
1238
- */
1239
- rank(queryKeys) {
1240
- const queryText = queryKeys.join(" ");
1241
- const qTerms = tokenize(queryText);
1242
- const qTermSet = new Set(qTerms);
1243
- const qTrigrams = trigrams(queryText);
1244
- const candidates = [];
1245
- for (const [id, doc] of this.docs) {
1246
- let hit = false;
1247
- for (const t of qTrigrams) {
1248
- if (doc.trigrams.has(t)) {
1249
- hit = true;
1250
- break;
1251
- }
1252
- }
1253
- if (!hit) {
1254
- for (const t of qTermSet) {
1255
- if (doc.tf.has(t)) {
1256
- hit = true;
1257
- break;
1258
- }
1259
- }
1260
- }
1261
- if (hit) candidates.push(id);
1262
- }
1263
- const scored = candidates.map((id) => ({ id, score: this.score(qTerms, id) })).filter((r) => r.score > 0);
1264
- scored.sort((a, b) => b.score - a.score || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
1265
- return scored;
1266
- }
1267
- /** Document count — for the caller's tripwire / diagnostics. */
1268
- get size() {
1269
- return this.n;
1270
- }
1271
- };
1272
-
1273
73
  // src/scripts/_lib/memory_fts_index.ts
1274
- import * as fs5 from "node:fs";
1275
- import * as path5 from "node:path";
1276
-
1277
- // src/scripts/_lib/sqlite_guard.ts
1278
- import { createRequire } from "node:module";
1279
- var SqliteUnavailableError = class extends Error {
1280
- constructor(callerName, cause) {
1281
- super(
1282
- `node:sqlite is unavailable in this runtime (${cause}). ${callerName} requires Node with the built-in SQLite module.`
1283
- );
1284
- this.name = "SqliteUnavailableError";
1285
- }
1286
- };
1287
- var _sqliteWarningSilenced = false;
1288
- function _silenceSqliteExperimentalWarning() {
1289
- if (_sqliteWarningSilenced) return;
1290
- _sqliteWarningSilenced = true;
1291
- const orig = process.emitWarning.bind(process);
1292
- process.emitWarning = ((warning, ...rest) => {
1293
- const text = typeof warning === "string" ? warning : warning?.message ?? "";
1294
- if (/SQLite is an experimental/i.test(text)) return;
1295
- orig(warning, ...rest);
1296
- });
1297
- }
1298
- var _require;
1299
- function _syncRequire() {
1300
- if (_require === void 0) {
1301
- _require = createRequire(import.meta.url);
1302
- }
1303
- return _require;
1304
- }
1305
- function loadSqliteSync(callerName) {
1306
- _silenceSqliteExperimentalWarning();
1307
- try {
1308
- return _syncRequire()("node:sqlite");
1309
- } catch (exc) {
1310
- const message = exc instanceof Error ? exc.message : String(exc);
1311
- throw new SqliteUnavailableError(callerName, message);
1312
- }
1313
- }
1314
- function probeFts5Sync() {
1315
- let sqlite;
1316
- try {
1317
- sqlite = loadSqliteSync("sqlite_guard.probeFts5Sync");
1318
- } catch {
1319
- return false;
1320
- }
1321
- let conn = null;
1322
- try {
1323
- conn = new sqlite.DatabaseSync(":memory:");
1324
- conn.exec("CREATE VIRTUAL TABLE _fts5_probe USING fts5(body)");
1325
- conn.exec("INSERT INTO _fts5_probe(body) VALUES ('probe')");
1326
- const row = conn.prepare("SELECT body FROM _fts5_probe WHERE _fts5_probe MATCH 'probe'").get();
1327
- return row !== void 0;
1328
- } catch {
1329
- return false;
1330
- } finally {
1331
- try {
1332
- conn?.close();
1333
- } catch {
1334
- }
1335
- }
1336
- }
1337
- function readUserVersion(conn) {
1338
- const row = conn.prepare("PRAGMA user_version").get();
1339
- const v = row?.["user_version"];
1340
- if (typeof v === "bigint") return Number(v);
1341
- if (typeof v === "number") return v;
1342
- return 0;
1343
- }
1344
- function stampUserVersion(conn, version) {
1345
- if (!Number.isInteger(version) || version < 0 || version > 2 ** 31 - 1) {
1346
- throw new RangeError(`stampUserVersion: version out of range: ${version}`);
1347
- }
1348
- conn.exec(`PRAGMA user_version = ${version}`);
1349
- }
74
+ import * as fs3 from "node:fs";
75
+ import * as path3 from "node:path";
1350
76
 
1351
77
  // src/scripts/memory_report.ts
1352
78
  var import_yaml = __toESM(require_dist(), 1);
1353
- import * as fs4 from "node:fs";
1354
- import * as path4 from "node:path";
79
+ import * as fs2 from "node:fs";
80
+ import * as path2 from "node:path";
1355
81
  import process3 from "node:process";
1356
- import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
82
+ import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
1357
83
 
1358
84
  // src/scripts/memory_status.ts
1359
85
  import process2 from "node:process";
1360
- import * as fs3 from "node:fs";
1361
- import * as path3 from "node:path";
1362
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
86
+ import * as fs from "node:fs";
87
+ import * as path from "node:path";
88
+ import { fileURLToPath, pathToFileURL } from "node:url";
1363
89
  var CONTRACT_VERSION = 1;
1364
90
  var _FILE_BACKEND_VERSION = "0.0.0-file";
1365
91
  var _FILE_BACKEND_FEATURES = ["file-fallback"];
@@ -1471,7 +197,7 @@ function _parseArgs(argv) {
1471
197
  }
1472
198
  return args;
1473
199
  }
1474
- function main2() {
200
+ function main() {
1475
201
  const args = _parseArgs(process2.argv.slice(2));
1476
202
  if (args.health) {
1477
203
  process2.stdout.write(`${_pyJsonDumps(health())}
@@ -1488,29 +214,29 @@ function main2() {
1488
214
  }
1489
215
  return 0;
1490
216
  }
1491
- function _isCliEntry2() {
217
+ function _isCliEntry() {
1492
218
  if (true) {
1493
219
  return false;
1494
220
  }
1495
221
  if (process2.argv[1] === void 0) {
1496
222
  return false;
1497
223
  }
1498
- const argvUrl = pathToFileURL2(path3.resolve(process2.argv[1])).href;
224
+ const argvUrl = pathToFileURL(path.resolve(process2.argv[1])).href;
1499
225
  if (import.meta.url === argvUrl) {
1500
226
  return true;
1501
227
  }
1502
228
  try {
1503
- const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
1504
- const argv = fs3.realpathSync(path3.resolve(process2.argv[1]));
229
+ const here = fs.realpathSync(fileURLToPath(import.meta.url));
230
+ const argv = fs.realpathSync(path.resolve(process2.argv[1]));
1505
231
  return here === argv;
1506
232
  } catch {
1507
233
  return false;
1508
234
  }
1509
235
  }
1510
- var _isMain = _isCliEntry2();
236
+ var _isMain = _isCliEntry();
1511
237
  if (_isMain) {
1512
238
  try {
1513
- process2.exitCode = main2();
239
+ process2.exitCode = main();
1514
240
  } catch (e) {
1515
241
  if (e instanceof _ExitError) {
1516
242
  process2.exitCode = e.code;
@@ -1521,8 +247,8 @@ if (_isMain) {
1521
247
  }
1522
248
 
1523
249
  // src/scripts/memory_report.ts
1524
- var MEMORY_ROOT = path4.join("agents", "memory");
1525
- var INTAKE_ROOT = path4.join(MEMORY_ROOT, "intake");
250
+ var MEMORY_ROOT = path2.join("agents", "memory");
251
+ var INTAKE_ROOT = path2.join(MEMORY_ROOT, "intake");
1526
252
  function _setMemoryRoot(p) {
1527
253
  MEMORY_ROOT = p;
1528
254
  }
@@ -1535,10 +261,10 @@ var CURATED_TYPES = [
1535
261
  ];
1536
262
  var _MODE_MARKER_PATTERN = /<!--\s*role-mode:\s*([a-z0-9][a-z0-9-]*)\s*\|\s*contract:[^>]*-->/g;
1537
263
  var _MODE_SCAN_DIRS = [
1538
- path4.join("agents", "sessions"),
1539
- path4.join("agents", "runtime", "reports"),
1540
- path4.join("agents", "handoffs"),
1541
- path4.join("agents", "learnings")
264
+ path2.join("agents", "sessions"),
265
+ path2.join("agents", "runtime", "reports"),
266
+ path2.join("agents", "handoffs"),
267
+ path2.join("agents", "learnings")
1542
268
  ];
1543
269
  var _KNOWN_MODES = /* @__PURE__ */ new Set([
1544
270
  "developer",
@@ -1559,31 +285,31 @@ var PyTimestamp = class {
1559
285
  return this.pyStr;
1560
286
  }
1561
287
  };
1562
- var PyFloat2 = class {
288
+ var PyFloat = class {
1563
289
  constructor(value) {
1564
290
  this.value = value;
1565
291
  }
1566
292
  value;
1567
293
  };
1568
- function _isPlainObject2(v) {
1569
- return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp) && !(v instanceof PyFloat2);
294
+ function _isPlainObject(v) {
295
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp) && !(v instanceof PyFloat);
1570
296
  }
1571
297
  function _isFile(p) {
1572
298
  try {
1573
- return fs4.statSync(p).isFile();
299
+ return fs2.statSync(p).isFile();
1574
300
  } catch {
1575
301
  return false;
1576
302
  }
1577
303
  }
1578
304
  function _isDir(p) {
1579
305
  try {
1580
- return fs4.statSync(p).isDirectory();
306
+ return fs2.statSync(p).isDirectory();
1581
307
  } catch {
1582
308
  return false;
1583
309
  }
1584
310
  }
1585
311
  function _load_yaml(p) {
1586
- const text = fs4.readFileSync(p, "utf-8");
312
+ const text = fs2.readFileSync(p, "utf-8");
1587
313
  const doc = (0, import_yaml.parseDocument)(text, { version: "1.1", prettyErrors: false });
1588
314
  if (doc.errors.length > 0) {
1589
315
  const err = doc.errors[0];
@@ -1643,30 +369,30 @@ function _pyYamlDatetimeStr(raw) {
1643
369
  function _iter_curated_entries() {
1644
370
  const out = [];
1645
371
  for (const mtype of CURATED_TYPES) {
1646
- const single = path4.join(MEMORY_ROOT, `${mtype}.yml`);
372
+ const single = path2.join(MEMORY_ROOT, `${mtype}.yml`);
1647
373
  if (_isFile(single)) {
1648
374
  const data = _load_yaml(single);
1649
375
  const entries = data["entries"];
1650
376
  if (Array.isArray(entries)) {
1651
377
  for (const e of entries) {
1652
- if (_isPlainObject2(e)) {
378
+ if (_isPlainObject(e)) {
1653
379
  out.push([single, mtype, e]);
1654
380
  }
1655
381
  }
1656
382
  }
1657
383
  }
1658
- const type_dir = path4.join(MEMORY_ROOT, mtype);
384
+ const type_dir = path2.join(MEMORY_ROOT, mtype);
1659
385
  if (_isDir(type_dir)) {
1660
386
  for (const yml of _rglobYml(type_dir)) {
1661
387
  const data = _load_yaml(yml) ?? {};
1662
388
  const entries = data["entries"];
1663
389
  if (Array.isArray(entries)) {
1664
390
  for (const e of entries) {
1665
- if (_isPlainObject2(e)) {
391
+ if (_isPlainObject(e)) {
1666
392
  out.push([yml, mtype, e]);
1667
393
  }
1668
394
  }
1669
- } else if (_isPlainObject2(data) && data["id"]) {
395
+ } else if (_isPlainObject(data) && data["id"]) {
1670
396
  out.push([yml, mtype, data]);
1671
397
  }
1672
398
  }
@@ -1679,12 +405,12 @@ function _rglobYml(root) {
1679
405
  const walk = (dir) => {
1680
406
  let entries;
1681
407
  try {
1682
- entries = fs4.readdirSync(dir, { withFileTypes: true });
408
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
1683
409
  } catch {
1684
410
  return;
1685
411
  }
1686
412
  for (const ent of entries) {
1687
- const full = path4.join(dir, ent.name);
413
+ const full = path2.join(dir, ent.name);
1688
414
  if (ent.isDirectory()) {
1689
415
  walk(full);
1690
416
  } else if (ent.isFile() && ent.name.endsWith(".yml")) {
@@ -1699,11 +425,11 @@ function _rglobYml(root) {
1699
425
  function _globJsonl(root) {
1700
426
  let names;
1701
427
  try {
1702
- names = fs4.readdirSync(root);
428
+ names = fs2.readdirSync(root);
1703
429
  } catch {
1704
430
  return [];
1705
431
  }
1706
- return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path4.join(root, n));
432
+ return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path2.join(root, n));
1707
433
  }
1708
434
  function _intake_stats() {
1709
435
  let total = 0;
@@ -1712,7 +438,7 @@ function _intake_stats() {
1712
438
  let superseded = 0;
1713
439
  if (_isDir(INTAKE_ROOT)) {
1714
440
  for (const jsonl of _globJsonl(INTAKE_ROOT)) {
1715
- const stem = path4.basename(jsonl).replace(/\.jsonl$/, "");
441
+ const stem = path2.basename(jsonl).replace(/\.jsonl$/, "");
1716
442
  const month = stem.replace("signals-", "");
1717
443
  for (const obj of _readJsonl(jsonl)) {
1718
444
  if (obj["type"] === "supersede") {
@@ -1738,7 +464,7 @@ function _intake_stats() {
1738
464
  function _readJsonl(p) {
1739
465
  let text;
1740
466
  try {
1741
- text = fs4.readFileSync(p, "utf-8");
467
+ text = fs2.readFileSync(p, "utf-8");
1742
468
  } catch {
1743
469
  return [];
1744
470
  }
@@ -1754,7 +480,7 @@ function _readJsonl(p) {
1754
480
  } catch {
1755
481
  continue;
1756
482
  }
1757
- if (_isPlainObject2(obj)) {
483
+ if (_isPlainObject(obj)) {
1758
484
  out.push(obj);
1759
485
  }
1760
486
  }
@@ -1872,7 +598,7 @@ function _quarterly_stats() {
1872
598
  return {
1873
599
  accepted_by_quarter: _mapToObj(accepted),
1874
600
  retired_by_quarter: _mapToObj(retired),
1875
- staleness_rate: new PyFloat2(_pyRound3(rate)),
601
+ staleness_rate: new PyFloat(_pyRound3(rate)),
1876
602
  curated_total: total,
1877
603
  curated_overdue: overdue
1878
604
  };
@@ -1901,7 +627,7 @@ function _role_mode_stats() {
1901
627
  for (const md of _rglobMd(root)) {
1902
628
  let text;
1903
629
  try {
1904
- text = fs4.readFileSync(md, "utf-8");
630
+ text = fs2.readFileSync(md, "utf-8");
1905
631
  } catch {
1906
632
  continue;
1907
633
  }
@@ -1930,7 +656,7 @@ function _role_mode_stats() {
1930
656
  }
1931
657
  function _exists(p) {
1932
658
  try {
1933
- fs4.statSync(p);
659
+ fs2.statSync(p);
1934
660
  return true;
1935
661
  } catch {
1936
662
  return false;
@@ -1941,12 +667,12 @@ function _rglobMd(root) {
1941
667
  const walk = (dir) => {
1942
668
  let entries;
1943
669
  try {
1944
- entries = fs4.readdirSync(dir, { withFileTypes: true });
670
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
1945
671
  } catch {
1946
672
  return;
1947
673
  }
1948
674
  for (const ent of entries) {
1949
- const full = path4.join(dir, ent.name);
675
+ const full = path2.join(dir, ent.name);
1950
676
  if (ent.isDirectory()) {
1951
677
  walk(full);
1952
678
  } else if (ent.isFile() && ent.name.endsWith(".md")) {
@@ -1972,19 +698,19 @@ function build_report() {
1972
698
  role_modes: _role_mode_stats()
1973
699
  };
1974
700
  }
1975
- function _print_text(report2) {
701
+ function _print_text(report) {
1976
702
  const out = [];
1977
- const b = report2.backend;
703
+ const b = report.backend;
1978
704
  out.push(`Backend: ${b.status} (backend=${b.backend})`);
1979
705
  if (b.reason) {
1980
706
  out.push(` reason: ${b.reason}`);
1981
707
  }
1982
- const intake = report2.intake;
708
+ const intake = report.intake;
1983
709
  out.push(`Intake: ${intake.total_active} active, ${intake.superseded} superseded`);
1984
710
  for (const [t, n] of _sortedEntries(intake.by_type)) {
1985
711
  out.push(` - ${t}: ${n}`);
1986
712
  }
1987
- const stale = report2.staleness;
713
+ const stale = report.staleness;
1988
714
  if (stale.length === 0) {
1989
715
  out.push("Staleness: no curated entries past review_after_days");
1990
716
  } else {
@@ -1996,7 +722,7 @@ function _print_text(report2) {
1996
722
  out.push(` (+${stale.length - 5} more)`);
1997
723
  }
1998
724
  }
1999
- const q = report2.quarterly;
725
+ const q = report.quarterly;
2000
726
  out.push(`Quarterly: staleness-rate=${_pyPercent1(q.staleness_rate.value)} (${q.curated_overdue}/${q.curated_total})`);
2001
727
  if (Object.keys(q.accepted_by_quarter).length > 0) {
2002
728
  const acc = _sortedEntries(q.accepted_by_quarter).map(([k, v]) => `${k}:${v}`).join(", ");
@@ -2006,7 +732,7 @@ function _print_text(report2) {
2006
732
  const ret = _sortedEntries(q.retired_by_quarter).map(([k, v]) => `${k}:${v}`).join(", ");
2007
733
  out.push(` retired: ${ret}`);
2008
734
  }
2009
- const rm = report2.role_modes;
735
+ const rm = report.role_modes;
2010
736
  if (rm.files_scanned) {
2011
737
  out.push(`Role modes: ${rm.total_markers} marker(s) in ${rm.files_scanned} file(s)`);
2012
738
  if (Object.keys(rm.by_mode).length > 0) {
@@ -2047,7 +773,7 @@ function _dumpsIndent(value, indent, depth) {
2047
773
  if (value instanceof PyTimestamp) {
2048
774
  return _jsonStrAscii(value.pyStr);
2049
775
  }
2050
- if (value instanceof PyFloat2) {
776
+ if (value instanceof PyFloat) {
2051
777
  return _jsonFloat(value.value);
2052
778
  }
2053
779
  if (Array.isArray(value)) {
@@ -2059,7 +785,7 @@ function _dumpsIndent(value, indent, depth) {
2059
785
  ${items.join(",\n")}
2060
786
  ${closePad}]`;
2061
787
  }
2062
- if (_isPlainObject2(value)) {
788
+ if (_isPlainObject(value)) {
2063
789
  const keys = Object.keys(value);
2064
790
  if (keys.length === 0) {
2065
791
  return "{}";
@@ -2141,7 +867,7 @@ function _escapeNonAscii2(s) {
2141
867
  }
2142
868
  var _PROG2 = "memory_report.py";
2143
869
  var _USAGE2 = "usage: memory_report.py [-h] [--format {text,json}]\n";
2144
- function _argError2(msg) {
870
+ function _argError(msg) {
2145
871
  process3.stderr.write(_USAGE2);
2146
872
  process3.stderr.write(`${_PROG2}: error: ${msg}
2147
873
  `);
@@ -2159,61 +885,61 @@ function _parseArgs2(argv) {
2159
885
  process3.stdout.write(_USAGE2);
2160
886
  process3.exit(0);
2161
887
  } else {
2162
- _argError2(`unrecognized arguments: ${a}`);
888
+ _argError(`unrecognized arguments: ${a}`);
2163
889
  }
2164
890
  }
2165
891
  return args;
2166
892
  }
2167
893
  function _checkChoice(value, choices, flag) {
2168
894
  if (value === void 0 || !choices.includes(value)) {
2169
- _argError2(
895
+ _argError(
2170
896
  `argument ${flag}: invalid choice: '${value ?? ""}' (choose from ${choices.map((c) => `'${c}'`).join(", ")})`
2171
897
  );
2172
898
  }
2173
899
  return value;
2174
900
  }
2175
- function main3() {
901
+ function main2() {
2176
902
  const args = _parseArgs2(process3.argv.slice(2));
2177
- const report2 = build_report();
903
+ const report = build_report();
2178
904
  if (args.format === "json") {
2179
- process3.stdout.write(`${pyJsonDumps(report2, 2)}
905
+ process3.stdout.write(`${pyJsonDumps(report, 2)}
2180
906
  `);
2181
907
  } else {
2182
- _print_text(report2);
908
+ _print_text(report);
2183
909
  }
2184
910
  return 0;
2185
911
  }
2186
- function _isCliEntry3() {
912
+ function _isCliEntry2() {
2187
913
  if (true) {
2188
914
  return false;
2189
915
  }
2190
916
  if (process3.argv[1] === void 0) {
2191
917
  return false;
2192
918
  }
2193
- const argvUrl = pathToFileURL3(path4.resolve(process3.argv[1])).href;
919
+ const argvUrl = pathToFileURL2(path2.resolve(process3.argv[1])).href;
2194
920
  if (import.meta.url === argvUrl) {
2195
921
  return true;
2196
922
  }
2197
923
  try {
2198
- const here = fs4.realpathSync(fileURLToPath4(import.meta.url));
2199
- const argv = fs4.realpathSync(path4.resolve(process3.argv[1]));
924
+ const here = fs2.realpathSync(fileURLToPath2(import.meta.url));
925
+ const argv = fs2.realpathSync(path2.resolve(process3.argv[1]));
2200
926
  return here === argv;
2201
927
  } catch {
2202
928
  return false;
2203
929
  }
2204
930
  }
2205
- var _isMain2 = _isCliEntry3();
931
+ var _isMain2 = _isCliEntry2();
2206
932
  if (_isMain2) {
2207
- process3.exit(main3());
933
+ process3.exit(main2());
2208
934
  }
2209
935
 
2210
936
  // src/scripts/_lib/memory_fts_index.ts
2211
- var MEMORY_INDEX_REL = path5.join("agents", "runtime", "state", "memory-index-v1.sqlite3");
937
+ var MEMORY_INDEX_REL = path3.join("agents", "runtime", "state", "memory-index-v1.sqlite3");
2212
938
  var SCHEMA_VERSION = 1;
2213
939
  var CURATED_STATUS_EXCLUDE = /* @__PURE__ */ new Set(["deprecated", "archived", "superseded"]);
2214
940
  var INDEX_TEXT_FIELDS = ["path", "key", "symptom", "feature", "rule", "body"];
2215
941
  function resolveIndexPath(consumerRoot) {
2216
- return path5.join(consumerRoot ?? process.cwd(), MEMORY_INDEX_REL);
942
+ return path3.join(consumerRoot ?? process.cwd(), MEMORY_INDEX_REL);
2217
943
  }
2218
944
  function _pyStr(v) {
2219
945
  if (v === void 0 || v === null) return "";
@@ -2229,7 +955,7 @@ function _entryIndexText(entry) {
2229
955
  }
2230
956
  return parts.join(" | ");
2231
957
  }
2232
- function _isPlainObject3(v) {
958
+ function _isPlainObject2(v) {
2233
959
  return typeof v === "object" && v !== null && !Array.isArray(v);
2234
960
  }
2235
961
  function _collectCurated(memoryRoot) {
@@ -2252,11 +978,11 @@ function _collectCurated(memoryRoot) {
2252
978
  function _globJsonl2(intakeRoot) {
2253
979
  let names;
2254
980
  try {
2255
- names = fs5.readdirSync(intakeRoot);
981
+ names = fs3.readdirSync(intakeRoot);
2256
982
  } catch {
2257
983
  return [];
2258
984
  }
2259
- return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path5.join(intakeRoot, n));
985
+ return names.filter((n) => n.endsWith(".jsonl")).sort().map((n) => path3.join(intakeRoot, n));
2260
986
  }
2261
987
  function _collectIntake(intakeRoot) {
2262
988
  const out = [];
@@ -2265,7 +991,7 @@ function _collectIntake(intakeRoot) {
2265
991
  const superseded = /* @__PURE__ */ new Set();
2266
992
  let content;
2267
993
  try {
2268
- content = fs5.readFileSync(jsonl, "utf-8");
994
+ content = fs3.readFileSync(jsonl, "utf-8");
2269
995
  } catch {
2270
996
  continue;
2271
997
  }
@@ -2278,7 +1004,7 @@ function _collectIntake(intakeRoot) {
2278
1004
  } catch {
2279
1005
  continue;
2280
1006
  }
2281
- if (!_isPlainObject3(obj)) continue;
1007
+ if (!_isPlainObject2(obj)) continue;
2282
1008
  if (obj["type"] === "supersede") {
2283
1009
  const target = obj["supersedes"];
2284
1010
  if (typeof target === "string") superseded.add(target);
@@ -2302,7 +1028,7 @@ function _sourcesMaxMtimeMs(memoryRoot, intakeRoot) {
2302
1028
  let max = 0;
2303
1029
  const bump = (p) => {
2304
1030
  try {
2305
- const st = fs5.statSync(p);
1031
+ const st = fs3.statSync(p);
2306
1032
  if (st.mtimeMs > max) max = st.mtimeMs;
2307
1033
  } catch {
2308
1034
  }
@@ -2310,21 +1036,21 @@ function _sourcesMaxMtimeMs(memoryRoot, intakeRoot) {
2310
1036
  const walk = (dir) => {
2311
1037
  let entries;
2312
1038
  try {
2313
- entries = fs5.readdirSync(dir, { withFileTypes: true });
1039
+ entries = fs3.readdirSync(dir, { withFileTypes: true });
2314
1040
  } catch {
2315
1041
  return;
2316
1042
  }
2317
1043
  for (const e of entries) {
2318
- const full = path5.join(dir, e.name);
1044
+ const full = path3.join(dir, e.name);
2319
1045
  if (e.isDirectory()) walk(full);
2320
1046
  else if (e.isFile() && e.name.endsWith(".yml")) bump(full);
2321
1047
  }
2322
1048
  };
2323
1049
  let memEntries;
2324
1050
  try {
2325
- memEntries = fs5.readdirSync(memoryRoot, { withFileTypes: true });
1051
+ memEntries = fs3.readdirSync(memoryRoot, { withFileTypes: true });
2326
1052
  for (const e of memEntries) {
2327
- const full = path5.join(memoryRoot, e.name);
1053
+ const full = path3.join(memoryRoot, e.name);
2328
1054
  if (e.isFile() && e.name.endsWith(".yml")) bump(full);
2329
1055
  else if (e.isDirectory()) walk(full);
2330
1056
  }
@@ -2335,7 +1061,7 @@ function _sourcesMaxMtimeMs(memoryRoot, intakeRoot) {
2335
1061
  }
2336
1062
  var META_KEY_SOURCES_MTIME = "sources_mtime_ms";
2337
1063
  function isStale(indexPath, memoryRoot, intakeRoot) {
2338
- if (!fs5.existsSync(indexPath)) return true;
1064
+ if (!fs3.existsSync(indexPath)) return true;
2339
1065
  let sqlite;
2340
1066
  try {
2341
1067
  sqlite = loadSqliteSync("memory_fts_index.isStale");
@@ -2361,11 +1087,11 @@ function isStale(indexPath, memoryRoot, intakeRoot) {
2361
1087
  }
2362
1088
  }
2363
1089
  function buildIndex(indexPath, memoryRoot, intakeRoot) {
2364
- fs5.mkdirSync(path5.dirname(indexPath), { recursive: true });
1090
+ fs3.mkdirSync(path3.dirname(indexPath), { recursive: true });
2365
1091
  try {
2366
- fs5.rmSync(indexPath, { force: true });
2367
- fs5.rmSync(`${indexPath}-wal`, { force: true });
2368
- fs5.rmSync(`${indexPath}-shm`, { force: true });
1092
+ fs3.rmSync(indexPath, { force: true });
1093
+ fs3.rmSync(`${indexPath}-wal`, { force: true });
1094
+ fs3.rmSync(`${indexPath}-shm`, { force: true });
2369
1095
  } catch {
2370
1096
  }
2371
1097
  const { DatabaseSync } = loadSqliteSync("memory_fts_index.buildIndex");
@@ -2449,39 +1175,40 @@ function queryIndex(indexPath, keys, types) {
2449
1175
  }
2450
1176
 
2451
1177
  // src/scripts/learning_sidecar.ts
2452
- import { createHash as createHash2 } from "node:crypto";
2453
- import * as fs6 from "node:fs";
2454
- import * as path6 from "node:path";
1178
+ import { createHash } from "node:crypto";
1179
+ import * as fs4 from "node:fs";
1180
+ import * as path4 from "node:path";
2455
1181
  import process4 from "node:process";
2456
- import { fileURLToPath as fileURLToPath5 } from "node:url";
1182
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
2457
1183
  var PROG = "learning_sidecar.ts";
2458
1184
  var HALF_LIFE_DAYS = 30;
2459
1185
  var MIN_CORROBORATIONS = 2;
2460
1186
  var SIDECAR_NAME = ".agent-learning.json";
2461
1187
  var LESSONS_NAME = "LESSONS.md";
2462
1188
  var SCHEMA_VERSION2 = 1;
1189
+ var FORMATS = ["text", "json", "status"];
2463
1190
  function _parseTsMs(ts) {
2464
1191
  const t = Date.parse(ts);
2465
1192
  return Number.isNaN(t) ? null : t;
2466
1193
  }
2467
1194
  function _bodyHash(body) {
2468
- return createHash2("sha256").update(body.trim().toLowerCase()).digest("hex").slice(0, 12);
1195
+ return createHash("sha256").update(body.trim().toLowerCase()).digest("hex").slice(0, 12);
2469
1196
  }
2470
1197
  function _globSignalFiles(dir) {
2471
1198
  let names;
2472
1199
  try {
2473
- names = fs6.readdirSync(dir);
1200
+ names = fs4.readdirSync(dir);
2474
1201
  } catch {
2475
1202
  return [];
2476
1203
  }
2477
- return names.filter((n) => n.startsWith("signals-") && n.endsWith(".jsonl")).sort().map((n) => path6.join(dir, n));
1204
+ return names.filter((n) => n.startsWith("signals-") && n.endsWith(".jsonl")).sort().map((n) => path4.join(dir, n));
2478
1205
  }
2479
1206
  function readSignals(intakeDir) {
2480
1207
  const out = [];
2481
1208
  for (const file of _globSignalFiles(intakeDir)) {
2482
1209
  let content;
2483
1210
  try {
2484
- content = fs6.readFileSync(file, "utf-8");
1211
+ content = fs4.readFileSync(file, "utf-8");
2485
1212
  } catch {
2486
1213
  continue;
2487
1214
  }
@@ -2572,9 +1299,11 @@ function aggregate(signals, nowMs) {
2572
1299
  return lessons;
2573
1300
  }
2574
1301
  function buildSidecar(intakeDir, nowIso) {
1302
+ return buildSidecarFromSignals(readSignals(intakeDir), nowIso);
1303
+ }
1304
+ function buildSidecarFromSignals(signals, nowIso) {
2575
1305
  const nowMs = _parseTsMs(nowIso) ?? 0;
2576
- const lessons = aggregate(readSignals(intakeDir), nowMs);
2577
- return { schema_version: SCHEMA_VERSION2, generated_at: nowIso, lessons };
1306
+ return { schema_version: SCHEMA_VERSION2, generated_at: nowIso, lessons: aggregate(signals, nowMs) };
2578
1307
  }
2579
1308
  function renderLessonsMd(sidecar) {
2580
1309
  const dead = sidecar.lessons.filter((l) => l.verdict === "dead_end");
@@ -2604,9 +1333,42 @@ function renderLessonsMd(sidecar) {
2604
1333
  L.push("");
2605
1334
  return L.join("\n");
2606
1335
  }
2607
- function main4(argv) {
2608
- let intakeDir = path6.join("agents", "memory", "intake");
2609
- let outDir = path6.join("agents", "memory");
1336
+ var STATUS_TOP_N = 5;
1337
+ var PROMOTION_HANDOFF = "Promote by hand: /memory:propose \xB7 src/skills/learning-to-rule-or-skill/SKILL.md";
1338
+ function renderStatus(sidecar) {
1339
+ const byVerdict = { preferred: 0, contested: 0, dead_end: 0 };
1340
+ for (const l of sidecar.lessons) byVerdict[l.verdict] += 1;
1341
+ const nowMs = _parseTsMs(sidecar.generated_at) ?? 0;
1342
+ const L = [];
1343
+ L.push(`${PROG}: ${String(sidecar.lessons.length)} lesson(s) as of ${sidecar.generated_at}`);
1344
+ L.push(
1345
+ `preferred: ${String(byVerdict.preferred)} \xB7 contested: ${String(byVerdict.contested)} \xB7 dead-end: ${String(byVerdict.dead_end)}`
1346
+ );
1347
+ L.push("");
1348
+ const preferred = sidecar.lessons.filter((l) => l.verdict === "preferred").sort((a, b) => b.score - a.score || (a.path < b.path ? -1 : 1)).slice(0, STATUS_TOP_N);
1349
+ L.push(`Top ${String(STATUS_TOP_N)} preferred:`);
1350
+ if (preferred.length === 0) {
1351
+ L.push(" (none \u2014 a lesson needs corroboration before it appears here)");
1352
+ } else {
1353
+ preferred.forEach((l, i) => {
1354
+ const ageDays = Math.max(0, Math.floor((nowMs - (_parseTsMs(l.last_ts) ?? nowMs)) / 864e5));
1355
+ L.push(
1356
+ ` ${String(i + 1)}. ${l.entry_type} @ ${l.path} \u2014 ${String(l.corroborations)} origins, ${String(ageDays)}d old`
1357
+ );
1358
+ L.push(` ${l.body}`);
1359
+ });
1360
+ }
1361
+ L.push("");
1362
+ L.push(
1363
+ `A lesson is promoted at ${String(MIN_CORROBORATIONS)} distinct origins, weighted by a ${String(HALF_LIFE_DAYS)}-day half-life; nothing here promotes itself.`
1364
+ );
1365
+ L.push(PROMOTION_HANDOFF);
1366
+ return `${L.join("\n")}
1367
+ `;
1368
+ }
1369
+ function main3(argv) {
1370
+ let intakeDir = path4.join("agents", "memory", "intake");
1371
+ let outDir = path4.join("agents", "memory");
2610
1372
  let nowIso = "";
2611
1373
  let write = false;
2612
1374
  let format = "text";
@@ -2616,10 +1378,19 @@ function main4(argv) {
2616
1378
  else if (a === "--out-dir") outDir = argv[++i] ?? outDir;
2617
1379
  else if (a === "--now") nowIso = argv[++i] ?? "";
2618
1380
  else if (a === "--write") write = true;
2619
- else if (a === "--format") format = argv[++i] ?? "text";
2620
- else if (a === "-h" || a === "--help") {
1381
+ else if (a === "--format") {
1382
+ const raw = argv[++i] ?? "";
1383
+ if (!FORMATS.includes(raw)) {
1384
+ process4.stderr.write(
1385
+ `${PROG}: error: argument --format: invalid choice: '${raw}' (choose from ${FORMATS.map((f) => `'${f}'`).join(", ")})
1386
+ `
1387
+ );
1388
+ return 2;
1389
+ }
1390
+ format = raw;
1391
+ } else if (a === "-h" || a === "--help") {
2621
1392
  process4.stdout.write(
2622
- `usage: ${PROG} [--intake-dir DIR] [--out-dir DIR] [--now ISO] [--write] [--format text|json]
1393
+ `usage: ${PROG} [--intake-dir DIR] [--out-dir DIR] [--now ISO] [--write] [--format ${FORMATS.join("|")}]
2623
1394
  `
2624
1395
  );
2625
1396
  return 0;
@@ -2629,6 +1400,11 @@ function main4(argv) {
2629
1400
  return 2;
2630
1401
  }
2631
1402
  }
1403
+ if (format === "status" && write) {
1404
+ process4.stderr.write(`${PROG}: error: --format status is read-only; drop --write
1405
+ `);
1406
+ return 2;
1407
+ }
2632
1408
  if (!nowIso) {
2633
1409
  nowIso = (/* @__PURE__ */ new Date(0)).toISOString();
2634
1410
  }
@@ -2641,14 +1417,18 @@ function main4(argv) {
2641
1417
  return 3;
2642
1418
  }
2643
1419
  if (write) {
2644
- fs6.mkdirSync(outDir, { recursive: true });
2645
- fs6.writeFileSync(path6.join(outDir, SIDECAR_NAME), JSON.stringify(sidecar, null, 2) + "\n");
2646
- fs6.writeFileSync(path6.join(outDir, LESSONS_NAME), renderLessonsMd(sidecar));
1420
+ fs4.mkdirSync(outDir, { recursive: true });
1421
+ fs4.writeFileSync(path4.join(outDir, SIDECAR_NAME), JSON.stringify(sidecar, null, 2) + "\n");
1422
+ fs4.writeFileSync(path4.join(outDir, LESSONS_NAME), renderLessonsMd(sidecar));
2647
1423
  }
2648
1424
  if (format === "json") {
2649
1425
  process4.stdout.write(JSON.stringify(sidecar, null, 2) + "\n");
2650
1426
  return 0;
2651
1427
  }
1428
+ if (format === "status") {
1429
+ process4.stdout.write(renderStatus(sidecar));
1430
+ return 0;
1431
+ }
2652
1432
  const byVerdict = { preferred: 0, contested: 0, dead_end: 0 };
2653
1433
  for (const l of sidecar.lessons) byVerdict[l.verdict] += 1;
2654
1434
  process4.stdout.write(
@@ -2658,8 +1438,8 @@ function main4(argv) {
2658
1438
  return 0;
2659
1439
  }
2660
1440
  var _bundled = true;
2661
- if (!_bundled && fileURLToPath5(import.meta.url) === path6.resolve(process4.argv[1] ?? "")) {
2662
- process4.exit(main4(process4.argv.slice(2)));
1441
+ if (!_bundled && fileURLToPath3(import.meta.url) === path4.resolve(process4.argv[1] ?? "")) {
1442
+ process4.exit(main3(process4.argv.slice(2)));
2663
1443
  }
2664
1444
 
2665
1445
  // src/scripts/memory_lookup.ts
@@ -2674,15 +1454,15 @@ var PyTimestamp2 = class {
2674
1454
  return this.pyStr;
2675
1455
  }
2676
1456
  };
2677
- var PyFloat3 = class {
1457
+ var PyFloat2 = class {
2678
1458
  constructor(value) {
2679
1459
  this.value = value;
2680
1460
  }
2681
1461
  value;
2682
1462
  };
2683
- var MEMORY_ROOT2 = path7.join("agents", "memory");
2684
- var INTAKE_ROOT2 = path7.join(MEMORY_ROOT2, "intake");
2685
- var KNOWLEDGE_ROOT = path7.join(MEMORY_ROOT2, "knowledge");
1463
+ var MEMORY_ROOT2 = path5.join("agents", "memory");
1464
+ var INTAKE_ROOT2 = path5.join(MEMORY_ROOT2, "intake");
1465
+ var KNOWLEDGE_ROOT = path5.join(MEMORY_ROOT2, "knowledge");
2686
1466
  var _LEX_TYPE_FILES_MAX = 200;
2687
1467
  var _LEX_CORPUS_FILES_MAX = 500;
2688
1468
  var _LEX_SOURCE_FACTOR = { curated: 1, intake: 0.9, knowledge: 0.85 };
@@ -2691,17 +1471,17 @@ var _corpusFileCounts = null;
2691
1471
  var _learningOverlay;
2692
1472
  function _loadLearningOverlay() {
2693
1473
  if (_learningOverlay !== void 0) return _learningOverlay;
2694
- const file = path7.join(MEMORY_ROOT2, SIDECAR_NAME);
1474
+ const file = path5.join(MEMORY_ROOT2, SIDECAR_NAME);
2695
1475
  try {
2696
- const parsed = JSON.parse(fs7.readFileSync(file, "utf-8"));
2697
- const lessons = _isPlainObject4(parsed) ? parsed["lessons"] : null;
1476
+ const parsed = JSON.parse(fs5.readFileSync(file, "utf-8"));
1477
+ const lessons = _isPlainObject3(parsed) ? parsed["lessons"] : null;
2698
1478
  if (!Array.isArray(lessons)) {
2699
1479
  _learningOverlay = null;
2700
1480
  return null;
2701
1481
  }
2702
1482
  const map = /* @__PURE__ */ new Map();
2703
1483
  for (const l of lessons) {
2704
- if (!_isPlainObject4(l)) continue;
1484
+ if (!_isPlainObject3(l)) continue;
2705
1485
  const t = l["entry_type"];
2706
1486
  const p = l["path"];
2707
1487
  const v = l["verdict"];
@@ -2761,30 +1541,30 @@ var Hit = class {
2761
1541
  source: this.source,
2762
1542
  path: this.path,
2763
1543
  // score is a Python float — render `0.0` not `0` for whole numbers.
2764
- score: new PyFloat3(this.score),
1544
+ score: new PyFloat2(this.score),
2765
1545
  entry: this.entry
2766
1546
  };
2767
1547
  }
2768
1548
  };
2769
- function _isPlainObject4(v) {
2770
- return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp2) && !(v instanceof PyFloat3);
1549
+ function _isPlainObject3(v) {
1550
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyTimestamp2) && !(v instanceof PyFloat2);
2771
1551
  }
2772
1552
  function _isFile2(p) {
2773
1553
  try {
2774
- return fs7.statSync(p).isFile();
1554
+ return fs5.statSync(p).isFile();
2775
1555
  } catch {
2776
1556
  return false;
2777
1557
  }
2778
1558
  }
2779
1559
  function _isDir2(p) {
2780
1560
  try {
2781
- return fs7.statSync(p).isDirectory();
1561
+ return fs5.statSync(p).isDirectory();
2782
1562
  } catch {
2783
1563
  return false;
2784
1564
  }
2785
1565
  }
2786
1566
  function _load_yaml2(p) {
2787
- const text = fs7.readFileSync(p, "utf-8");
1567
+ const text = fs5.readFileSync(p, "utf-8");
2788
1568
  const doc = (0, import_yaml2.parseDocument)(text, { version: "1.1", prettyErrors: false });
2789
1569
  if (doc.errors.length > 0) {
2790
1570
  const err = doc.errors[0];
@@ -2842,13 +1622,13 @@ function _pad22(v) {
2842
1622
  return v.length === 1 ? `0${v}` : v;
2843
1623
  }
2844
1624
  function* _iter_curated_entries2(mtype) {
2845
- const type_dir = path7.join(MEMORY_ROOT2, mtype);
2846
- const single_file = path7.join(MEMORY_ROOT2, `${mtype}.yml`);
1625
+ const type_dir = path5.join(MEMORY_ROOT2, mtype);
1626
+ const single_file = path5.join(MEMORY_ROOT2, `${mtype}.yml`);
2847
1627
  if (_isFile2(single_file)) {
2848
1628
  const data = _load_yaml2(single_file);
2849
- const entries = _isPlainObject4(data) ? data["entries"] : null;
1629
+ const entries = _isPlainObject3(data) ? data["entries"] : null;
2850
1630
  for (const e of Array.isArray(entries) ? entries : []) {
2851
- if (_isPlainObject4(e)) {
1631
+ if (_isPlainObject3(e)) {
2852
1632
  if (_statusExcluded(e)) {
2853
1633
  continue;
2854
1634
  }
@@ -2860,17 +1640,17 @@ function* _iter_curated_entries2(mtype) {
2860
1640
  for (const yml of _rglobYmlSorted(type_dir)) {
2861
1641
  const dataRaw = _load_yaml2(yml);
2862
1642
  const data = dataRaw == null ? {} : dataRaw;
2863
- const entries = _isPlainObject4(data) ? data["entries"] : void 0;
1643
+ const entries = _isPlainObject3(data) ? data["entries"] : void 0;
2864
1644
  if (Array.isArray(entries)) {
2865
1645
  for (const e of entries) {
2866
- if (_isPlainObject4(e)) {
1646
+ if (_isPlainObject3(e)) {
2867
1647
  if (_statusExcluded(e)) {
2868
1648
  continue;
2869
1649
  }
2870
1650
  yield [yml, e];
2871
1651
  }
2872
1652
  }
2873
- } else if (_isPlainObject4(data) && data["id"]) {
1653
+ } else if (_isPlainObject3(data) && data["id"]) {
2874
1654
  if (_statusExcluded(data)) {
2875
1655
  continue;
2876
1656
  }
@@ -2880,13 +1660,13 @@ function* _iter_curated_entries2(mtype) {
2880
1660
  }
2881
1661
  }
2882
1662
  function* _iter_curated_entries_all(mtype) {
2883
- const type_dir = path7.join(MEMORY_ROOT2, mtype);
2884
- const single_file = path7.join(MEMORY_ROOT2, `${mtype}.yml`);
1663
+ const type_dir = path5.join(MEMORY_ROOT2, mtype);
1664
+ const single_file = path5.join(MEMORY_ROOT2, `${mtype}.yml`);
2885
1665
  if (_isFile2(single_file)) {
2886
1666
  const data = _load_yaml2(single_file);
2887
- const entries = _isPlainObject4(data) ? data["entries"] : null;
1667
+ const entries = _isPlainObject3(data) ? data["entries"] : null;
2888
1668
  for (const e of Array.isArray(entries) ? entries : []) {
2889
- if (_isPlainObject4(e)) {
1669
+ if (_isPlainObject3(e)) {
2890
1670
  yield [single_file, e];
2891
1671
  }
2892
1672
  }
@@ -2895,14 +1675,14 @@ function* _iter_curated_entries_all(mtype) {
2895
1675
  for (const yml of _rglobYmlSorted(type_dir)) {
2896
1676
  const dataRaw = _load_yaml2(yml);
2897
1677
  const data = dataRaw == null ? {} : dataRaw;
2898
- const entries = _isPlainObject4(data) ? data["entries"] : void 0;
1678
+ const entries = _isPlainObject3(data) ? data["entries"] : void 0;
2899
1679
  if (Array.isArray(entries)) {
2900
1680
  for (const e of entries) {
2901
- if (_isPlainObject4(e)) {
1681
+ if (_isPlainObject3(e)) {
2902
1682
  yield [yml, e];
2903
1683
  }
2904
1684
  }
2905
- } else if (_isPlainObject4(data) && data["id"]) {
1685
+ } else if (_isPlainObject3(data) && data["id"]) {
2906
1686
  yield [yml, data];
2907
1687
  }
2908
1688
  }
@@ -2915,7 +1695,7 @@ function* _iter_intake_entries(mtype) {
2915
1695
  for (const jsonl of _globJsonlSorted(INTAKE_ROOT2)) {
2916
1696
  const by_id = /* @__PURE__ */ new Map();
2917
1697
  const superseded = /* @__PURE__ */ new Set();
2918
- const content = fs7.readFileSync(jsonl, "utf-8");
1698
+ const content = fs5.readFileSync(jsonl, "utf-8");
2919
1699
  for (let line of content.split(/\n/)) {
2920
1700
  line = line.trim();
2921
1701
  if (!line) {
@@ -2927,7 +1707,7 @@ function* _iter_intake_entries(mtype) {
2927
1707
  } catch {
2928
1708
  continue;
2929
1709
  }
2930
- if (!_isPlainObject4(obj)) {
1710
+ if (!_isPlainObject3(obj)) {
2931
1711
  continue;
2932
1712
  }
2933
1713
  if (obj["type"] === "supersede") {
@@ -2961,27 +1741,27 @@ function* _iter_knowledge_entries() {
2961
1741
  if (!_isDir2(ingest_dir)) {
2962
1742
  continue;
2963
1743
  }
2964
- const manifest_path = path7.join(ingest_dir, "manifest.json");
1744
+ const manifest_path = path5.join(ingest_dir, "manifest.json");
2965
1745
  let manifest = {};
2966
1746
  if (_isFile2(manifest_path)) {
2967
1747
  try {
2968
- const parsed = JSON.parse(fs7.readFileSync(manifest_path, "utf-8"));
2969
- manifest = _isPlainObject4(parsed) ? parsed : {};
1748
+ const parsed = JSON.parse(fs5.readFileSync(manifest_path, "utf-8"));
1749
+ manifest = _isPlainObject3(parsed) ? parsed : {};
2970
1750
  } catch {
2971
1751
  manifest = {};
2972
1752
  }
2973
1753
  }
2974
- const ingest_id = String(manifest["ingest_id"] || path7.basename(ingest_dir));
1754
+ const ingest_id = String(manifest["ingest_id"] || path5.basename(ingest_dir));
2975
1755
  const source = String(manifest["source"] || "");
2976
1756
  const pinned = Boolean(manifest["pinned"] ?? false);
2977
- const chunks_dir = path7.join(ingest_dir, "chunks");
1757
+ const chunks_dir = path5.join(ingest_dir, "chunks");
2978
1758
  if (!_isDir2(chunks_dir)) {
2979
1759
  continue;
2980
1760
  }
2981
1761
  for (const chunk of _globMdSorted(chunks_dir)) {
2982
1762
  let body;
2983
1763
  try {
2984
- body = fs7.readFileSync(chunk, "utf-8");
1764
+ body = fs5.readFileSync(chunk, "utf-8");
2985
1765
  } catch {
2986
1766
  continue;
2987
1767
  }
@@ -3083,7 +1863,7 @@ function _entry_id(mtype, file, entry, ordinals) {
3083
1863
  if (own !== void 0 && own !== null && String(own).trim() !== "") {
3084
1864
  return String(own);
3085
1865
  }
3086
- return `${mtype}:${path7.basename(file)}:${ordinal}`;
1866
+ return `${mtype}:${path5.basename(file)}:${ordinal}`;
3087
1867
  }
3088
1868
  function _entry_title(entry, id) {
3089
1869
  for (const field of ["title", "key", "path"]) {
@@ -3108,7 +1888,7 @@ var _tokenizer;
3108
1888
  function _lazy_tokenizer() {
3109
1889
  if (_tokenizer === void 0) {
3110
1890
  try {
3111
- const req = createRequire2(import.meta.url);
1891
+ const req = createRequire(import.meta.url);
3112
1892
  _tokenizer = req("./_lib/token_count.js");
3113
1893
  } catch {
3114
1894
  _tokenizer = null;
@@ -3127,13 +1907,13 @@ function _entry_tokens_estimate(envelope_entry) {
3127
1907
  function _countMdFiles(dir) {
3128
1908
  let entries;
3129
1909
  try {
3130
- entries = fs7.readdirSync(dir, { withFileTypes: true });
1910
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
3131
1911
  } catch {
3132
1912
  return 0;
3133
1913
  }
3134
1914
  let n = 0;
3135
1915
  for (const e of entries) {
3136
- if (e.isDirectory()) n += _countMdFiles(path7.join(dir, e.name));
1916
+ if (e.isDirectory()) n += _countMdFiles(path5.join(dir, e.name));
3137
1917
  else if (e.isFile() && e.name.endsWith(".md")) n += 1;
3138
1918
  }
3139
1919
  return n;
@@ -3143,7 +1923,7 @@ function _countCorpusFiles() {
3143
1923
  const perType = /* @__PURE__ */ new Map();
3144
1924
  let total = 0;
3145
1925
  for (const t of CURATED_TYPES2) {
3146
- const exists = fs7.existsSync(path7.join(MEMORY_ROOT2, `${t}.yml`));
1926
+ const exists = fs5.existsSync(path5.join(MEMORY_ROOT2, `${t}.yml`));
3147
1927
  const c = exists ? 1 : 0;
3148
1928
  perType.set(t, c);
3149
1929
  total += c;
@@ -3152,7 +1932,7 @@ function _countCorpusFiles() {
3152
1932
  perType.set(KNOWLEDGE_TYPE, kn);
3153
1933
  total += kn;
3154
1934
  try {
3155
- for (const e of fs7.readdirSync(INTAKE_ROOT2, { withFileTypes: true })) {
1935
+ for (const e of fs5.readdirSync(INTAKE_ROOT2, { withFileTypes: true })) {
3156
1936
  if (e.isFile() && e.name.endsWith(".jsonl")) total += 1;
3157
1937
  }
3158
1938
  } catch {
@@ -3300,7 +2080,7 @@ function _retrieve_internal(types, keys, limit, today) {
3300
2080
  }
3301
2081
  const hits = (positives.length > 0 ? positives : repo_hits).slice(0, limit);
3302
2082
  for (const h of hits) {
3303
- if (_isPlainObject4(h.entry)) h.entry = sanitize_entry(h.entry);
2083
+ if (_isPlainObject3(h.entry)) h.entry = sanitize_entry(h.entry);
3304
2084
  }
3305
2085
  return [hits, skipped];
3306
2086
  }
@@ -3344,7 +2124,7 @@ function retrieve_v1(types, keys, limit = 20, options = {}) {
3344
2124
  let truncation = null;
3345
2125
  for (let hi = 0; hi < hits.length; hi++) {
3346
2126
  const h = hits[hi];
3347
- const safeEntry = _isPlainObject4(h.entry) ? sanitize_entry(h.entry) : {};
2127
+ const safeEntry = _isPlainObject3(h.entry) ? sanitize_entry(h.entry) : {};
3348
2128
  const learning = _learningVerdictFor(h.type, safeEntry);
3349
2129
  if (learning !== void 0) safeEntry["learning"] = learning;
3350
2130
  if (budgeted) {
@@ -3364,7 +2144,7 @@ function retrieve_v1(types, keys, limit = 20, options = {}) {
3364
2144
  id: h.id,
3365
2145
  type: h.type,
3366
2146
  source: "repo",
3367
- confidence: new PyFloat3(_round4(h.score)),
2147
+ confidence: new PyFloat2(_round4(h.score)),
3368
2148
  line,
3369
2149
  ...learning !== void 0 ? { learning } : {}
3370
2150
  });
@@ -3378,7 +2158,7 @@ function retrieve_v1(types, keys, limit = 20, options = {}) {
3378
2158
  type: h.type,
3379
2159
  source: "repo",
3380
2160
  // confidence = round(float(score), 4) — always a Python float.
3381
- confidence: new PyFloat3(_round4(h.score)),
2161
+ confidence: new PyFloat2(_round4(h.score)),
3382
2162
  body: safeEntry
3383
2163
  };
3384
2164
  if (h.type in slice_counts) {
@@ -3389,7 +2169,7 @@ function retrieve_v1(types, keys, limit = 20, options = {}) {
3389
2169
  id: h.id,
3390
2170
  type: h.type,
3391
2171
  source: "repo",
3392
- confidence: new PyFloat3(_round4(h.score)),
2172
+ confidence: new PyFloat2(_round4(h.score)),
3393
2173
  title: _entry_title(safeEntry, h.id),
3394
2174
  tokens_estimate: _entry_tokens_estimate(envelope_entry),
3395
2175
  // Knowledge chunks carry their pinned flag in the index row
@@ -3438,7 +2218,7 @@ function memory_get_v1(ids) {
3438
2218
  const found = /* @__PURE__ */ new Map();
3439
2219
  const record = (id, mtype, entry) => {
3440
2220
  if (wanted.has(id) && !found.has(id)) {
3441
- const safeEntry = _isPlainObject4(entry) ? sanitize_entry(entry) : {};
2221
+ const safeEntry = _isPlainObject3(entry) ? sanitize_entry(entry) : {};
3442
2222
  const learning = _learningVerdictFor(mtype, safeEntry);
3443
2223
  if (learning !== void 0) safeEntry["learning"] = learning;
3444
2224
  found.set(id, {
@@ -3563,7 +2343,7 @@ function _printUsage() {
3563
2343
  "usage: memory_lookup [-h] [--types TYPES] [--key KEY] [--limit LIMIT] [--format {text,json}] [--envelope {legacy,v1}] [--detail {index,full}] [--token-budget N]\n"
3564
2344
  );
3565
2345
  }
3566
- function main5() {
2346
+ function main4() {
3567
2347
  const args = _parseArgs3(process5.argv.slice(2));
3568
2348
  const types = args.types.split(",").map((t) => t.trim()).filter((t) => t);
3569
2349
  if (types.length === 0) {
@@ -3710,12 +2490,12 @@ function _rglobYmlSorted(root) {
3710
2490
  const walk = (dir) => {
3711
2491
  let entries;
3712
2492
  try {
3713
- entries = fs7.readdirSync(dir, { withFileTypes: true });
2493
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
3714
2494
  } catch {
3715
2495
  return;
3716
2496
  }
3717
2497
  for (const ent of entries) {
3718
- const full = path7.join(dir, ent.name);
2498
+ const full = path5.join(dir, ent.name);
3719
2499
  if (ent.name.endsWith(".yml")) {
3720
2500
  out.push(full);
3721
2501
  }
@@ -3737,27 +2517,27 @@ function _globMdSorted(dir) {
3737
2517
  function _globSorted(dir, suffix) {
3738
2518
  let entries;
3739
2519
  try {
3740
- entries = fs7.readdirSync(dir, { withFileTypes: true });
2520
+ entries = fs5.readdirSync(dir, { withFileTypes: true });
3741
2521
  } catch {
3742
2522
  return [];
3743
2523
  }
3744
- const out = entries.filter((e) => e.name.endsWith(suffix) && (e.isFile() || e.isSymbolicLink() && _isFile2(path7.join(dir, e.name)))).map((e) => path7.join(dir, e.name));
2524
+ const out = entries.filter((e) => e.name.endsWith(suffix) && (e.isFile() || e.isSymbolicLink() && _isFile2(path5.join(dir, e.name)))).map((e) => path5.join(dir, e.name));
3745
2525
  out.sort();
3746
2526
  return out;
3747
2527
  }
3748
2528
  function _iterdirSorted(dir) {
3749
2529
  let names;
3750
2530
  try {
3751
- names = fs7.readdirSync(dir);
2531
+ names = fs5.readdirSync(dir);
3752
2532
  } catch {
3753
2533
  return [];
3754
2534
  }
3755
- const out = names.map((name) => path7.join(dir, name));
2535
+ const out = names.map((name) => path5.join(dir, name));
3756
2536
  out.sort();
3757
2537
  return out;
3758
2538
  }
3759
2539
  function _stem(p) {
3760
- const base = path7.basename(p);
2540
+ const base = path5.basename(p);
3761
2541
  const dot = base.lastIndexOf(".");
3762
2542
  return dot <= 0 ? base : base.slice(0, dot);
3763
2543
  }
@@ -3782,7 +2562,7 @@ function _dumpsIndent2(value, indent, depth) {
3782
2562
  if (value instanceof PyTimestamp2) {
3783
2563
  return _jsonStrAscii2(value.pyStr);
3784
2564
  }
3785
- if (value instanceof PyFloat3) {
2565
+ if (value instanceof PyFloat2) {
3786
2566
  return _jsonFloat2(value.value);
3787
2567
  }
3788
2568
  if (Array.isArray(value)) {
@@ -3794,7 +2574,7 @@ function _dumpsIndent2(value, indent, depth) {
3794
2574
  ${items.join(",\n")}
3795
2575
  ${closePad}]`;
3796
2576
  }
3797
- if (_isPlainObject4(value)) {
2577
+ if (_isPlainObject3(value)) {
3798
2578
  const keys = Object.keys(value);
3799
2579
  if (keys.length === 0) {
3800
2580
  return "{}";
@@ -3874,25 +2654,25 @@ function _escapeNonAscii3(s) {
3874
2654
  }
3875
2655
  return out;
3876
2656
  }
3877
- function _isCliEntry4() {
2657
+ function _isCliEntry3() {
3878
2658
  if (process5.argv[1] === void 0) {
3879
2659
  return false;
3880
2660
  }
3881
- const argvUrl = pathToFileURL4(path7.resolve(process5.argv[1])).href;
2661
+ const argvUrl = pathToFileURL3(path5.resolve(process5.argv[1])).href;
3882
2662
  if (import.meta.url === argvUrl) {
3883
2663
  return true;
3884
2664
  }
3885
2665
  try {
3886
- const here = fs7.realpathSync(fileURLToPath6(import.meta.url));
3887
- const argv = fs7.realpathSync(path7.resolve(process5.argv[1]));
2666
+ const here = fs5.realpathSync(fileURLToPath4(import.meta.url));
2667
+ const argv = fs5.realpathSync(path5.resolve(process5.argv[1]));
3888
2668
  return here === argv;
3889
2669
  } catch {
3890
2670
  return false;
3891
2671
  }
3892
2672
  }
3893
- var _isMain3 = _isCliEntry4();
2673
+ var _isMain3 = _isCliEntry3();
3894
2674
  if (_isMain3) {
3895
- process5.exit(main5());
2675
+ process5.exit(main4());
3896
2676
  }
3897
2677
 
3898
2678
  export {