@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
@@ -0,0 +1,1201 @@
1
+ import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
+ import {
3
+ DeadScopeError,
4
+ assertScanned
5
+ } from "./chunk-I3EIN2YG.js";
6
+
7
+ // src/scripts/_lib/security_lint.ts
8
+ import { createHash } from "node:crypto";
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ var _HERE = path.dirname(fileURLToPath(import.meta.url));
13
+ var ROOT = path.resolve(_HERE, "..", "..", "..");
14
+ var EXAMPLE_FENCE_LANG = "security-example";
15
+ var GUIDELINE = "docs/guidelines/agent-infra/security-lint-containment.md";
16
+ var GUIDELINE_EPILOG = `False-positive containment (fenced security-example block, confidence weighting, per-file \`security-lint: allow\` pragma \u2014 no global allowlist): see ${GUIDELINE}.`;
17
+ var DEFAULT_SCAN_ROOTS = [
18
+ "src/skills",
19
+ "src/rules",
20
+ "src/agent-src",
21
+ "src/domains",
22
+ "dist/agent-src"
23
+ ];
24
+ var _EXAMPLE_PATH = /(^|\/)(docs|evals|tests?|fixtures?)(\/|$)|example|template|sample|\/_template/i;
25
+ var _PRAGMA = /<!--\s*security-lint:\s*allow\s+([A-Za-z0-9_-]+)\s+"([^"]+)"((?:\s+sha256:[0-9a-f]{64})*)\s*-->/g;
26
+ var _FENCE = /^(\s*)(`{3,}|~{3,})\s*([A-Za-z0-9_-]*)\s*$/;
27
+ var SEVERITY_RANK = { LOW: 1, MED: 2, HIGH: 3 };
28
+ var Finding = class {
29
+ path;
30
+ // repo-relative
31
+ line;
32
+ // 1-based; 0 = file-level
33
+ check;
34
+ // stable check id, also the pragma key
35
+ severity;
36
+ // HIGH | MED | LOW
37
+ message;
38
+ weight;
39
+ // 1.0 or 0.25
40
+ constructor(pathArg, line, check, severity, message, weight = 1) {
41
+ this.path = pathArg;
42
+ this.line = line;
43
+ this.check = check;
44
+ this.severity = severity;
45
+ this.message = message;
46
+ this.weight = weight;
47
+ }
48
+ /** A HIGH-severity, full-weight finding fails the build. */
49
+ get is_fail() {
50
+ return this.severity === "HIGH" && this.weight >= 1;
51
+ }
52
+ /** Mirror Python dataclass `__dict__` key order for `--json` parity. */
53
+ toDict() {
54
+ return {
55
+ path: this.path,
56
+ line: this.line,
57
+ check: this.check,
58
+ severity: this.severity,
59
+ message: this.message,
60
+ // float in Python → render integer-valued (1.0) as `1.0`.
61
+ weight: new PyFloat(this.weight)
62
+ };
63
+ }
64
+ };
65
+ function is_example_path(rel_path) {
66
+ return _EXAMPLE_PATH.test(rel_path);
67
+ }
68
+ function path_weight(rel_path) {
69
+ return is_example_path(rel_path) ? 0.25 : 1;
70
+ }
71
+ var PROJECTION_PREFIX = "dist/agent-src/";
72
+ var PROJECTION_SOURCE_LANES = [
73
+ // The guidelines lane copies `docs/guidelines/` (`_lib/guidelines_lane.ts`),
74
+ // not `src/guidelines/`, which does not exist. Without this row a projected
75
+ // guideline gets a DIFFERENT identity from its own source, so a bound
76
+ // pragma written in the source cannot suppress the projection's copy of the
77
+ // very same line — measured on
78
+ // `untrusted-input-spotlighting.md`, a defense guideline that quotes
79
+ // role-takeover phrases to teach refusal and carries three accepted
80
+ // fingerprints for exactly that.
81
+ ["guidelines/", "docs/guidelines/"]
82
+ ];
83
+ function source_identity(rel) {
84
+ if (!rel.startsWith(PROJECTION_PREFIX)) return rel;
85
+ const tail = rel.slice(PROJECTION_PREFIX.length);
86
+ for (const [lane, sourceRoot] of PROJECTION_SOURCE_LANES) {
87
+ if (tail.startsWith(lane)) return sourceRoot + tail.slice(lane.length);
88
+ }
89
+ return `src/${tail}`;
90
+ }
91
+ var ScannedFile = class {
92
+ path;
93
+ // absolute or as-given path
94
+ rel;
95
+ lines;
96
+ // per-line flags (1-based index → flag); index 0 unused
97
+ in_example_fence;
98
+ in_any_fence;
99
+ pragmas;
100
+ // check id → reason
101
+ /** check id → the sha256 fingerprints that pragma accepts. Empty = unbound. */
102
+ pragma_hashes;
103
+ weight;
104
+ constructor(pathArg, rel, lines, in_example_fence, in_any_fence, pragmas, weight, pragma_hashes = {}) {
105
+ this.path = pathArg;
106
+ this.rel = rel;
107
+ this.lines = lines;
108
+ this.in_example_fence = in_example_fence;
109
+ this.in_any_fence = in_any_fence;
110
+ this.pragmas = pragmas;
111
+ this.weight = weight;
112
+ this.pragma_hashes = pragma_hashes;
113
+ }
114
+ pragma_allows(check) {
115
+ return Object.prototype.hasOwnProperty.call(this.pragmas, check);
116
+ }
117
+ /**
118
+ * Which form of pragma this file carries for `check`.
119
+ *
120
+ * `absent` — no pragma; scan normally.
121
+ * `legacy` — a pragma with no fingerprint. Suppresses the whole file, as it
122
+ * always did, and says so: a key lookup accepts whatever the
123
+ * line is later changed to say, which is the defect being paid
124
+ * down, not a mode anybody should choose.
125
+ * `bound` — a pragma listing fingerprints. Suppresses exactly the matches
126
+ * whose evidence hashes to one of them; anything else survives.
127
+ */
128
+ pragma_form(check) {
129
+ if (!this.pragma_allows(check)) return "absent";
130
+ return (this.pragma_hashes[check] ?? []).length > 0 ? "bound" : "legacy";
131
+ }
132
+ /**
133
+ * The fingerprint one finding's evidence hashes to.
134
+ *
135
+ * WHAT IS HASHED, and each part earns its place:
136
+ * check id — the same line may legitimately be accepted for one check
137
+ * and not another.
138
+ * rel path — the location IDENTITY. Not the line NUMBER: an unrelated
139
+ * insert above would otherwise break every pragma in the
140
+ * file, and a suppression that breaks on unrelated edits
141
+ * gets deleted rather than re-derived. A `dist/agent-src/`
142
+ * path folds to its `src/` original first, because the
143
+ * projection is byte-exact by contract (ADR-201) and
144
+ * carries the pragma verbatim — without the fold, every
145
+ * migrated pragma would suppress the source and leave its
146
+ * own copy flagged, which is a gate that reds on a file
147
+ * nobody may edit.
148
+ * matched line, — the evidence. ASCII whitespace runs collapse so a reflow
149
+ * normalized does not invalidate the binding, `sha256:<hex>` tokens are
150
+ * removed, and NOTHING else is stripped: the zero-width
151
+ * characters this suite hunts are exactly what a wider
152
+ * normalizer would erase.
153
+ *
154
+ * WHY THE HASH TOKENS COME OUT. A pragma's own reason string can be the
155
+ * matched evidence — two in this tree are, because they quote the very
156
+ * phrases their check detects in order to explain themselves. Without this
157
+ * the fingerprint would have to be computed over a line that already
158
+ * contains it, which has no fixed point: writing the hash changes the line,
159
+ * which changes the hash. Stripping the tokens makes the fingerprint stable
160
+ * under the act of recording it, and costs nothing elsewhere — a `sha256:`
161
+ * token in ordinary prose is not evidence of anything a linter here checks.
162
+ *
163
+ * Deliberately NOT the whole file: incidental content elsewhere would make
164
+ * every unrelated edit re-fire the pragma, which is the noise that produces
165
+ * blanket suppressions.
166
+ */
167
+ evidence_fingerprint(finding) {
168
+ const text = finding.line > 0 ? this.lines[finding.line - 1] ?? "" : "";
169
+ const normalized = text.replace(/\s*sha256:[0-9a-f]{64}/g, "").replace(/[ \t]+/g, " ").trim();
170
+ return createHash("sha256").update(`${finding.check}
171
+ ${source_identity(this.rel)}
172
+ ${normalized}`, "utf-8").digest("hex");
173
+ }
174
+ /** Drop the findings this file's BOUND pragma for `check` accepts. */
175
+ filter_bound_pragma(check, findings) {
176
+ const accepted = new Set(this.pragma_hashes[check] ?? []);
177
+ if (accepted.size === 0) return [...findings];
178
+ return findings.filter((f) => !accepted.has(this.evidence_fingerprint(f)));
179
+ }
180
+ /** Yield [lineno, text] honouring the fence masks (mirrors iter_lines). */
181
+ *iter_lines(opts = {}) {
182
+ const skip_example_fence = opts.skip_example_fence ?? true;
183
+ const skip_any_fence = opts.skip_any_fence ?? false;
184
+ for (let i = 1; i <= this.lines.length; i++) {
185
+ if (skip_example_fence && this.in_example_fence[i]) {
186
+ continue;
187
+ }
188
+ if (skip_any_fence && this.in_any_fence[i]) {
189
+ continue;
190
+ }
191
+ yield [i, this.lines[i - 1]];
192
+ }
193
+ }
194
+ };
195
+ function _splitlines(text) {
196
+ const out = [];
197
+ let buf = "";
198
+ const n = text.length;
199
+ let i = 0;
200
+ while (i < n) {
201
+ const ch = text[i];
202
+ const code = text.charCodeAt(i);
203
+ if (code === 13) {
204
+ out.push(buf);
205
+ buf = "";
206
+ if (i + 1 < n && text.charCodeAt(i + 1) === 10) {
207
+ i += 2;
208
+ } else {
209
+ i += 1;
210
+ }
211
+ continue;
212
+ }
213
+ if (code === 10 || code === 11 || code === 12 || code === 28 || code === 29 || code === 30 || code === 133 || code === 8232 || code === 8233) {
214
+ out.push(buf);
215
+ buf = "";
216
+ i += 1;
217
+ continue;
218
+ }
219
+ buf += ch;
220
+ i += 1;
221
+ }
222
+ if (buf !== "") {
223
+ out.push(buf);
224
+ }
225
+ return out;
226
+ }
227
+ function scan_file(filePath) {
228
+ let rel;
229
+ if (path.isAbsolute(filePath)) {
230
+ const root = path.resolve(ROOT);
231
+ const resolved = path.resolve(filePath);
232
+ if (resolved === root || resolved.startsWith(root + path.sep)) {
233
+ rel = path.relative(root, resolved).split(path.sep).join("/");
234
+ } else {
235
+ rel = path.basename(filePath);
236
+ }
237
+ } else {
238
+ rel = filePath.split(path.sep).join("/");
239
+ }
240
+ const raw = fs.readFileSync(filePath, "utf-8");
241
+ const lines = _splitlines(raw);
242
+ const n = lines.length;
243
+ const in_example = new Array(n + 1).fill(false);
244
+ const in_any = new Array(n + 1).fill(false);
245
+ let fence_open = false;
246
+ let fence_marker = "";
247
+ let fence_is_example = false;
248
+ for (let idx = 1; idx <= n; idx++) {
249
+ const text = lines[idx - 1];
250
+ const m = _FENCE.exec(text);
251
+ if (m && !fence_open) {
252
+ fence_open = true;
253
+ fence_marker = m[2][0];
254
+ fence_is_example = m[3] === EXAMPLE_FENCE_LANG;
255
+ in_any[idx] = true;
256
+ in_example[idx] = fence_is_example;
257
+ continue;
258
+ }
259
+ if (fence_open) {
260
+ in_any[idx] = true;
261
+ in_example[idx] = fence_is_example;
262
+ const cm = _FENCE.exec(text);
263
+ if (cm && cm[2][0] === fence_marker && cm[3] === "") {
264
+ fence_open = false;
265
+ fence_is_example = false;
266
+ }
267
+ }
268
+ }
269
+ const pragmas = {};
270
+ const pragmaHashes = {};
271
+ for (const text of lines) {
272
+ _PRAGMA.lastIndex = 0;
273
+ let m;
274
+ while ((m = _PRAGMA.exec(text)) !== null) {
275
+ const check = m[1];
276
+ pragmas[check] = m[2];
277
+ const hashes = (m[3] ?? "").match(/sha256:([0-9a-f]{64})/g) ?? [];
278
+ pragmaHashes[check] = [
279
+ ...pragmaHashes[check] ?? [],
280
+ ...hashes.map((h) => h.slice("sha256:".length))
281
+ ];
282
+ if (m.index === _PRAGMA.lastIndex) {
283
+ _PRAGMA.lastIndex += 1;
284
+ }
285
+ }
286
+ }
287
+ return new ScannedFile(
288
+ filePath,
289
+ rel,
290
+ lines,
291
+ in_example,
292
+ in_any,
293
+ pragmas,
294
+ path_weight(rel),
295
+ pragmaHashes
296
+ );
297
+ }
298
+ function scan_path(filePath, base) {
299
+ const sf = scan_file(filePath);
300
+ let rel;
301
+ const baseResolved = path.resolve(base);
302
+ const fileResolved = path.resolve(filePath);
303
+ if (fileResolved === baseResolved || fileResolved.startsWith(baseResolved + path.sep)) {
304
+ rel = path.relative(baseResolved, fileResolved).split(path.sep).join("/");
305
+ } else {
306
+ rel = path.basename(filePath);
307
+ }
308
+ return new ScannedFile(
309
+ sf.path,
310
+ rel,
311
+ sf.lines,
312
+ sf.in_example_fence,
313
+ sf.in_any_fence,
314
+ sf.pragmas,
315
+ path_weight(rel),
316
+ sf.pragma_hashes
317
+ );
318
+ }
319
+ function _rglobSorted(base) {
320
+ const found = [];
321
+ const walk = (dir) => {
322
+ let entries;
323
+ try {
324
+ entries = fs.readdirSync(dir, { withFileTypes: true });
325
+ } catch {
326
+ return;
327
+ }
328
+ for (const e of entries) {
329
+ const full = path.join(dir, e.name);
330
+ found.push(full);
331
+ if (e.isDirectory()) {
332
+ walk(full);
333
+ }
334
+ }
335
+ };
336
+ walk(base);
337
+ found.sort((a, b) => _pathCmp(a, b));
338
+ return found;
339
+ }
340
+ function _pathCmp(a, b) {
341
+ const pa = a.split(path.sep);
342
+ const pb = b.split(path.sep);
343
+ const len = Math.min(pa.length, pb.length);
344
+ for (let i = 0; i < len; i++) {
345
+ if (pa[i] < pb[i]) return -1;
346
+ if (pa[i] > pb[i]) return 1;
347
+ }
348
+ if (pa.length < pb.length) return -1;
349
+ if (pa.length > pb.length) return 1;
350
+ return 0;
351
+ }
352
+ function* iter_corpus(roots = DEFAULT_SCAN_ROOTS, exts = [".md"], scanBase = ROOT) {
353
+ for (const root of roots) {
354
+ const base = path.join(scanBase, root);
355
+ if (!fs.existsSync(base)) {
356
+ continue;
357
+ }
358
+ for (const p of _rglobSorted(base)) {
359
+ let st;
360
+ try {
361
+ st = fs.statSync(p);
362
+ } catch {
363
+ continue;
364
+ }
365
+ if (st.isFile() && exts.includes(path.extname(p))) {
366
+ yield scan_path(p, scanBase);
367
+ }
368
+ }
369
+ }
370
+ }
371
+ var LEGACY_PRAGMA_CHECK = "legacy-pragma";
372
+ function legacy_pragma_finding(sf, check) {
373
+ return new Finding(
374
+ sf.rel,
375
+ 0,
376
+ LEGACY_PRAGMA_CHECK,
377
+ "LOW",
378
+ `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.`,
379
+ sf.weight
380
+ );
381
+ }
382
+ var CHILD_SCANNED_PREFIX = "scanned: ";
383
+ function report_child_scanned(count) {
384
+ process.stderr.write(`${CHILD_SCANNED_PREFIX}${String(count)}
385
+ `);
386
+ }
387
+ function report(findings, opts) {
388
+ const check_label = opts.check_label;
389
+ if (findings.length === 0) {
390
+ process.stdout.write(`\u2705 ${check_label}: clean (${_corpus_note(opts.scanned_roots)}).
391
+ `);
392
+ return 0;
393
+ }
394
+ const fails = findings.filter((f) => f.is_fail);
395
+ const warns = findings.filter((f) => !f.is_fail);
396
+ const sorted = [...findings].sort((a, b) => {
397
+ const ra = -(SEVERITY_RANK[a.severity] ?? 0);
398
+ const rb = -(SEVERITY_RANK[b.severity] ?? 0);
399
+ if (ra !== rb) return ra - rb;
400
+ if (a.path < b.path) return -1;
401
+ if (a.path > b.path) return 1;
402
+ return a.line - b.line;
403
+ });
404
+ for (const f of sorted) {
405
+ const glyph = f.is_fail ? "\u{1F534}" : "\u26A0\uFE0F";
406
+ const loc = f.line ? `${f.path}:${f.line}` : f.path;
407
+ const wnote = f.weight >= 1 ? "" : ` (weight ${_pyG(f.weight)})`;
408
+ process.stdout.write(` ${glyph} [${f.severity}] ${f.check} \u2014 ${loc}${wnote}: ${f.message}
409
+ `);
410
+ }
411
+ process.stdout.write("\n");
412
+ if (fails.length > 0) {
413
+ process.stdout.write(
414
+ `\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.
415
+ `
416
+ );
417
+ return 1;
418
+ }
419
+ process.stdout.write(`\u26A0\uFE0F ${check_label}: ${warns.length} warning(s), 0 blocking.
420
+ `);
421
+ return 0;
422
+ }
423
+ function _pyG(n) {
424
+ let s = n.toPrecision(6);
425
+ if (s.includes(".")) {
426
+ s = s.replace(/0+$/, "").replace(/\.$/, "");
427
+ }
428
+ return s;
429
+ }
430
+ function _corpus_note(scanned_roots) {
431
+ const roots = scanned_roots && scanned_roots.length > 0 ? scanned_roots : DEFAULT_SCAN_ROOTS;
432
+ return "scanned " + roots.join(", ");
433
+ }
434
+ var PyFloat = class {
435
+ constructor(value) {
436
+ this.value = value;
437
+ }
438
+ value;
439
+ };
440
+ function _isPlainObject(v) {
441
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof PyFloat);
442
+ }
443
+ function _pyJsonStr(s) {
444
+ let out = '"';
445
+ for (const ch of s) {
446
+ const code = ch.codePointAt(0);
447
+ switch (ch) {
448
+ case '"':
449
+ out += '\\"';
450
+ break;
451
+ case "\\":
452
+ out += "\\\\";
453
+ break;
454
+ case "\n":
455
+ out += "\\n";
456
+ break;
457
+ case "\r":
458
+ out += "\\r";
459
+ break;
460
+ case " ":
461
+ out += "\\t";
462
+ break;
463
+ case "\b":
464
+ out += "\\b";
465
+ break;
466
+ case "\f":
467
+ out += "\\f";
468
+ break;
469
+ default:
470
+ if (code < 32) {
471
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
472
+ } else {
473
+ out += ch;
474
+ }
475
+ }
476
+ }
477
+ return out + '"';
478
+ }
479
+ function _pyJsonFloat(n) {
480
+ if (!Number.isFinite(n)) {
481
+ if (Number.isNaN(n)) return "NaN";
482
+ return n > 0 ? "Infinity" : "-Infinity";
483
+ }
484
+ return Number.isInteger(n) ? `${n}.0` : String(n);
485
+ }
486
+ function _pyJsonScalar(value) {
487
+ if (value === null || value === void 0) return "null";
488
+ if (typeof value === "boolean") return value ? "true" : "false";
489
+ if (value instanceof PyFloat) return _pyJsonFloat(value.value);
490
+ if (typeof value === "number") {
491
+ if (!Number.isFinite(value)) {
492
+ if (Number.isNaN(value)) return "NaN";
493
+ return value > 0 ? "Infinity" : "-Infinity";
494
+ }
495
+ return String(value);
496
+ }
497
+ if (typeof value === "string") return _pyJsonStr(value);
498
+ return null;
499
+ }
500
+ function py_json_dumps_indent2(value, depth = 0) {
501
+ return _dumpIndent2(value, depth, true);
502
+ }
503
+ function _dumpIndent2(value, depth, ensureAscii) {
504
+ const scalar = ensureAscii ? _pyJsonScalarAscii(value) : _pyJsonScalar(value);
505
+ if (scalar !== null) {
506
+ return scalar;
507
+ }
508
+ const pad = " ".repeat(2 * (depth + 1));
509
+ const closePad = " ".repeat(2 * depth);
510
+ if (Array.isArray(value)) {
511
+ if (value.length === 0) return "[]";
512
+ const items = value.map((v) => pad + _dumpIndent2(v, depth + 1, ensureAscii));
513
+ return `[
514
+ ${items.join(",\n")}
515
+ ${closePad}]`;
516
+ }
517
+ if (_isPlainObject(value)) {
518
+ const keys = Object.keys(value);
519
+ if (keys.length === 0) return "{}";
520
+ const items = keys.map(
521
+ (k) => `${pad}${ensureAscii ? _pyJsonStrAscii(k) : _pyJsonStr(k)}: ${_dumpIndent2(value[k], depth + 1, ensureAscii)}`
522
+ );
523
+ return `{
524
+ ${items.join(",\n")}
525
+ ${closePad}}`;
526
+ }
527
+ return ensureAscii ? _pyJsonStrAscii(String(value)) : _pyJsonStr(String(value));
528
+ }
529
+ function _pyJsonStrAscii(s) {
530
+ let out = '"';
531
+ for (const ch of s) {
532
+ const code = ch.codePointAt(0);
533
+ switch (ch) {
534
+ case '"':
535
+ out += '\\"';
536
+ break;
537
+ case "\\":
538
+ out += "\\\\";
539
+ break;
540
+ case "\n":
541
+ out += "\\n";
542
+ break;
543
+ case "\r":
544
+ out += "\\r";
545
+ break;
546
+ case " ":
547
+ out += "\\t";
548
+ break;
549
+ case "\b":
550
+ out += "\\b";
551
+ break;
552
+ case "\f":
553
+ out += "\\f";
554
+ break;
555
+ default:
556
+ if (code < 32) {
557
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
558
+ } else if (code < 127) {
559
+ out += ch;
560
+ } else if (code <= 65535) {
561
+ out += `\\u${code.toString(16).padStart(4, "0")}`;
562
+ } else {
563
+ const cp = code - 65536;
564
+ const hi = 55296 + (cp >> 10);
565
+ const lo = 56320 + (cp & 1023);
566
+ out += `\\u${hi.toString(16).padStart(4, "0")}\\u${lo.toString(16).padStart(4, "0")}`;
567
+ }
568
+ }
569
+ }
570
+ return out + '"';
571
+ }
572
+ function _pyJsonScalarAscii(value) {
573
+ if (typeof value === "string") return _pyJsonStrAscii(value);
574
+ return _pyJsonScalar(value);
575
+ }
576
+
577
+ // src/scripts/lint_hidden_unicode.ts
578
+ import * as fs2 from "node:fs";
579
+ import * as path2 from "node:path";
580
+ import { spawnSync } from "node:child_process";
581
+ import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
582
+ var CHECK = "hidden-unicode";
583
+ var _BIDI = /* @__PURE__ */ new Set([
584
+ 8234,
585
+ 8235,
586
+ 8236,
587
+ 8237,
588
+ 8238,
589
+ 8294,
590
+ 8295,
591
+ 8296,
592
+ 8297,
593
+ 8206,
594
+ 8207,
595
+ 1564
596
+ ]);
597
+ var _ZERO_WIDTH = /* @__PURE__ */ new Set([8203, 8204, 8205, 8288, 65279, 173]);
598
+ var _DEPRECATED = /* @__PURE__ */ new Set([
599
+ 8298,
600
+ 8299,
601
+ 8300,
602
+ 8301,
603
+ 8302,
604
+ 8303,
605
+ 65529,
606
+ 65530,
607
+ 65531
608
+ ]);
609
+ function _classify(cp) {
610
+ if (_BIDI.has(cp)) {
611
+ return "bidi-control";
612
+ }
613
+ if (_ZERO_WIDTH.has(cp)) {
614
+ return "zero-width";
615
+ }
616
+ if (cp >= 917504 && cp <= 917631) {
617
+ return "unicode-tag";
618
+ }
619
+ if (_DEPRECATED.has(cp)) {
620
+ return "deprecated-format";
621
+ }
622
+ if (cp >= 57344 && cp <= 63743 || cp >= 983040 && cp <= 1048573 || cp >= 1048576 && cp <= 1114109) {
623
+ return "private-use-area";
624
+ }
625
+ if ((cp >= 0 && cp <= 31 || cp >= 127 && cp <= 159) && cp !== 9 && cp !== 10 && cp !== 13) {
626
+ return "control-char";
627
+ }
628
+ return null;
629
+ }
630
+ var _VS_LO = 917760;
631
+ var _VS_HI = 917999;
632
+ function _isVS(cp) {
633
+ return cp >= _VS_LO && cp <= _VS_HI;
634
+ }
635
+ var _CP_NAME = {
636
+ 173: "SOFT HYPHEN",
637
+ 1564: "ARABIC LETTER MARK",
638
+ 8203: "ZERO WIDTH SPACE",
639
+ 8204: "ZERO WIDTH NON-JOINER",
640
+ 8205: "ZERO WIDTH JOINER",
641
+ 8206: "LEFT-TO-RIGHT MARK",
642
+ 8207: "RIGHT-TO-LEFT MARK",
643
+ 8234: "LEFT-TO-RIGHT EMBEDDING",
644
+ 8235: "RIGHT-TO-LEFT EMBEDDING",
645
+ 8236: "POP DIRECTIONAL FORMATTING",
646
+ 8237: "LEFT-TO-RIGHT OVERRIDE",
647
+ 8238: "RIGHT-TO-LEFT OVERRIDE",
648
+ 8288: "WORD JOINER",
649
+ 8294: "LEFT-TO-RIGHT ISOLATE",
650
+ 8295: "RIGHT-TO-LEFT ISOLATE",
651
+ 8296: "FIRST STRONG ISOLATE",
652
+ 8297: "POP DIRECTIONAL ISOLATE",
653
+ 8298: "INHIBIT SYMMETRIC SWAPPING",
654
+ 8299: "ACTIVATE SYMMETRIC SWAPPING",
655
+ 8300: "INHIBIT ARABIC FORM SHAPING",
656
+ 8301: "ACTIVATE ARABIC FORM SHAPING",
657
+ 8302: "NATIONAL DIGIT SHAPES",
658
+ 8303: "NOMINAL DIGIT SHAPES",
659
+ 65279: "ZERO WIDTH NO-BREAK SPACE",
660
+ 65529: "INTERLINEAR ANNOTATION ANCHOR",
661
+ 65530: "INTERLINEAR ANNOTATION SEPARATOR",
662
+ 65531: "INTERLINEAR ANNOTATION TERMINATOR",
663
+ 917505: "LANGUAGE TAG",
664
+ 917536: "TAG SPACE",
665
+ 917537: "TAG EXCLAMATION MARK",
666
+ 917538: "TAG QUOTATION MARK",
667
+ 917539: "TAG NUMBER SIGN",
668
+ 917540: "TAG DOLLAR SIGN",
669
+ 917541: "TAG PERCENT SIGN",
670
+ 917542: "TAG AMPERSAND",
671
+ 917543: "TAG APOSTROPHE",
672
+ 917544: "TAG LEFT PARENTHESIS",
673
+ 917545: "TAG RIGHT PARENTHESIS",
674
+ 917546: "TAG ASTERISK",
675
+ 917547: "TAG PLUS SIGN",
676
+ 917548: "TAG COMMA",
677
+ 917549: "TAG HYPHEN-MINUS",
678
+ 917550: "TAG FULL STOP",
679
+ 917551: "TAG SOLIDUS",
680
+ 917552: "TAG DIGIT ZERO",
681
+ 917553: "TAG DIGIT ONE",
682
+ 917554: "TAG DIGIT TWO",
683
+ 917555: "TAG DIGIT THREE",
684
+ 917556: "TAG DIGIT FOUR",
685
+ 917557: "TAG DIGIT FIVE",
686
+ 917558: "TAG DIGIT SIX",
687
+ 917559: "TAG DIGIT SEVEN",
688
+ 917560: "TAG DIGIT EIGHT",
689
+ 917561: "TAG DIGIT NINE",
690
+ 917562: "TAG COLON",
691
+ 917563: "TAG SEMICOLON",
692
+ 917564: "TAG LESS-THAN SIGN",
693
+ 917565: "TAG EQUALS SIGN",
694
+ 917566: "TAG GREATER-THAN SIGN",
695
+ 917567: "TAG QUESTION MARK",
696
+ 917568: "TAG COMMERCIAL AT",
697
+ 917569: "TAG LATIN CAPITAL LETTER A",
698
+ 917570: "TAG LATIN CAPITAL LETTER B",
699
+ 917571: "TAG LATIN CAPITAL LETTER C",
700
+ 917572: "TAG LATIN CAPITAL LETTER D",
701
+ 917573: "TAG LATIN CAPITAL LETTER E",
702
+ 917574: "TAG LATIN CAPITAL LETTER F",
703
+ 917575: "TAG LATIN CAPITAL LETTER G",
704
+ 917576: "TAG LATIN CAPITAL LETTER H",
705
+ 917577: "TAG LATIN CAPITAL LETTER I",
706
+ 917578: "TAG LATIN CAPITAL LETTER J",
707
+ 917579: "TAG LATIN CAPITAL LETTER K",
708
+ 917580: "TAG LATIN CAPITAL LETTER L",
709
+ 917581: "TAG LATIN CAPITAL LETTER M",
710
+ 917582: "TAG LATIN CAPITAL LETTER N",
711
+ 917583: "TAG LATIN CAPITAL LETTER O",
712
+ 917584: "TAG LATIN CAPITAL LETTER P",
713
+ 917585: "TAG LATIN CAPITAL LETTER Q",
714
+ 917586: "TAG LATIN CAPITAL LETTER R",
715
+ 917587: "TAG LATIN CAPITAL LETTER S",
716
+ 917588: "TAG LATIN CAPITAL LETTER T",
717
+ 917589: "TAG LATIN CAPITAL LETTER U",
718
+ 917590: "TAG LATIN CAPITAL LETTER V",
719
+ 917591: "TAG LATIN CAPITAL LETTER W",
720
+ 917592: "TAG LATIN CAPITAL LETTER X",
721
+ 917593: "TAG LATIN CAPITAL LETTER Y",
722
+ 917594: "TAG LATIN CAPITAL LETTER Z",
723
+ 917595: "TAG LEFT SQUARE BRACKET",
724
+ 917596: "TAG REVERSE SOLIDUS",
725
+ 917597: "TAG RIGHT SQUARE BRACKET",
726
+ 917598: "TAG CIRCUMFLEX ACCENT",
727
+ 917599: "TAG LOW LINE",
728
+ 917600: "TAG GRAVE ACCENT",
729
+ 917601: "TAG LATIN SMALL LETTER A",
730
+ 917602: "TAG LATIN SMALL LETTER B",
731
+ 917603: "TAG LATIN SMALL LETTER C",
732
+ 917604: "TAG LATIN SMALL LETTER D",
733
+ 917605: "TAG LATIN SMALL LETTER E",
734
+ 917606: "TAG LATIN SMALL LETTER F",
735
+ 917607: "TAG LATIN SMALL LETTER G",
736
+ 917608: "TAG LATIN SMALL LETTER H",
737
+ 917609: "TAG LATIN SMALL LETTER I",
738
+ 917610: "TAG LATIN SMALL LETTER J",
739
+ 917611: "TAG LATIN SMALL LETTER K",
740
+ 917612: "TAG LATIN SMALL LETTER L",
741
+ 917613: "TAG LATIN SMALL LETTER M",
742
+ 917614: "TAG LATIN SMALL LETTER N",
743
+ 917615: "TAG LATIN SMALL LETTER O",
744
+ 917616: "TAG LATIN SMALL LETTER P",
745
+ 917617: "TAG LATIN SMALL LETTER Q",
746
+ 917618: "TAG LATIN SMALL LETTER R",
747
+ 917619: "TAG LATIN SMALL LETTER S",
748
+ 917620: "TAG LATIN SMALL LETTER T",
749
+ 917621: "TAG LATIN SMALL LETTER U",
750
+ 917622: "TAG LATIN SMALL LETTER V",
751
+ 917623: "TAG LATIN SMALL LETTER W",
752
+ 917624: "TAG LATIN SMALL LETTER X",
753
+ 917625: "TAG LATIN SMALL LETTER Y",
754
+ 917626: "TAG LATIN SMALL LETTER Z",
755
+ 917627: "TAG LEFT CURLY BRACKET",
756
+ 917628: "TAG VERTICAL LINE",
757
+ 917629: "TAG RIGHT CURLY BRACKET",
758
+ 917630: "TAG TILDE",
759
+ 917631: "CANCEL TAG"
760
+ };
761
+ function _cpName(cp) {
762
+ const n = _CP_NAME[cp];
763
+ return n !== void 0 ? n : "<unnamed>";
764
+ }
765
+ function _scan(sf) {
766
+ const pragmaForm = sf.pragma_form(CHECK);
767
+ if (pragmaForm === "legacy") {
768
+ return [legacy_pragma_finding(sf, CHECK)];
769
+ }
770
+ const finish = (hits) => pragmaForm === "bound" ? sf.filter_bound_pragma(CHECK, hits) : hits;
771
+ const out = [];
772
+ for (const [lineno, text] of sf.iter_lines({ skip_example_fence: true })) {
773
+ let vs_run = 0;
774
+ for (const ch of text) {
775
+ const cp = ch.codePointAt(0);
776
+ if (_isVS(cp)) {
777
+ vs_run += 1;
778
+ continue;
779
+ }
780
+ const kind = _classify(cp);
781
+ if (kind) {
782
+ const name = _cpName(cp);
783
+ out.push(
784
+ new Finding(
785
+ sf.rel,
786
+ lineno,
787
+ CHECK,
788
+ "HIGH",
789
+ `${kind} U+${cp.toString(16).toUpperCase().padStart(4, "0")} (${name})`,
790
+ sf.weight
791
+ )
792
+ );
793
+ }
794
+ }
795
+ if (vs_run >= 3) {
796
+ out.push(
797
+ new Finding(
798
+ sf.rel,
799
+ lineno,
800
+ CHECK,
801
+ "HIGH",
802
+ `variation-selector run x${vs_run} (steganography signature)`,
803
+ sf.weight
804
+ )
805
+ );
806
+ }
807
+ }
808
+ return finish(out);
809
+ }
810
+ var _BINARY_EXT = /* @__PURE__ */ new Set([
811
+ "png",
812
+ "jpg",
813
+ "jpeg",
814
+ "gif",
815
+ "ico",
816
+ "webp",
817
+ "avif",
818
+ "bmp",
819
+ "tiff",
820
+ "pdf",
821
+ "woff",
822
+ "woff2",
823
+ "ttf",
824
+ "otf",
825
+ "eot",
826
+ "zip",
827
+ "gz",
828
+ "tgz",
829
+ "bz2",
830
+ "xz",
831
+ "zst",
832
+ "7z",
833
+ "rar",
834
+ "wasm",
835
+ "mp3",
836
+ "mp4",
837
+ "wav",
838
+ "ogg",
839
+ "webm",
840
+ "mov",
841
+ "avi",
842
+ "so",
843
+ "dylib",
844
+ "dll",
845
+ "node",
846
+ "a",
847
+ "o",
848
+ "exe",
849
+ "jar",
850
+ "class",
851
+ "pyc",
852
+ "pyo",
853
+ "db",
854
+ "sqlite",
855
+ "sqlite3",
856
+ "bin",
857
+ "dat",
858
+ "docx",
859
+ "xlsx",
860
+ "pptx",
861
+ "doc",
862
+ "xls",
863
+ "ppt",
864
+ "odt",
865
+ "ods",
866
+ "odp"
867
+ ]);
868
+ var _GENERATED_PREFIXES = [
869
+ "dist/",
870
+ ".augment/",
871
+ ".claude/",
872
+ ".cursor/",
873
+ ".clinerules/",
874
+ // Verbatim captured tool output, not authored text. These are RECORDINGS:
875
+ // escaping a control byte inside one would falsify the record it exists to
876
+ // preserve. If a capture needs to stay grep-readable, the fix is to escape
877
+ // at capture time — never to edit the artifact afterwards.
878
+ "agents/evidence/analysis/",
879
+ // The frozen hostile-encoding corpus. Its positives ARE raw control
880
+ // characters — that is the thing under test, so flagging them would demand
881
+ // escaping the fixtures and thereby delete the test. The corpus has its own,
882
+ // stronger integrity controls: a sha256 freeze plus a scope-guard test that
883
+ // is itself falsified by an out-of-scope fixture. Excluded here because it
884
+ // is DATA, on the same principle as the captures above.
885
+ "internal/bench/corpora/encoding-channels/"
886
+ ];
887
+ function _isGenerated(rel) {
888
+ return _GENERATED_PREFIXES.some((p) => rel.startsWith(p));
889
+ }
890
+ function _isRawControlCodepoint(cp) {
891
+ if (cp === 9 || cp === 10 || cp === 13) return false;
892
+ return cp <= 31 || cp >= 127 && cp <= 159;
893
+ }
894
+ function _extensionOf(rel) {
895
+ const base = rel.slice(rel.lastIndexOf("/") + 1);
896
+ const dot = base.lastIndexOf(".");
897
+ return dot <= 0 ? "" : base.slice(dot + 1).toLowerCase();
898
+ }
899
+ function _trackedFiles() {
900
+ const res = spawnSync("git", ["ls-files", "-z"], {
901
+ cwd: ROOT,
902
+ maxBuffer: 64 * 1024 * 1024
903
+ });
904
+ if (res.status !== 0 || res.stdout === null) return null;
905
+ return res.stdout.toString("utf-8").split("\0").filter((p) => p !== "");
906
+ }
907
+ function _eligibleSourceFiles() {
908
+ const tracked = _trackedFiles();
909
+ if (tracked === null) return null;
910
+ return tracked.filter((rel) => !_isGenerated(rel) && !_BINARY_EXT.has(_extensionOf(rel)));
911
+ }
912
+ function _scanSourceControlBytes(files = null) {
913
+ const tracked = files ?? _eligibleSourceFiles();
914
+ if (tracked === null) return [];
915
+ const out = [];
916
+ for (const rel of tracked) {
917
+ if (_isGenerated(rel)) continue;
918
+ if (_BINARY_EXT.has(_extensionOf(rel))) continue;
919
+ let data;
920
+ try {
921
+ data = fs2.readFileSync(path2.join(ROOT, rel));
922
+ } catch {
923
+ continue;
924
+ }
925
+ try {
926
+ new TextDecoder("utf-8", { fatal: true }).decode(data);
927
+ } catch {
928
+ continue;
929
+ }
930
+ let line = 1;
931
+ for (const ch of data.toString("utf-8")) {
932
+ const cp = ch.codePointAt(0);
933
+ if (cp === void 0) continue;
934
+ if (cp === 10) {
935
+ line += 1;
936
+ continue;
937
+ }
938
+ if (!_isRawControlCodepoint(cp)) continue;
939
+ const hex = cp.toString(16).toUpperCase().padStart(2, "0");
940
+ out.push(
941
+ new Finding(
942
+ rel,
943
+ line,
944
+ CHECK,
945
+ "HIGH",
946
+ `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`,
947
+ 1
948
+ )
949
+ );
950
+ }
951
+ }
952
+ return out;
953
+ }
954
+ function _sanitize(filePath) {
955
+ const raw = fs2.readFileSync(filePath, "utf-8");
956
+ let cleaned = "";
957
+ for (const ch of raw) {
958
+ const cp = ch.codePointAt(0);
959
+ if (_classify(cp) === null && !_isVS(cp)) {
960
+ cleaned += ch;
961
+ }
962
+ }
963
+ cleaned = cleaned.normalize("NFKC");
964
+ const out = filePath + ".sanitized";
965
+ fs2.writeFileSync(out, cleaned, "utf-8");
966
+ return out;
967
+ }
968
+ function _argError(msg) {
969
+ process.stderr.write("usage: lint_hidden_unicode [-h] [--json] [--fix] [--root DIR]\n");
970
+ process.stderr.write(`lint_hidden_unicode: error: ${msg}
971
+ `);
972
+ process.exit(2);
973
+ }
974
+ function parse_args(argv) {
975
+ const out = { json: false, fix: false };
976
+ const extra = [];
977
+ let rootPending = false;
978
+ for (const a of argv) {
979
+ if (rootPending) {
980
+ out.root = a;
981
+ rootPending = false;
982
+ continue;
983
+ }
984
+ if (a === "-h" || a === "--help") {
985
+ process.stdout.write("usage: lint_hidden_unicode [-h] [--json] [--fix] [--root DIR]\n");
986
+ process.exit(0);
987
+ } else if (a === "--json") {
988
+ out.json = true;
989
+ } else if (a === "--fix") {
990
+ out.fix = true;
991
+ } else if (a === "--root" || a.startsWith("--root=")) {
992
+ const eq = a.indexOf("=");
993
+ if (eq !== -1) {
994
+ out.root = a.slice(eq + 1);
995
+ } else {
996
+ rootPending = true;
997
+ }
998
+ } else {
999
+ extra.push(a);
1000
+ }
1001
+ }
1002
+ if (extra.length > 0) {
1003
+ _argError(`unrecognized arguments: ${extra.join(" ")}`);
1004
+ }
1005
+ return out;
1006
+ }
1007
+ function main(argv = null) {
1008
+ const args = parse_args(argv ?? process.argv.slice(2));
1009
+ const scanBase = args.root === void 0 ? ROOT : path2.resolve(args.root);
1010
+ const findings = [];
1011
+ const flagged = /* @__PURE__ */ new Set();
1012
+ let corpusFiles = 0;
1013
+ for (const sf of iter_corpus(DEFAULT_SCAN_ROOTS, [".md"], scanBase)) {
1014
+ corpusFiles += 1;
1015
+ const hits = _scan(sf);
1016
+ for (const h of hits) {
1017
+ findings.push(h);
1018
+ }
1019
+ if (hits.length > 0) {
1020
+ flagged.add(sf.path);
1021
+ }
1022
+ }
1023
+ try {
1024
+ assertScanned({
1025
+ gate: "lint_hidden_unicode",
1026
+ scanned: corpusFiles,
1027
+ units: "corpus file(s)",
1028
+ roots: DEFAULT_SCAN_ROOTS
1029
+ });
1030
+ } catch (exc) {
1031
+ if (exc instanceof DeadScopeError) {
1032
+ process.stderr.write(`\u274C ${exc.message}
1033
+ `);
1034
+ return 1;
1035
+ }
1036
+ throw exc;
1037
+ }
1038
+ const sourceFiles = args.root === void 0 ? _eligibleSourceFiles() : [];
1039
+ for (const h of _scanSourceControlBytes(sourceFiles)) {
1040
+ findings.push(h);
1041
+ }
1042
+ if (!args.json) {
1043
+ const n = sourceFiles === null ? 0 : sourceFiles.length;
1044
+ process.stdout.write(
1045
+ 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)
1046
+ `
1047
+ );
1048
+ }
1049
+ if (args.fix) {
1050
+ for (const p of [...flagged].sort()) {
1051
+ const sanitized = _sanitize(p);
1052
+ const rel = path2.relative(path2.resolve(ROOT), path2.resolve(sanitized));
1053
+ process.stdout.write(` fixed \u2192 ${rel.split(path2.sep).join("/")}
1054
+ `);
1055
+ }
1056
+ }
1057
+ if (args.json) {
1058
+ report_child_scanned(corpusFiles + (sourceFiles === null ? 0 : sourceFiles.length));
1059
+ }
1060
+ if (args.json) {
1061
+ const payload = py_json_dumps_indent2(findings.map((f) => f.toDict()));
1062
+ process.stdout.write(payload + "\n");
1063
+ return findings.some((f) => f.is_fail) ? 1 : 0;
1064
+ }
1065
+ return report(findings, { check_label: "hidden-unicode" });
1066
+ }
1067
+ function _isCliEntry() {
1068
+ if (true) {
1069
+ return false;
1070
+ }
1071
+ if (process.argv[1] === void 0) {
1072
+ return false;
1073
+ }
1074
+ const argvUrl = pathToFileURL(path2.resolve(process.argv[1])).href;
1075
+ if (import.meta.url === argvUrl) {
1076
+ return true;
1077
+ }
1078
+ try {
1079
+ const here = fs2.realpathSync(fileURLToPath2(import.meta.url));
1080
+ const argv = fs2.realpathSync(path2.resolve(process.argv[1]));
1081
+ return here === argv;
1082
+ } catch {
1083
+ return false;
1084
+ }
1085
+ }
1086
+ if (_isCliEntry()) {
1087
+ process.exitCode = main();
1088
+ }
1089
+
1090
+ // src/scripts/_lib/confusables.ts
1091
+ var MIN_LETTERS = 3;
1092
+ var _LATIN = new RegExp("\\p{Script=Latin}", "u");
1093
+ var _CYRILLIC = new RegExp("\\p{Script=Cyrillic}", "u");
1094
+ var _GREEK = new RegExp("\\p{Script=Greek}", "u");
1095
+ var _LETTER = new RegExp("\\p{L}", "u");
1096
+ var TOKEN_RE = /[\p{L}\p{M}\p{Nd}]+/gu;
1097
+ var CONFUSABLE_FOREIGN = /* @__PURE__ */ new Set([
1098
+ // Cyrillic lowercase ↔ Latin
1099
+ 1072,
1100
+ 1077,
1101
+ 1086,
1102
+ 1088,
1103
+ 1089,
1104
+ 1091,
1105
+ 1093,
1106
+ 1109,
1107
+ 1110,
1108
+ 1112,
1109
+ 1211,
1110
+ 1281,
1111
+ 1307,
1112
+ 1399,
1113
+ // Cyrillic uppercase ↔ Latin
1114
+ 1040,
1115
+ 1042,
1116
+ 1045,
1117
+ 1050,
1118
+ 1052,
1119
+ 1053,
1120
+ 1054,
1121
+ 1056,
1122
+ 1057,
1123
+ 1058,
1124
+ 1059,
1125
+ 1061,
1126
+ 1029,
1127
+ 1030,
1128
+ 1032,
1129
+ // Greek lowercase ↔ Latin
1130
+ 945,
1131
+ 949,
1132
+ 959,
1133
+ 957,
1134
+ 961,
1135
+ 965,
1136
+ 967,
1137
+ 954,
1138
+ // Greek uppercase ↔ Latin (only those with a real Latin twin)
1139
+ 913,
1140
+ 914,
1141
+ 917,
1142
+ 918,
1143
+ 919,
1144
+ 921,
1145
+ 922,
1146
+ 924,
1147
+ 925,
1148
+ 927,
1149
+ 929,
1150
+ 932,
1151
+ 933,
1152
+ 935,
1153
+ 914
1154
+ ]);
1155
+ function _script_of(ch) {
1156
+ if (_LATIN.test(ch)) return "latin";
1157
+ if (_CYRILLIC.test(ch)) return "cyrillic";
1158
+ if (_GREEK.test(ch)) return "greek";
1159
+ return "other";
1160
+ }
1161
+ function classifyToken(token) {
1162
+ let latin = 0;
1163
+ let cyrillic = 0;
1164
+ let greek = 0;
1165
+ let letters = 0;
1166
+ for (const ch of token) {
1167
+ if (!_LETTER.test(ch)) continue;
1168
+ letters += 1;
1169
+ const script = _script_of(ch);
1170
+ if (script === "latin") {
1171
+ latin += 1;
1172
+ } else if (script === "cyrillic" || script === "greek") {
1173
+ if (CONFUSABLE_FOREIGN.has(ch.codePointAt(0))) {
1174
+ if (script === "cyrillic") cyrillic += 1;
1175
+ else greek += 1;
1176
+ }
1177
+ }
1178
+ }
1179
+ if (letters < MIN_LETTERS) return null;
1180
+ const foreign = cyrillic + greek;
1181
+ if (latin === 0 || foreign === 0) return null;
1182
+ if (latin <= foreign) return null;
1183
+ const which = cyrillic > 0 && greek > 0 ? "cyrillic+greek" : cyrillic > 0 ? "cyrillic" : "greek";
1184
+ return `latin+${which} (${latin} latin / ${foreign} foreign-confusable)`;
1185
+ }
1186
+
1187
+ export {
1188
+ ROOT,
1189
+ DEFAULT_SCAN_ROOTS,
1190
+ Finding,
1191
+ scan_file,
1192
+ iter_corpus,
1193
+ legacy_pragma_finding,
1194
+ report_child_scanned,
1195
+ report,
1196
+ py_json_dumps_indent2,
1197
+ _classify,
1198
+ _scan,
1199
+ TOKEN_RE,
1200
+ classifyToken
1201
+ };