sinapse-ai 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/.claude/hooks/mind-clone-governance.py +212 -212
  2. package/.claude/hooks/read-protection.py +152 -152
  3. package/.claude/hooks/slug-validation.py +175 -175
  4. package/.claude/hooks/sql-governance.py +183 -183
  5. package/.claude/rules/documentation-first.md +1 -1
  6. package/.claude/rules/hook-governance.md +1 -1
  7. package/.claude/rules/mandatory-delegation.md +1 -1
  8. package/.claude/rules/project-intelligence.md +1 -1
  9. package/.codex/agents/analyst.md +4 -371
  10. package/.codex/agents/animations-orqx.md +4 -57
  11. package/.codex/agents/architect.md +4 -560
  12. package/.codex/agents/brand-orqx.md +4 -95
  13. package/.codex/agents/claude-mastery-chief.md +4 -0
  14. package/.codex/agents/cloning-orqx.md +4 -70
  15. package/.codex/agents/commercial-orqx.md +4 -67
  16. package/.codex/agents/config-engineer.md +2 -2
  17. package/.codex/agents/content-orqx.md +4 -77
  18. package/.codex/agents/copy-orqx.md +4 -65
  19. package/.codex/agents/cost-optimizer.md +4 -0
  20. package/.codex/agents/council-orqx.md +4 -68
  21. package/.codex/agents/courses-orqx.md +4 -64
  22. package/.codex/agents/cro-persuasion.md +4 -0
  23. package/.codex/agents/cyber-orqx.md +4 -67
  24. package/.codex/agents/data-engineer.md +4 -542
  25. package/.codex/agents/design-orqx.md +4 -65
  26. package/.codex/agents/design-system.md +4 -210
  27. package/.codex/agents/developer.md +4 -666
  28. package/.codex/agents/devops.md +4 -668
  29. package/.codex/agents/finance-orqx.md +4 -57
  30. package/.codex/agents/fiscal-compliance-br.md +4 -0
  31. package/.codex/agents/forecast-strategist.md +4 -0
  32. package/.codex/agents/growth-orqx.md +4 -75
  33. package/.codex/agents/hooks-architect.md +2 -2
  34. package/.codex/agents/mcp-integrator.md +2 -2
  35. package/.codex/agents/paidmedia-orqx.md +4 -67
  36. package/.codex/agents/platform-aesthetic-director.md +4 -0
  37. package/.codex/agents/premium-packaging-strategist.md +4 -0
  38. package/.codex/agents/product-lead.md +4 -371
  39. package/.codex/agents/product-orqx.md +4 -57
  40. package/.codex/agents/product-surface-director.md +4 -0
  41. package/.codex/agents/project-integrator.md +2 -2
  42. package/.codex/agents/project-lead.md +4 -414
  43. package/.codex/agents/quality-gate.md +4 -547
  44. package/.codex/agents/research-orqx.md +4 -67
  45. package/.codex/agents/roadmap-sentinel.md +2 -2
  46. package/.codex/agents/skill-craftsman.md +2 -2
  47. package/.codex/agents/snps-orqx.md +4 -684
  48. package/.codex/agents/sop-extractor.md +4 -61
  49. package/.codex/agents/sprint-lead.md +4 -324
  50. package/.codex/agents/squad-creator.md +4 -402
  51. package/.codex/agents/storytelling-orqx.md +4 -65
  52. package/.codex/agents/swarm-orqx.md +4 -64
  53. package/.codex/agents/ux-design-expert.md +4 -532
  54. package/.codex/agents/ux-designer.md +4 -124
  55. package/.codex/command-registry.json +9 -9
  56. package/.codex/delegation-matrix.json +375 -839
  57. package/.codex/delegation-parity.json +658 -0
  58. package/.codex/handoff-packet.parity.schema.json +148 -0
  59. package/.codex/handoff-packet.template.json +26 -0
  60. package/.codex/instructions.md +8 -8
  61. package/.codex/scripts/resolve-codex-agent.js +482 -0
  62. package/.codex/scripts/resolve-codex-command.js +75 -12
  63. package/.codex/scripts/resolve-codex-delegation.js +131 -92
  64. package/.codex/skills/sinapse-claude/SKILL.md +3 -3
  65. package/.codex/skills/sinapse-po/SKILL.md +1 -1
  66. package/.codex/tasks/resolve-sinapse-conflict.md +1 -1
  67. package/.sinapse-ai/constitution.md +5 -5
  68. package/.sinapse-ai/core/doctor/checks/git-hooks.js +163 -19
  69. package/.sinapse-ai/core/events/dashboard-emitter.js +30 -9
  70. package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
  71. package/.sinapse-ai/core/synapse/engine.js +15 -0
  72. package/.sinapse-ai/core/ui/observability-panel.js +240 -0
  73. package/.sinapse-ai/core-config.yaml +0 -20
  74. package/.sinapse-ai/data/entity-registry.yaml +185 -236
  75. package/.sinapse-ai/development/agents/snps-orqx.md +16 -26
  76. package/.sinapse-ai/development/tasks/build-autonomous.md +11 -1
  77. package/.sinapse-ai/development/tasks/build-resume.md +8 -0
  78. package/.sinapse-ai/development/tasks/build-status.md +8 -0
  79. package/.sinapse-ai/development/tasks/build.md +8 -0
  80. package/.sinapse-ai/development/tasks/cleanup-worktrees.md +8 -1
  81. package/.sinapse-ai/development/tasks/gotcha.md +8 -0
  82. package/.sinapse-ai/development/tasks/gotchas.md +8 -0
  83. package/.sinapse-ai/development/tasks/ids-health.md +14 -6
  84. package/.sinapse-ai/development/tasks/list-mcps.md +15 -0
  85. package/.sinapse-ai/development/tasks/merge-worktree.md +8 -1
  86. package/.sinapse-ai/development/tasks/qa-review-build.md +18 -0
  87. package/.sinapse-ai/development/tasks/remove-mcp.md +8 -1
  88. package/.sinapse-ai/development/tasks/validate-agents.md +26 -14
  89. package/.sinapse-ai/development/templates/service-template/README.md.hbs +159 -159
  90. package/.sinapse-ai/development/templates/service-template/__tests__/index.test.ts.hbs +238 -238
  91. package/.sinapse-ai/development/templates/service-template/client.ts.hbs +404 -404
  92. package/.sinapse-ai/development/templates/service-template/errors.ts.hbs +183 -183
  93. package/.sinapse-ai/development/templates/service-template/index.ts.hbs +121 -121
  94. package/.sinapse-ai/development/templates/service-template/package.json.hbs +88 -88
  95. package/.sinapse-ai/development/templates/service-template/types.ts.hbs +146 -146
  96. package/.sinapse-ai/development/templates/squad-template/LICENSE +22 -22
  97. package/.sinapse-ai/git-hooks/lib/framework-guard.js +258 -0
  98. package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +355 -0
  99. package/.sinapse-ai/git-hooks/lib/staged-secret-scan.js +179 -0
  100. package/.sinapse-ai/git-hooks/lib/staged-sql-guard.js +204 -0
  101. package/.sinapse-ai/git-hooks/post-commit +28 -0
  102. package/.sinapse-ai/git-hooks/pre-commit +81 -0
  103. package/.sinapse-ai/git-hooks/pre-push +83 -0
  104. package/.sinapse-ai/hooks/ids-post-commit.js +13 -11
  105. package/.sinapse-ai/hooks/ids-pre-push.js +9 -7
  106. package/.sinapse-ai/infrastructure/scripts/codex-parity/resolve.js +161 -0
  107. package/.sinapse-ai/infrastructure/scripts/dashboard-status-writer.js +6 -2
  108. package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +65 -68
  109. package/.sinapse-ai/infrastructure/scripts/sync-codex-local-first.js +156 -1
  110. package/.sinapse-ai/infrastructure/scripts/validate-codex-delegation.js +1 -4
  111. package/.sinapse-ai/infrastructure/scripts/validate-codex-integration.js +41 -5
  112. package/.sinapse-ai/infrastructure/templates/coderabbit.yaml.template +280 -280
  113. package/.sinapse-ai/infrastructure/templates/config/env.example +16 -16
  114. package/.sinapse-ai/infrastructure/templates/config/gitignore-additions.tmpl +59 -59
  115. package/.sinapse-ai/infrastructure/templates/github/CODEOWNERS.template +12 -12
  116. package/.sinapse-ai/infrastructure/templates/github-workflows/ci.yml.template +170 -170
  117. package/.sinapse-ai/infrastructure/templates/github-workflows/pr-automation.yml.template +331 -331
  118. package/.sinapse-ai/infrastructure/templates/github-workflows/release.yml.template +197 -197
  119. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +19 -19
  120. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-node.tmpl +86 -86
  121. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-python.tmpl +146 -146
  122. package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-sinapse-base.tmpl +64 -64
  123. package/.sinapse-ai/infrastructure/templates/safe-collab/CODEOWNERS.template +16 -16
  124. package/.sinapse-ai/infrastructure/templates/sinapse-sync.yaml.template +183 -183
  125. package/.sinapse-ai/install-manifest.yaml +112 -164
  126. package/.sinapse-ai/local-config.yaml.template +65 -65
  127. package/.sinapse-ai/product/templates/adr.hbs +126 -126
  128. package/.sinapse-ai/product/templates/dbdr.hbs +242 -242
  129. package/.sinapse-ai/product/templates/epic.hbs +213 -213
  130. package/.sinapse-ai/product/templates/ide-rules/codex-rules.md +30 -0
  131. package/.sinapse-ai/product/templates/pmdr.hbs +187 -187
  132. package/.sinapse-ai/product/templates/prd-v2.0.hbs +217 -217
  133. package/.sinapse-ai/product/templates/prd.hbs +202 -202
  134. package/.sinapse-ai/product/templates/statusline/statusline-script.js +31 -8
  135. package/.sinapse-ai/product/templates/statusline/track-agent-clear.cjs +79 -0
  136. package/.sinapse-ai/product/templates/statusline/track-agent.cjs +218 -0
  137. package/.sinapse-ai/product/templates/story.hbs +264 -264
  138. package/.sinapse-ai/product/templates/task.hbs +171 -171
  139. package/.sinapse-ai/product/templates/tmpl-comment-on-examples.sql +159 -159
  140. package/.sinapse-ai/product/templates/tmpl-migration-script.sql +92 -92
  141. package/.sinapse-ai/product/templates/tmpl-rls-granular-policies.sql +105 -105
  142. package/.sinapse-ai/product/templates/tmpl-rls-kiss-policy.sql +11 -11
  143. package/.sinapse-ai/product/templates/tmpl-rls-roles.sql +136 -136
  144. package/.sinapse-ai/product/templates/tmpl-rls-simple.sql +78 -78
  145. package/.sinapse-ai/product/templates/tmpl-rls-tenant.sql +153 -153
  146. package/.sinapse-ai/product/templates/tmpl-rollback-script.sql +78 -78
  147. package/.sinapse-ai/product/templates/tmpl-seed-data.sql +141 -141
  148. package/.sinapse-ai/product/templates/tmpl-smoke-test.sql +17 -17
  149. package/.sinapse-ai/product/templates/tmpl-staging-copy-merge.sql +140 -140
  150. package/.sinapse-ai/product/templates/tmpl-stored-proc.sql +141 -141
  151. package/.sinapse-ai/product/templates/tmpl-trigger.sql +153 -153
  152. package/.sinapse-ai/product/templates/tmpl-view-materialized.sql +134 -134
  153. package/.sinapse-ai/product/templates/tmpl-view.sql +178 -178
  154. package/AGENTS.md +193 -0
  155. package/CHANGELOG.md +1247 -0
  156. package/LICENSE +63 -63
  157. package/README.en.md +17 -18
  158. package/README.md +18 -19
  159. package/bin/cli.js +1 -1
  160. package/bin/commands/install.js +194 -22
  161. package/bin/commands/status.js +14 -1
  162. package/bin/commands/uninstall.js +2 -2
  163. package/bin/commands/update.js +52 -0
  164. package/bin/lib/setup-statusline.js +191 -0
  165. package/bin/sinapse-init.js +11 -83
  166. package/bin/utils/framework-guard.js +17 -4
  167. package/bin/utils/secret-scanner-core.js +109 -7
  168. package/bin/utils/staged-sql-guard.js +204 -0
  169. package/bin/utils/validate-publish.js +63 -0
  170. package/docs/agent-reference-guide.md +5 -7
  171. package/docs/framework/agent-prefix-convention.md +58 -0
  172. package/docs/framework/architecture-overview.md +4 -4
  173. package/docs/framework/collaboration-activation.md +45 -0
  174. package/docs/framework/guiding-principles.md +9 -9
  175. package/docs/getting-started.md +1 -1
  176. package/docs/guides/agent-reference.md +1 -1
  177. package/docs/guides/codex-config.md +4 -5
  178. package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
  179. package/docs/security/overview.md +1 -1
  180. package/package.json +16 -12
  181. package/packages/installer/src/index.js +26 -0
  182. package/packages/installer/src/installer/git-hooks-installer.js +211 -47
  183. package/packages/installer/src/installer/sinapse-ai-installer.js +71 -0
  184. package/packages/installer/src/wizard/feedback.js +1 -1
  185. package/packages/installer/src/wizard/ide-config-generator.js +26 -26
  186. package/packages/installer/src/wizard/index.js +53 -4
  187. package/packages/sinapse-install/bin/edmcp.js +0 -0
  188. package/packages/sinapse-install/bin/sinapse-install.js +0 -0
  189. package/scripts/audit-tasks.cjs +112 -91
  190. package/scripts/check-markdown-links.py +352 -352
  191. package/scripts/prepare-hooks.js +58 -0
  192. package/scripts/regenerate-orqx-stubs.ps1 +2 -3
  193. package/scripts/sync-counts.js +10 -2
  194. package/scripts/sync-squad-yaml-components.js +108 -6
  195. package/scripts/validate-agents-md.js +128 -0
  196. package/scripts/validate-all.js +1 -0
  197. package/scripts/validate-squad-orqx.js +19 -9
  198. package/sinapse/agents/sinapse-orqx.md +16 -26
  199. package/sinapse/agents/snps-orqx.md +15 -25
  200. package/sinapse/knowledge-base/routing-catalog.md +1 -1
  201. package/sinapse/tasks/diagnose-and-route.md +1 -1
  202. package/sinapse/tasks/squad-status-report.md +1 -1
  203. package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
  204. package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
  205. package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
  206. package/squads/claude-code-mastery/squad.yaml +8 -0
  207. package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
  208. package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
  209. package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
  210. package/squads/squad-animations/README.md +1 -1
  211. package/squads/squad-animations/squad.yaml +1 -1
  212. package/squads/squad-brand/squad.yaml +1 -1
  213. package/squads/squad-cloning/README.md +1 -1
  214. package/squads/squad-cloning/squad.yaml +1 -1
  215. package/squads/squad-commercial/README.md +1 -1
  216. package/squads/squad-commercial/squad.yaml +2 -3
  217. package/squads/squad-content/README.md +1 -1
  218. package/squads/squad-content/squad.yaml +1 -1
  219. package/squads/squad-copy/README.md +1 -1
  220. package/squads/squad-copy/squad.yaml +2 -3
  221. package/squads/squad-council/README.md +1 -1
  222. package/squads/squad-courses/README.md +1 -1
  223. package/squads/squad-courses/squad.yaml +1 -1
  224. package/squads/squad-cybersecurity/README.md +1 -1
  225. package/squads/squad-cybersecurity/squad.yaml +2 -3
  226. package/squads/squad-design/README.md +1 -1
  227. package/squads/{squad-artdir → squad-design}/agents/cro-persuasion.md +1 -1
  228. package/squads/{squad-artdir → squad-design}/agents/platform-aesthetic-director.md +2 -2
  229. package/squads/{squad-artdir → squad-design}/agents/premium-packaging-strategist.md +2 -2
  230. package/squads/{squad-artdir → squad-design}/agents/product-surface-director.md +3 -3
  231. package/squads/squad-design/squad.yaml +6 -3
  232. package/squads/squad-finance/README.md +1 -1
  233. package/squads/squad-finance/squad.yaml +7 -1
  234. package/squads/squad-growth/README.md +1 -1
  235. package/squads/squad-growth/squad.yaml +1 -1
  236. package/squads/squad-paidmedia/README.md +1 -1
  237. package/squads/squad-paidmedia/squad.yaml +2 -3
  238. package/squads/squad-product/README.md +1 -1
  239. package/squads/squad-product/squad.yaml +1 -1
  240. package/squads/squad-research/README.md +1 -1
  241. package/squads/squad-research/squad.yaml +2 -3
  242. package/squads/squad-storytelling/README.md +1 -1
  243. package/squads/squad-storytelling/squad.yaml +2 -3
  244. package/.codex/agents/brad-frost.md +0 -46
  245. package/.codex/agents/claude-orqx.md +0 -72
  246. package/.codex/agents/copy-chief.md +0 -162
  247. package/.codex/agents/cyber-chief.md +0 -169
  248. package/.codex/agents/dan-mall.md +0 -43
  249. package/.codex/agents/data-chief.md +0 -198
  250. package/.codex/agents/dave-malouf.md +0 -43
  251. package/.codex/agents/db-sage.md +0 -152
  252. package/.codex/agents/design-chief.md +0 -226
  253. package/.codex/agents/dev.md +0 -102
  254. package/.codex/agents/legal-chief.md +0 -199
  255. package/.codex/agents/nano-banana-generator.md +0 -42
  256. package/.codex/agents/pm.md +0 -81
  257. package/.codex/agents/po.md +0 -85
  258. package/.codex/agents/qa.md +0 -98
  259. package/.codex/agents/sm.md +0 -77
  260. package/.codex/agents/squad-chief.md +0 -1553
  261. package/.codex/agents/squad.md +0 -66
  262. package/.codex/agents/story-chief.md +0 -180
  263. package/.codex/agents/tools-orqx.md +0 -219
  264. package/.codex/agents/traffic-masters-chief.md +0 -211
  265. package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
  266. package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
  267. package/.sinapse-ai/data/registry-update-log.jsonl +0 -158
  268. package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +0 -298
  269. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +0 -121
  270. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +0 -119
  271. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +0 -191
  272. package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +0 -448
  273. package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
  274. package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
  275. package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
  276. package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
  277. package/.sinapse-ai/monitor/hooks/lib/__init__.py +0 -2
  278. package/.sinapse-ai/monitor/hooks/lib/enrich.py +0 -59
  279. package/.sinapse-ai/monitor/hooks/lib/send_event.py +0 -48
  280. package/.sinapse-ai/monitor/hooks/notification.py +0 -30
  281. package/.sinapse-ai/monitor/hooks/post_tool_use.py +0 -46
  282. package/.sinapse-ai/monitor/hooks/pre_compact.py +0 -30
  283. package/.sinapse-ai/monitor/hooks/pre_tool_use.py +0 -41
  284. package/.sinapse-ai/monitor/hooks/stop.py +0 -30
  285. package/.sinapse-ai/monitor/hooks/subagent_stop.py +0 -30
  286. package/.sinapse-ai/monitor/hooks/user_prompt_submit.py +0 -39
  287. package/.sinapse-ai/product/templates/statusline/track-agent.sh +0 -69
  288. package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
  289. package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
  290. package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
  291. package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
  292. package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
  293. package/bin/sinapse-graph.js +0 -19
  294. package/docs/codex-integration-process.md +0 -22
  295. package/docs/codex-parity-program.md +0 -27
  296. package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
  297. package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
  298. package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
  299. package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -383
  300. package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
  301. package/packages/installer/tests/unit/config-validator.test.js +0 -315
  302. package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
  303. package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -636
  304. package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
  305. package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -186
  306. package/packages/installer/tests/unit/env-template.test.js +0 -187
  307. package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
  308. package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
  309. package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
  310. package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
  311. package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
  312. package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
  313. package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
  314. package/packages/sinapse-install/tests/unit/chrome-brain.smoke.test.js +0 -66
  315. package/scripts/install-monitor-hooks.sh +0 -82
  316. package/squads/squad-artdir/README.md +0 -90
  317. package/squads/squad-artdir/agents/accessibility-guardian.md +0 -184
  318. package/squads/squad-artdir/agents/artdir-orqx.md +0 -222
  319. package/squads/squad-artdir/agents/color-psychologist.md +0 -166
  320. package/squads/squad-artdir/agents/design-system-architect.md +0 -100
  321. package/squads/squad-artdir/agents/ia-architect.md +0 -169
  322. package/squads/squad-artdir/agents/interaction-designer.md +0 -162
  323. package/squads/squad-artdir/agents/layout-engineer.md +0 -163
  324. package/squads/squad-artdir/agents/motion-architect.md +0 -185
  325. package/squads/squad-artdir/agents/type-systemist.md +0 -138
  326. package/squads/squad-artdir/agents/visual-strategist.md +0 -127
  327. package/squads/squad-artdir/checklists/seven-pillars-validation-checklist.md +0 -172
  328. package/squads/squad-artdir/knowledge-base/case-nyo-ia-reference.md +0 -289
  329. package/squads/squad-artdir/knowledge-base/deliverables-templates.md +0 -457
  330. package/squads/squad-artdir/knowledge-base/motion-technique-catalog.md +0 -247
  331. package/squads/squad-artdir/knowledge-base/premium-packaging-principles.md +0 -133
  332. package/squads/squad-artdir/knowledge-base/psychological-toolkit.md +0 -229
  333. package/squads/squad-artdir/knowledge-base/saas-art-direction-canon.md +0 -242
  334. package/squads/squad-artdir/knowledge-base/seven-pillars-framework.md +0 -289
  335. package/squads/squad-artdir/knowledge-base/ten-pillars-framework.md +0 -221
  336. package/squads/squad-artdir/package.json +0 -20
  337. package/squads/squad-artdir/squad.yaml +0 -299
  338. package/squads/squad-artdir/tasks/audit-conversion.md +0 -97
  339. package/squads/squad-artdir/tasks/audit-drift-multi-surface.md +0 -55
  340. package/squads/squad-artdir/tasks/consult-saas-canon.md +0 -54
  341. package/squads/squad-artdir/tasks/create-art-direction-brief.md +0 -110
  342. package/squads/squad-artdir/tasks/create-premium-packaging-brief.md +0 -61
  343. package/squads/squad-artdir/tasks/create-wireflow.md +0 -84
  344. package/squads/squad-artdir/tasks/design-color-system.md +0 -81
  345. package/squads/squad-artdir/tasks/design-product-surface.md +0 -60
  346. package/squads/squad-artdir/tasks/design-token-system.md +0 -58
  347. package/squads/squad-artdir/tasks/diagnose-visual-language.md +0 -92
  348. package/squads/squad-artdir/tasks/first-5-minutes-choreography.md +0 -65
  349. package/squads/squad-artdir/tasks/specify-motion-system.md +0 -84
  350. package/squads/squad-artdir/tasks/validate-against-pillars.md +0 -143
  351. package/squads/squad-artdir/templates/art-direction-brief-template.md +0 -215
  352. package/squads/squad-artdir/workflows/conversion-audit-cycle.yaml +0 -142
  353. package/squads/squad-artdir/workflows/full-art-direction-cycle.yaml +0 -179
  354. package/squads/squad-artdir/workflows/saas-platform-art-direction-cycle.yaml +0 -338
  355. package/squads/squad-commercial/agents/legal-chief.md +0 -199
  356. package/squads/squad-copy/agents/copy-chief.md +0 -162
  357. package/squads/squad-cybersecurity/agents/cyber-chief.md +0 -169
  358. package/squads/squad-design/agents/design-chief.md +0 -226
  359. package/squads/squad-paidmedia/agents/traffic-masters-chief.md +0 -211
  360. package/squads/squad-research/agents/data-chief.md +0 -198
  361. package/squads/squad-storytelling/agents/story-chief.md +0 -180
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Framework Guard — Pre-commit hook that blocks commits to L1/L2 protected paths.
3
+ *
4
+ * Reads ALL configuration from core-config.yaml (single source of truth):
5
+ * - boundary.frameworkProtection: toggle (true/false)
6
+ * - boundary.protected: L1/L2 blocked glob patterns
7
+ * - boundary.exceptions: L3 allowed glob patterns
8
+ *
9
+ * When frameworkProtection is true (default), blocks staged changes to protected paths.
10
+ * When false, acts as a no-op (contributor mode).
11
+ *
12
+ * Story: BM-3 (Epic: Boundary Mapping & Framework-Project Separation)
13
+ */
14
+
15
+ 'use strict';
16
+
17
+ const { execSync } = require('child_process');
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ // Hardcoded fallbacks — used ONLY if core-config.yaml is missing or malformed.
22
+ // The canonical source is core-config.yaml boundary.protected/exceptions.
23
+ const FALLBACK_PROTECTED = [
24
+ '.sinapse-ai/core/**',
25
+ '.sinapse-ai/development/tasks/**',
26
+ '.sinapse-ai/development/templates/**',
27
+ '.sinapse-ai/development/checklists/**',
28
+ '.sinapse-ai/development/workflows/**',
29
+ '.sinapse-ai/infrastructure/**',
30
+ '.sinapse-ai/constitution.md',
31
+ 'bin/sinapse.js',
32
+ 'bin/sinapse-init.js',
33
+ ];
34
+
35
+ const FALLBACK_EXCEPTIONS = [
36
+ '.sinapse-ai/data/**',
37
+ '.sinapse-ai/development/agents/*/MEMORY.md',
38
+ ];
39
+
40
+ /**
41
+ * Convert a glob pattern to a RegExp.
42
+ * Supports: ** (any depth), * (single segment), literal dots.
43
+ * @param {string} glob
44
+ * @returns {RegExp}
45
+ */
46
+ function globToRegex(glob) {
47
+ // 1. Replace ** with placeholder before processing
48
+ let pattern = glob.replace(/\*\*/g, '\u0000');
49
+
50
+ // 2. Escape all regex-special chars (dots, plus, etc.) — but NOT * or placeholder
51
+ pattern = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
52
+
53
+ // 3. Convert remaining single * to single-segment matcher
54
+ pattern = pattern.replace(/\*/g, '[^/]+');
55
+
56
+ // 4. Restore ** placeholder to any-depth matcher
57
+ pattern = pattern.replace(/\u0000/g, '.+');
58
+
59
+ // If pattern ends with .+ (was **), match prefix
60
+ if (glob.endsWith('**')) {
61
+ return new RegExp('^' + pattern);
62
+ }
63
+ // Exact file match
64
+ return new RegExp('^' + pattern + '$');
65
+ }
66
+
67
+ /**
68
+ * Read the raw content of core-config.yaml.
69
+ *
70
+ * Resolution order (so this works both as the in-repo hook AND when bundled
71
+ * into .sinapse-ai/git-hooks/lib/ by the installer, where __dirname no longer
72
+ * sits two levels under the project root):
73
+ * 1. `<cwd>/.sinapse-ai/core-config.yaml` — git runs hooks from the repo root,
74
+ * so cwd is the project root in the pre-commit path.
75
+ * 2. `<__dirname>/../../.sinapse-ai/core-config.yaml` — the original in-repo
76
+ * location (bin/utils -> repo root), kept for direct/test invocation.
77
+ * @returns {string|null}
78
+ */
79
+ function readConfigContent() {
80
+ const candidates = [
81
+ path.resolve(process.cwd(), '.sinapse-ai/core-config.yaml'),
82
+ path.resolve(__dirname, '../../.sinapse-ai/core-config.yaml'),
83
+ ];
84
+ for (const configPath of candidates) {
85
+ if (fs.existsSync(configPath)) {
86
+ return fs.readFileSync(configPath, 'utf8');
87
+ }
88
+ }
89
+ return null;
90
+ }
91
+
92
+ /**
93
+ * Parse a YAML list under a given key using simple line-based parsing.
94
+ * Avoids js-yaml dependency for speed.
95
+ * @param {string} content - YAML file content
96
+ * @param {string} parentKey - Parent key (e.g., 'boundary')
97
+ * @param {string} listKey - List key (e.g., 'protected')
98
+ * @returns {string[]}
99
+ */
100
+ function parseYamlList(content, parentKey, listKey) {
101
+ const lines = content.split('\n');
102
+ const items = [];
103
+ let inParent = false;
104
+ let inList = false;
105
+ let parentIndent = -1;
106
+ let listIndent = -1;
107
+
108
+ for (const line of lines) {
109
+ const trimmed = line.trimStart();
110
+ const indent = line.length - trimmed.length;
111
+
112
+ // Find parent key (e.g., "boundary:")
113
+ if (trimmed === parentKey + ':' || trimmed.startsWith(parentKey + ':')) {
114
+ inParent = true;
115
+ parentIndent = indent;
116
+ continue;
117
+ }
118
+
119
+ // If we're past the parent section (back to same or lower indent)
120
+ if (inParent && indent <= parentIndent && trimmed.length > 0 && !trimmed.startsWith('#')) {
121
+ inParent = false;
122
+ inList = false;
123
+ }
124
+
125
+ if (!inParent) continue;
126
+
127
+ // Find list key within parent (e.g., "protected:")
128
+ if (trimmed === listKey + ':' || trimmed.startsWith(listKey + ':')) {
129
+ inList = true;
130
+ listIndent = indent;
131
+ continue;
132
+ }
133
+
134
+ // If we're past the list (back to same or lower indent within parent)
135
+ if (inList && indent <= listIndent && trimmed.length > 0 && !trimmed.startsWith('#') && !trimmed.startsWith('-')) {
136
+ inList = false;
137
+ }
138
+
139
+ // Collect list items
140
+ if (inList && trimmed.startsWith('- ')) {
141
+ items.push(trimmed.slice(2).trim());
142
+ }
143
+ }
144
+
145
+ return items;
146
+ }
147
+
148
+ /**
149
+ * Read boundary config from core-config.yaml.
150
+ * @returns {{ enabled: boolean, protected: string[], exceptions: string[] }}
151
+ */
152
+ function readBoundaryConfig() {
153
+ const content = readConfigContent();
154
+ if (!content) {
155
+ return { enabled: true, protected: FALLBACK_PROTECTED, exceptions: FALLBACK_EXCEPTIONS };
156
+ }
157
+
158
+ // Read toggle
159
+ const toggleMatch = content.match(/frameworkProtection:\s*(true|false)/);
160
+ const enabled = toggleMatch ? toggleMatch[1] === 'true' : true;
161
+
162
+ // Read lists
163
+ const protectedPaths = parseYamlList(content, 'boundary', 'protected');
164
+ const exceptionPaths = parseYamlList(content, 'boundary', 'exceptions');
165
+
166
+ return {
167
+ enabled,
168
+ protected: protectedPaths.length > 0 ? protectedPaths : FALLBACK_PROTECTED,
169
+ exceptions: exceptionPaths.length > 0 ? exceptionPaths : FALLBACK_EXCEPTIONS,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Check if a file path matches any pattern in the list.
175
+ * @param {string} filePath
176
+ * @param {RegExp[]} patterns
177
+ * @returns {boolean}
178
+ */
179
+ function matchesAny(filePath, patterns) {
180
+ return patterns.some((pattern) => pattern.test(filePath));
181
+ }
182
+
183
+ /**
184
+ * Get list of staged files from git.
185
+ * @returns {string[]}
186
+ */
187
+ function getStagedFiles() {
188
+ try {
189
+ const output = execSync('git diff --cached --name-only', { encoding: 'utf8' });
190
+ return output
191
+ .split('\n')
192
+ .map((f) => f.trim())
193
+ .filter(Boolean);
194
+ } catch {
195
+ return [];
196
+ }
197
+ }
198
+
199
+ function main() {
200
+ // Step 1: Read config (single source of truth)
201
+ const config = readBoundaryConfig();
202
+
203
+ if (!config.enabled) {
204
+ process.exit(0);
205
+ }
206
+
207
+ // Step 2: Compile glob patterns to regex
208
+ const blockedPatterns = config.protected.map(globToRegex);
209
+ const allowedPatterns = config.exceptions.map(globToRegex);
210
+
211
+ // Step 3: Get staged files
212
+ const stagedFiles = getStagedFiles();
213
+ if (stagedFiles.length === 0) {
214
+ process.exit(0);
215
+ }
216
+
217
+ // Step 4: Check each staged file
218
+ const blockedFiles = [];
219
+ for (const file of stagedFiles) {
220
+ // Normalize to forward slashes (Windows compat)
221
+ const normalized = file.replace(/\\/g, '/');
222
+ if (matchesAny(normalized, blockedPatterns) && !matchesAny(normalized, allowedPatterns)) {
223
+ blockedFiles.push(normalized);
224
+ }
225
+ }
226
+
227
+ // Step 5: Report
228
+ if (blockedFiles.length > 0) {
229
+ console.error('');
230
+ console.error('Framework Guard: Commit blocked!');
231
+ console.error('');
232
+ console.error('The following framework files are protected (L1/L2):');
233
+ for (const file of blockedFiles) {
234
+ console.error(` - ${file}`);
235
+ }
236
+ console.error('');
237
+ console.error('These files are read-only in project mode (boundary.frameworkProtection: true).');
238
+ console.error('');
239
+ console.error('To bypass (framework contributors only):');
240
+ console.error(' git commit --no-verify');
241
+ console.error('');
242
+ console.error('To disable permanently (contributors):');
243
+ console.error(' Set boundary.frameworkProtection: false in core-config.yaml');
244
+ console.error('');
245
+ process.exit(1);
246
+ }
247
+
248
+ process.exit(0);
249
+ }
250
+
251
+ // Export for testing
252
+ module.exports = { readBoundaryConfig, globToRegex, matchesAny, getStagedFiles, FALLBACK_PROTECTED, FALLBACK_EXCEPTIONS };
253
+
254
+ // Run when executed directly
255
+ if (require.main === module) {
256
+ main();
257
+ }
258
+
@@ -0,0 +1,355 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Secret Scanner Core — shared detection logic.
5
+ *
6
+ * Single source of truth for secret detection, imported by BOTH:
7
+ * - .claude/hooks/secret-scanning.cjs (Claude Code PreToolUse, Write/Edit content)
8
+ * - bin/utils/staged-secret-scan.js (git pre-commit, staged diff content)
9
+ *
10
+ * Hardening (Frente 4.2 — Stream A):
11
+ * - All 20+ named patterns preserved (no regression).
12
+ * - Shannon entropy: flags high-entropy tokens (>= 20 contiguous chars,
13
+ * >= 4.5 bits/char) as suspected secrets, BEYOND the named patterns.
14
+ * Lockfile-hash context + code-identifier band are allowlisted to avoid
15
+ * false positives on integrity hashes and long camelCase symbols.
16
+ * - Allowlist: placeholders (your-key-here, xxx, CHANGEME, REPLACE_ME, <...>,
17
+ * example.com, etc.) and obvious fakes PASS.
18
+ * - Redaction: matched secrets are never printed in full.
19
+ * - Designed for fail-CLOSED callers (this module never silently allows).
20
+ *
21
+ * @module bin/utils/secret-scanner-core
22
+ */
23
+
24
+ // Private-key PEM headers are built from fragments so this source file holds no
25
+ // literal "BEGIN ... PRIVATE KEY" marker (avoids self-tripping secret scanners).
26
+ const PK = 'PRIVATE KEY';
27
+ const BEGIN = '-----BEGIN ';
28
+ const END5 = '-----';
29
+
30
+ const NAMED_PATTERNS = [
31
+ // API Keys & Tokens
32
+ { name: 'AWS Access Key', pattern: /AKIA[0-9A-Z]{16}/ },
33
+ { name: 'AWS Secret Key', pattern: /(?:aws_secret_access_key|secret_key)\s*[=:]\s*['"]?[A-Za-z0-9/+=]{40}['"]?/i, lowConfidence: true },
34
+ { name: 'GitHub Token', pattern: /gh[ps]_[A-Za-z0-9_]{36,}/ },
35
+ { name: 'GitHub OAuth', pattern: /gho_[A-Za-z0-9_]{36,}/ },
36
+ { name: 'GitHub Fine-Grained Token', pattern: /github_pat_[A-Za-z0-9_]{22,}/ },
37
+ { name: 'Slack Token', pattern: /xox[bpors]-[0-9]{10,}-[A-Za-z0-9-]+/ },
38
+ { name: 'Stripe Key', pattern: /[sr]k_(live|test)_[A-Za-z0-9]{20,}/ },
39
+ // OpenAI project-scoped keys (`sk-proj-…`) contain hyphens/underscores, so the
40
+ // generic `sk-[A-Za-z0-9]{20,}` rule below stops at the first hyphen and never
41
+ // matches the body. This explicit rule is CONCLUSIVE on shape (the `sk-proj-`
42
+ // prefix is unambiguous) and is NOT entropy-gated, so a leaked project key is
43
+ // caught even when its tail happens to score low on Shannon entropy.
44
+ { name: 'OpenAI Project Key', pattern: /sk-proj-[A-Za-z0-9_-]{40,}/ },
45
+ // Other OpenAI key families that also carry separators (svcacct / admin).
46
+ { name: 'OpenAI Service/Admin Key', pattern: /sk-(?:svcacct|admin)-[A-Za-z0-9_-]{20,}/ },
47
+ // Legacy/classic OpenAI keys (`sk-` + contiguous alnum). Entropy-gated to avoid
48
+ // tripping on `sk-` prefixed identifiers; threshold lowered to 3.0 (see note).
49
+ { name: 'OpenAI Key', pattern: new RegExp('sk-[A-Za-z0-9]{20,}'), entropyGated: true },
50
+ { name: 'Anthropic Key', pattern: new RegExp('sk-ant-[A-Za-z0-9-]{20,}') },
51
+ { name: 'Supabase Key', pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9_-]{50,}/ },
52
+ { name: 'Google API Key', pattern: /AIza[0-9A-Za-z_-]{35}/ },
53
+ { name: 'Vercel Token', pattern: /vercel_[A-Za-z0-9]{20,}/ },
54
+
55
+ // Private Keys (built dynamically; no literal header in source)
56
+ { name: 'RSA Private Key', pattern: new RegExp(BEGIN + 'RSA ' + PK + END5) },
57
+ { name: 'SSH Private Key', pattern: new RegExp(BEGIN + 'OPENSSH ' + PK + END5) },
58
+ { name: 'PGP Private Key', pattern: new RegExp(BEGIN + 'PGP ' + PK + ' BLOCK' + END5) },
59
+ { name: 'EC Private Key', pattern: new RegExp(BEGIN + 'EC ' + PK + END5) },
60
+ { name: 'DSA Private Key', pattern: new RegExp(BEGIN + 'DSA ' + PK + END5) },
61
+ { name: 'Generic Private Key', pattern: new RegExp(BEGIN + PK + END5) },
62
+
63
+ // Connection Strings
64
+ // user/password are restricted to non-whitespace so the negated classes can't
65
+ // span newlines and match a bogus region across a whole file (the host part of
66
+ // a URL followed by an unrelated colon and at-sign on later lines). Real
67
+ // credentials never contain whitespace, so true detection is unaffected.
68
+ { name: 'DB Connection String', pattern: /(?:postgres|mysql|mongodb|redis):\/\/[^:\s]+:[^@\s]+@[^/\s]+/i, credentialPlaceholderGated: true },
69
+ { name: 'Supabase DB URL', pattern: /postgresql:\/\/postgres\.[A-Za-z0-9]+:[^@\s]+@/i, credentialPlaceholderGated: true },
70
+
71
+ // Generic Patterns (broader, lower confidence — placeholder-allowlisted)
72
+ { name: 'Hardcoded Password', pattern: /(?:password|passwd|pwd)\s*[=:]\s*['"][^'"]{8,}['"]/i, lowConfidence: true },
73
+ { name: 'Bearer Token', pattern: /[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/, entropyGated: true, lowConfidence: true },
74
+ { name: 'Basic Auth', pattern: /[Bb]asic\s+[A-Za-z0-9+/=]{20,}/, lowConfidence: true },
75
+
76
+ // Long pure-hex runs (32–64 chars). The 16-symbol hex alphabet caps Shannon
77
+ // entropy at ~4.0, BELOW ENTROPY_THRESHOLD (4.5), so the generic entropy
78
+ // backstop never sees them — Twilio auth tokens (32-hex), webhook signing
79
+ // secrets and SHA-style 64-hex digests slip through. This explicit rule closes
80
+ // that gap. It is `hashContextGated`: a hex run sitting in an integrity /
81
+ // checksum / lockfile / git-sha context (covered by HASH_CONTEXT_PATTERN) is a
82
+ // legitimate hash, NOT a secret, and is allowlisted. `lowConfidence` keeps it
83
+ // OUT of the release publish gate (entropy:false there would otherwise match
84
+ // every documented digest); the diff-scoped commit hook still enforces it.
85
+ { name: 'Long Hex Token', pattern: /\b[a-f0-9]{32,64}\b/i, hashContextGated: true, lowConfidence: true },
86
+ ];
87
+
88
+ const PLACEHOLDER_TOKENS = [
89
+ 'your-key', 'your_key', 'yourkey', 'your-secret', 'your_secret',
90
+ 'your-token', 'your_token', 'your-api', 'your_api',
91
+ 'xxx', 'changeme', 'change-me', 'change_me',
92
+ 'replace_me', 'replace-me', 'replaceme', 'replace_with',
93
+ 'placeholder', 'example', 'sample', 'dummy', 'fake',
94
+ 'todo', 'tbd', 'redacted', 'insert', 'put-your', 'put_your',
95
+ 'my-secret', 'mysecret', 'my-password', 'mypassword',
96
+ 'supersecret', 'super-secret', 'secret123', 'password123',
97
+ 'notarealkey', 'not-a-real', 'test-key', 'test_key', 'testkey',
98
+ 'abcdef', '123456', '000000', 'aaaaaa',
99
+ ];
100
+
101
+ const PLACEHOLDER_PATTERNS = [
102
+ /^<.*>$/, // <sua-chave>, <token>, <REPLACE>
103
+ /^\[.*\]$/, // [CHANGE_ME], [your key]
104
+ /^\{\{?.*\}?\}$/, // {token}, {{secret}}
105
+ /^\$\{.*\}$/, // ${TOKEN}
106
+ // SCREAMING_SNAKE env-var name used as value. Requires an underscore so raw
107
+ // all-caps secrets (e.g. AWS access keys) are NOT allowlisted by this rule.
108
+ /^[A-Z][A-Z0-9]*_[A-Z0-9_]*$/,
109
+ /^(x+|y+|z+|n+|0+|a+|\.+|-+|_+|\*+)$/i, // xxxx, 0000, ----, ....
110
+ ];
111
+
112
+ const EXAMPLE_HOST_PATTERN = /(?:example\.(?:com|org|net)|localhost|127\.0\.0\.1|host\b|your-host|placeholder)/i;
113
+
114
+ // A keyword placeholder only allowlists when it DOMINATES the value: once every
115
+ // placeholder occurrence is removed, the TOTAL alphanumeric length that remains
116
+ // is too short to be a secret on its own (< ENTROPY_MIN_LEN). A bare
117
+ // `lower.includes(token)` was a trivial bypass — ANY real secret that happened to
118
+ // contain "abcdef" / "123456" / "example" anywhere in its body got silently
119
+ // allowlisted (e.g. Xq9Zk2…abcdef…Fg5 passed). Measuring the *total* remainder
120
+ // (not just the longest contiguous run) closes that hole even when the buried
121
+ // placeholder splits the secret into two sub-threshold runs: the legitimate
122
+ // cases (your-key-here, placeholder123456, test-key-example, CHANGEME) strip down
123
+ // to nothing, while a 35-char random token minus a 6-char placeholder still has
124
+ // ~29 alphanumeric chars left and is NOT allowlisted.
125
+ function placeholderDominates(lower) {
126
+ let stripped = lower;
127
+ let matchedAny = false;
128
+ for (const token of PLACEHOLDER_TOKENS) {
129
+ if (stripped.includes(token)) {
130
+ matchedAny = true;
131
+ // Replace with a separator so adjacent runs aren't fused into a longer one.
132
+ stripped = stripped.split(token).join(' ');
133
+ }
134
+ }
135
+ if (!matchedAny) return false;
136
+ // Total alphanumeric chars left after removing every placeholder occurrence.
137
+ const remaining = (stripped.match(/[a-z0-9]/g) || []).length;
138
+ return remaining < ENTROPY_MIN_LEN;
139
+ }
140
+
141
+ function isAllowlistPlaceholder(value) {
142
+ if (value === null || value === undefined) return false;
143
+ const v = String(value).trim();
144
+ if (v.length === 0) return true;
145
+
146
+ // Structural placeholders are always safe: <...>, [...], {token}, ${VAR},
147
+ // SCREAMING_SNAKE env-var names, and repeated single-symbol fillers.
148
+ for (const re of PLACEHOLDER_PATTERNS) {
149
+ if (re.test(v)) return true;
150
+ }
151
+ if (/^(.)\1{5,}$/.test(v)) return true; // repeated single char
152
+
153
+ // Keyword placeholders: word-boundary anchored + dominance, NOT raw includes().
154
+ if (placeholderDominates(v.toLowerCase())) return true;
155
+
156
+ return false;
157
+ }
158
+
159
+ function shannonEntropy(str) {
160
+ if (!str || str.length === 0) return 0;
161
+ const freq = Object.create(null);
162
+ for (const ch of str) {
163
+ freq[ch] = (freq[ch] || 0) + 1;
164
+ }
165
+ const len = str.length;
166
+ let entropy = 0;
167
+ for (const ch in freq) {
168
+ const p = freq[ch] / len;
169
+ entropy -= p * Math.log2(p);
170
+ }
171
+ return entropy;
172
+ }
173
+
174
+ // 4.5 bits/char: long camelCase/snake identifiers in source top out around
175
+ // ~4.2 (dictionary words have repeated letters), while real random secrets —
176
+ // base64 of random bytes (~5.5), mixed alphanumeric tokens (~5.0), even base64
177
+ // of text (~4.5) — clear it. Set above the identifier band to kill false
178
+ // positives on code symbols; named patterns still catch branded low-entropy keys.
179
+ const ENTROPY_THRESHOLD = 4.5;
180
+ const ENTROPY_MIN_LEN = 20;
181
+ const TOKEN_SHAPE = /[A-Za-z0-9_\-/+=]{20,}/g;
182
+
183
+ const LOCKFILE_PATH_PATTERN = /(?:^|\/)(?:package-lock\.json|yarn\.lock|pnpm-lock\.yaml|composer\.lock|Cargo\.lock|poetry\.lock|Gemfile\.lock|bun\.lockb)$/i;
184
+ const HASH_CONTEXT_PATTERN = /(?:sha512-|sha384-|sha256-|sha1-|integrity"?\s*[:=]|"resolved"|"checksum"|"hash"|[a-f0-9]{40}|[a-f0-9]{64})/i;
185
+
186
+ function isLockfilePath(filePath) {
187
+ if (!filePath) return false;
188
+ return LOCKFILE_PATH_PATTERN.test(String(filePath).replace(/\\/g, '/'));
189
+ }
190
+
191
+ function redactMatch(value, keep = 4) {
192
+ const v = String(value == null ? '' : value);
193
+ if (v.length === 0) return '[REDACTED]';
194
+ if (v.length <= keep) return '[REDACTED]';
195
+ const prefix = v.slice(0, keep);
196
+ return prefix + '...[REDACTED ' + (v.length - keep) + ' chars]';
197
+ }
198
+
199
+ function scanContent(content, options = {}) {
200
+ const text = String(content == null ? '' : content);
201
+ const filePath = options.filePath || '';
202
+ const entropyEnabled = options.entropy !== false;
203
+ const findings = [];
204
+
205
+ if (text.length === 0) return findings;
206
+
207
+ // 1) Named patterns
208
+ // Structural patterns (AWS access key, Stripe, Google, PEM headers,
209
+ // connection strings, JWT) are CONCLUSIVE on shape — they are NOT
210
+ // placeholder-allowlisted (a real AKIA… key that happens to contain
211
+ // "123456" is still a leak). Only `lowConfidence` value-bearing patterns
212
+ // (password/secret assignments, Bearer/Basic headers) get the placeholder
213
+ // allowlist, applied to the captured *value* portion.
214
+ for (const descriptor of NAMED_PATTERNS) {
215
+ const m = text.match(descriptor.pattern);
216
+ if (!m) continue;
217
+ const matched = m[0];
218
+
219
+ if (descriptor.lowConfidence) {
220
+ // Isolate the value side of `key = "<value>"` / `Bearer <value>`.
221
+ const valuePart = (matched.match(/(?:[=:]\s*['"]?|\s+)([^'"]+)['"]?\s*$/) || [null, matched])[1] || matched;
222
+ if (isAllowlistPlaceholder(valuePart)) continue;
223
+ }
224
+
225
+ if (descriptor.credentialPlaceholderGated) {
226
+ // A connection string carries a structured user:password@host. When the
227
+ // password slot is a placeholder/interpolation (${VAR}, <pass>, a
228
+ // SCREAMING_SNAKE env-var name, your-password…) it is a template/example,
229
+ // NOT a leaked credential — and is the idiomatic, safe way to document
230
+ // one. A real leaked password (random/literal) is not allowlisted and is
231
+ // still flagged.
232
+ const cred = (matched.match(/:\/\/[^:/\s]+:([^@\s]+)@/) || [null, ''])[1];
233
+ if (cred && isAllowlistPlaceholder(cred)) continue;
234
+ }
235
+
236
+ if (descriptor.entropyGated) {
237
+ const tail = (matched.match(/[A-Za-z0-9_\-/+=]{16,}$/) || [matched])[0];
238
+ if (isAllowlistPlaceholder(tail)) continue;
239
+ // Threshold lowered 2.5 -> 2.0: a short real token (e.g. a 20-char legacy
240
+ // key with a narrow alphabet) can dip just under 2.5 and would have been a
241
+ // false-NEGATIVE (leaked secret allowed through). 2.0 still rejects obvious
242
+ // non-random fixtures (a single repeated char ~ 0.0, repeated words ~1.5)
243
+ // while no longer dropping legitimately-shaped keys.
244
+ if (shannonEntropy(tail) < 2.0) continue; // clearly non-random
245
+ }
246
+
247
+ if (descriptor.hashContextGated) {
248
+ // Long hex runs are flagged as suspected secrets (Twilio token, webhook
249
+ // signing secret, SHA-style digest) EXCEPT when they sit in an integrity /
250
+ // checksum / lockfile / git-sha context — those are legitimate hashes, not
251
+ // leaked credentials. Reuse HASH_CONTEXT_PATTERN over a window around the
252
+ // match so package-lock integrity:, "resolved" tarball #sha, "checksum",
253
+ // and 40/64-hex git shas are NOT false-positived. A fully repeated/obvious
254
+ // placeholder hex (deadbeef…, 000…) is also allowlisted.
255
+ if (isAllowlistPlaceholder(matched)) continue;
256
+ if (isLockfilePath(filePath)) continue;
257
+ // Canonical digest lengths — git SHA-1 (40) and SHA-256 (64) — are
258
+ // overwhelmingly legitimate hashes (commit refs, file checksums, content
259
+ // digests) and appear bare in changelogs/docs/lockfiles. Treating every
260
+ // isolated 40/64-hex run as a leak would false-positive on the entire git
261
+ // ecosystem, so these exact lengths are hash-shaped by default. The
262
+ // headline real-secret case (Twilio auth token = 32-hex) and other
263
+ // non-digest lengths (33–39, 41–63) are NOT standard digests and stay
264
+ // flagged. A leaked literal hash secret of EXACTLY 40/64 hex is the
265
+ // accepted blind spot of this length-based heuristic (documented tradeoff
266
+ // favouring zero false-positives on git/checksum hashes).
267
+ const hexLen = matched.length;
268
+ if (hexLen === 40 || hexLen === 64) continue;
269
+ // For non-canonical lengths, allowlist only when the SURROUNDING context
270
+ // carries a hash marker (integrity:, sha256-, "resolved", "checksum"). The
271
+ // window is widened on the left so a marker earlier on the line
272
+ // (e.g. `"resolved": "https://…/x.tgz#<hex>"`) is still seen.
273
+ const mIdx = text.indexOf(matched);
274
+ const before = text.slice(Math.max(0, mIdx - 64), mIdx);
275
+ const after = text.slice(mIdx + matched.length, mIdx + matched.length + 4);
276
+ if (HASH_CONTEXT_PATTERN.test(before + ' ' + after)) continue;
277
+ }
278
+
279
+ findings.push({ name: descriptor.name, redacted: redactMatch(matched), kind: 'pattern' });
280
+ }
281
+
282
+ // 2) Generic high-entropy detector (beyond named patterns)
283
+ if (entropyEnabled && !isLockfilePath(filePath)) {
284
+ const seen = new Set();
285
+ let match;
286
+ TOKEN_SHAPE.lastIndex = 0;
287
+ while ((match = TOKEN_SHAPE.exec(text)) !== null) {
288
+ const token = match[0];
289
+ if (token.length < ENTROPY_MIN_LEN) continue;
290
+ if (seen.has(token)) continue;
291
+ seen.add(token);
292
+ if (isAllowlistPlaceholder(token)) continue;
293
+
294
+ // Real high-entropy secrets are a long *contiguous* alphanumeric body.
295
+ // File paths (a/b/c), kebab/snake IDs (PROP-20260507-slug) and dotted
296
+ // slugs split into short segments at separators — they only look
297
+ // "high-entropy" as a whole. Require a contiguous run >= ENTROPY_MIN_LEN
298
+ // so example identifiers and paths in prose/docs stop tripping the net,
299
+ // while branded tokens (caught by NAMED_PATTERNS) and raw base64/hex
300
+ // bodies still qualify.
301
+ const longestRun = (token.match(/[A-Za-z0-9]+/g) || [])
302
+ .reduce((max, seg) => Math.max(max, seg.length), 0);
303
+ if (longestRun < ENTROPY_MIN_LEN) continue;
304
+
305
+ // For KEY=value tokens (e.g. DATABASE_URL=your-db-url) also check whether
306
+ // the value-side alone is a placeholder. The token shape regex includes '='
307
+ // so a whole assignment can be captured as one long token; if the value part
308
+ // looks like a placeholder the whole thing is safe.
309
+ const eqIdx = token.indexOf('=');
310
+ if (eqIdx > 0) {
311
+ const valueSide = token.slice(eqIdx + 1);
312
+ if (isAllowlistPlaceholder(valueSide)) continue;
313
+ // Generic "your-*" / "my-*" prefix heuristic covers cases not in the list.
314
+ const vsLower = valueSide.toLowerCase();
315
+ if (vsLower.startsWith('your-') || vsLower.startsWith('your_') ||
316
+ vsLower.startsWith('my-') || vsLower.startsWith('my_') ||
317
+ vsLower.startsWith('insert-') || vsLower.startsWith('put-')) continue;
318
+ }
319
+
320
+ const ent = shannonEntropy(token);
321
+ if (ent < ENTROPY_THRESHOLD) continue;
322
+
323
+ const ctxStart = Math.max(0, match.index - 24);
324
+ const context = text.slice(ctxStart, match.index + token.length + 4);
325
+ if (HASH_CONTEXT_PATTERN.test(context)) continue;
326
+
327
+ findings.push({
328
+ name: 'High-entropy string (suspected secret)',
329
+ redacted: redactMatch(token),
330
+ entropy: Number(ent.toFixed(2)),
331
+ kind: 'entropy',
332
+ });
333
+ }
334
+ }
335
+
336
+ return findings;
337
+ }
338
+
339
+ function hasSecret(content, options = {}) {
340
+ return scanContent(content, options).length > 0;
341
+ }
342
+
343
+ module.exports = {
344
+ NAMED_PATTERNS,
345
+ PLACEHOLDER_TOKENS,
346
+ EXAMPLE_HOST_PATTERN,
347
+ ENTROPY_THRESHOLD,
348
+ ENTROPY_MIN_LEN,
349
+ shannonEntropy,
350
+ isAllowlistPlaceholder,
351
+ isLockfilePath,
352
+ redactMatch,
353
+ scanContent,
354
+ hasSecret,
355
+ };