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
@@ -31,6 +31,29 @@ const {
31
31
  const { promptLlmChoice } = require('../lib/prompts');
32
32
  const { generateCommandMd, generateSquadAwareness } = require('./install');
33
33
  const { registerGroundingHooks, HOOK_FILENAMES } = require('../lib/register-grounding-hooks');
34
+ const { execSync } = require('child_process');
35
+
36
+ // Query the latest version published to npm. Returns null when npm is unreachable.
37
+ function fetchLatestVersion() {
38
+ try {
39
+ const out = execSync('npm view sinapse-ai version', {
40
+ encoding: 'utf8',
41
+ stdio: ['ignore', 'pipe', 'ignore'],
42
+ timeout: 15000,
43
+ });
44
+ return (out || '').trim() || null;
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+
50
+ function isNewerVersion(candidate, current) {
51
+ try {
52
+ return require('semver').gt(candidate, current);
53
+ } catch {
54
+ return candidate !== current;
55
+ }
56
+ }
34
57
 
35
58
  async function cmdUpdateGlobal() {
36
59
  const logger = getLogger();
@@ -45,6 +68,35 @@ async function cmdUpdateGlobal() {
45
68
  const existing = detectExistingInstall();
46
69
  const prevVer = existing.prevMeta && existing.prevMeta.version ? existing.prevMeta.version : 'unknown';
47
70
 
71
+ // Real update (like `claude update`): fetch the latest published version and, if
72
+ // it is newer than what is running, download + apply it, then hand off to the
73
+ // freshly installed binary to re-sync. `--local` / `--no-fetch` skip this (used by
74
+ // the handoff to avoid a loop, and for offline re-sync of the running version).
75
+ const skipFetch = process.argv.includes('--local') || process.argv.includes('--no-fetch');
76
+ if (!skipFetch) {
77
+ const latest = fetchLatestVersion();
78
+ if (latest && isNewerVersion(latest, VERSION)) {
79
+ logger.always(`${BOLD} Nova versão disponível: v${latest}${NC} ${DIM}(você está na v${VERSION})${NC}`);
80
+ logger.always(`${DIM} Baixando e aplicando a versão nova...${NC}\n`);
81
+ try {
82
+ execSync('npm install -g sinapse-ai@latest', { stdio: 'inherit' });
83
+ // Hand off to the new version to apply it. No loop: once installed, the new
84
+ // run sees latest === running and falls through to the local re-sync.
85
+ execSync('sinapse-ai update --local', { stdio: 'inherit' });
86
+ logger.always(`\n${GREEN}Atualizado para v${latest}.${NC}`);
87
+ return;
88
+ } catch (e) {
89
+ const reason = (e && e.message ? e.message.split('\n')[0] : 'erro desconhecido');
90
+ logger.always(`\n${YELLOW}Não consegui atualizar automaticamente (${reason}).${NC}`);
91
+ logger.always(`${DIM} Rode manualmente: ${CYAN}! npm install -g sinapse-ai@latest${NC} e depois ${CYAN}sinapse update${NC}.${NC}`);
92
+ logger.always(`${DIM} Seguindo com a versão atual por enquanto...${NC}\n`);
93
+ // fall through to local re-sync below
94
+ }
95
+ } else if (latest) {
96
+ logger.always(`${GREEN} Você já está na versão mais recente (v${VERSION}).${NC}\n`);
97
+ }
98
+ }
99
+
48
100
  // Welcome back screen
49
101
  logger.always(`${BOLD} Que bom que voce voltou!${NC}`);
50
102
  logger.always(`${DIM} Atualizando SNPS AI: v${prevVer} -> v${VERSION}${NC}`);
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared statusline + agent-tracker installer (D4).
5
+ *
6
+ * Single source of truth for cabling the SINAPSE statusline feedback loop into
7
+ * a user's global Claude Code config (`~/.claude/`). Consumed by both:
8
+ * - bin/sinapse-init.js (setupGlobalStatuslineLegacy)
9
+ * - packages/installer/src/wizard/index.js
10
+ *
11
+ * What it installs:
12
+ * - ~/.claude/statusline-script.js (renders the statusline)
13
+ * - ~/.claude/hooks/track-agent.cjs (UserPromptSubmit detector — writes session-cache)
14
+ * - ~/.claude/hooks/track-agent-clear.cjs (Stop hook — clears session-cache on session end)
15
+ * - ~/.claude/session-cache/ (per-cwd cache directory)
16
+ * - settings.statusLine (graceful skip if already set)
17
+ * - settings.hooks.UserPromptSubmit → track-agent.cjs (idempotent guard)
18
+ * - settings.hooks.Stop → track-agent-clear.cjs (idempotent guard)
19
+ *
20
+ * Design notes:
21
+ * - FAIL-SOFT: statusline is non-critical. Any unexpected error returns a
22
+ * result object with `installed:false` + `reason`, never throws.
23
+ * - Idempotent: re-running never duplicates hook entries or clobbers an
24
+ * existing user statusLine.
25
+ * - Detector is now `.cjs` (Node), not the legacy `.sh` (bash) — works on
26
+ * Windows without a bash dependency.
27
+ */
28
+
29
+ const os = require('os');
30
+ const path = require('path');
31
+ const fse = require('fs-extra');
32
+
33
+ /**
34
+ * Resolve the canonical source paths for the statusline templates.
35
+ * @param {string} sourceCoreDir Absolute path to the installed `.sinapse-ai` directory.
36
+ */
37
+ function resolveSources(sourceCoreDir) {
38
+ const templatesDir = path.join(sourceCoreDir, 'product', 'templates', 'statusline');
39
+ return {
40
+ templatesDir,
41
+ script: path.join(templatesDir, 'statusline-script.js'),
42
+ detector: path.join(templatesDir, 'track-agent.cjs'),
43
+ clear: path.join(templatesDir, 'track-agent-clear.cjs'),
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Check whether an event array already has a hook whose command references `needle`.
49
+ * @param {Array} eventArr settings.hooks[event] array
50
+ * @param {string} needle substring to look for in the command (e.g. "track-agent-clear")
51
+ */
52
+ function hasHookCommand(eventArr, needle) {
53
+ if (!Array.isArray(eventArr)) return false;
54
+ return eventArr.some((entry) => {
55
+ if (entry && Array.isArray(entry.hooks)) {
56
+ return entry.hooks.some((h) => h && h.command && h.command.includes(needle));
57
+ }
58
+ return entry && entry.command && entry.command.includes(needle);
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Build the `node "<path>"` command string, escaping backslashes for JSON on Windows.
64
+ * @param {string} targetPath absolute path to the hook script
65
+ */
66
+ function nodeCommand(targetPath) {
67
+ return `node "${targetPath.replace(/\\/g, '\\\\')}"`;
68
+ }
69
+
70
+ /**
71
+ * Install the SINAPSE statusline + agent trackers into the user's global Claude config.
72
+ *
73
+ * @param {Object} [opts]
74
+ * @param {string} [opts.sourceCoreDir] Absolute path to the installed `.sinapse-ai` directory.
75
+ * Defaults to the repo's `.sinapse-ai` relative to this module (`../../.sinapse-ai`).
76
+ * @param {string} [opts.homeDir] Override the home directory (testing). Defaults to os.homedir().
77
+ * @returns {Promise<{installed:boolean, reason?:string, statusLineSet:boolean, detectorHook:boolean, clearHook:boolean, files:string[]}>}
78
+ */
79
+ async function setupStatusline(opts = {}) {
80
+ const result = {
81
+ installed: false,
82
+ statusLineSet: false,
83
+ detectorHook: false,
84
+ clearHook: false,
85
+ files: [],
86
+ };
87
+
88
+ try {
89
+ const homeDir = opts.homeDir || os.homedir();
90
+ const sourceCoreDir =
91
+ opts.sourceCoreDir || path.join(__dirname, '..', '..', '.sinapse-ai');
92
+
93
+ const sources = resolveSources(sourceCoreDir);
94
+
95
+ // Source templates must exist (statusline-script + the .cjs detector).
96
+ // track-agent-clear is best-effort — if missing we still install the rest.
97
+ if (!(await fse.pathExists(sources.script)) || !(await fse.pathExists(sources.detector))) {
98
+ result.reason = 'source templates missing (statusline-script.js or track-agent.cjs)';
99
+ return result;
100
+ }
101
+ const hasClear = await fse.pathExists(sources.clear);
102
+
103
+ const claudeDir = path.join(homeDir, '.claude');
104
+ const hooksDir = path.join(claudeDir, 'hooks');
105
+ const globalSettingsPath = path.join(claudeDir, 'settings.json');
106
+
107
+ const scriptTarget = path.join(claudeDir, 'statusline-script.js');
108
+ const detectorTarget = path.join(hooksDir, 'track-agent.cjs');
109
+ const clearTarget = path.join(hooksDir, 'track-agent-clear.cjs');
110
+
111
+ // Ensure directories (hooks + session-cache the trackers write to).
112
+ await fse.ensureDir(hooksDir);
113
+ await fse.ensureDir(path.join(claudeDir, 'session-cache'));
114
+
115
+ // Copy templates (overwrite is fine — these are framework-owned).
116
+ await fse.copy(sources.script, scriptTarget);
117
+ result.files.push(scriptTarget);
118
+ await fse.copy(sources.detector, detectorTarget);
119
+ result.files.push(detectorTarget);
120
+ if (hasClear) {
121
+ await fse.copy(sources.clear, clearTarget);
122
+ result.files.push(clearTarget);
123
+ }
124
+
125
+ // Read existing global settings (back-compat with any prior config).
126
+ let settings = {};
127
+ try {
128
+ if (await fse.pathExists(globalSettingsPath)) {
129
+ settings = JSON.parse(await fse.readFile(globalSettingsPath, 'utf8'));
130
+ }
131
+ } catch {
132
+ settings = {};
133
+ }
134
+
135
+ // statusLine — graceful skip if the user already configured one.
136
+ if (!settings.statusLine) {
137
+ settings.statusLine = {
138
+ type: 'command',
139
+ command: nodeCommand(scriptTarget),
140
+ };
141
+ result.statusLineSet = true;
142
+ }
143
+
144
+ // Hooks — register the .cjs detector (UserPromptSubmit) + clear (Stop), idempotently.
145
+ if (!settings.hooks || typeof settings.hooks !== 'object') {
146
+ settings.hooks = {};
147
+ }
148
+
149
+ if (!Array.isArray(settings.hooks.UserPromptSubmit)) {
150
+ settings.hooks.UserPromptSubmit = [];
151
+ }
152
+ if (!hasHookCommand(settings.hooks.UserPromptSubmit, 'track-agent.cjs')) {
153
+ settings.hooks.UserPromptSubmit.push({
154
+ matcher: '',
155
+ hooks: [{ type: 'command', command: nodeCommand(detectorTarget), timeout: 10 }],
156
+ });
157
+ result.detectorHook = true;
158
+ }
159
+
160
+ if (hasClear) {
161
+ if (!Array.isArray(settings.hooks.Stop)) {
162
+ settings.hooks.Stop = [];
163
+ }
164
+ if (!hasHookCommand(settings.hooks.Stop, 'track-agent-clear.cjs')) {
165
+ settings.hooks.Stop.push({
166
+ matcher: '',
167
+ hooks: [{ type: 'command', command: nodeCommand(clearTarget), timeout: 10 }],
168
+ });
169
+ result.clearHook = true;
170
+ }
171
+ }
172
+
173
+ await fse.ensureDir(path.dirname(globalSettingsPath));
174
+ await fse.writeFile(globalSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
175
+
176
+ result.installed = true;
177
+ return result;
178
+ } catch (error) {
179
+ // Statusline is non-critical — never block an install.
180
+ result.reason = error && error.message ? error.message : 'unknown error';
181
+ return result;
182
+ }
183
+ }
184
+
185
+ module.exports = {
186
+ setupStatusline,
187
+ // Exported for testing / reuse.
188
+ resolveSources,
189
+ hasHookCommand,
190
+ nodeCommand,
191
+ };
@@ -1000,92 +1000,20 @@ async function savePMConfig(pmTool, config, projectRoot) {
1000
1000
  }
1001
1001
 
1002
1002
  /**
1003
- * Setup global statusline for Claude Code (legacy installer version)
1004
- * Graceful skip: returns silently if user already has a statusLine configured.
1003
+ * Setup global statusline for Claude Code (legacy installer version).
1004
+ *
1005
+ * Thin wrapper over the shared module (bin/lib/setup-statusline.js) so the
1006
+ * legacy and wizard installers share ONE implementation. Installs:
1007
+ * - ~/.claude/statusline-script.js (graceful skip if user already has one)
1008
+ * - ~/.claude/hooks/track-agent.cjs → UserPromptSubmit detector (.cjs, not .sh)
1009
+ * - ~/.claude/hooks/track-agent-clear.cjs → Stop hook (clears session-cache)
1010
+ * - ~/.claude/session-cache/
1011
+ * FAIL-SOFT: never throws — statusline is non-critical.
1005
1012
  * @param {string} sourceCoreDir - Path to installed .sinapse-ai directory
1006
1013
  */
1007
1014
  async function setupGlobalStatuslineLegacy(sourceCoreDir) {
1008
- const os = require('os');
1009
- const homeDir = os.homedir();
1010
- const globalSettingsPath = path.join(homeDir, '.claude', 'settings.json');
1011
-
1012
- // Read existing global settings
1013
- let settings = {};
1014
- try {
1015
- if (fs.existsSync(globalSettingsPath)) {
1016
- settings = JSON.parse(fs.readFileSync(globalSettingsPath, 'utf8'));
1017
- }
1018
- } catch {
1019
- settings = {};
1020
- }
1021
-
1022
- // GRACEFUL SKIP: User already has a statusLine
1023
- if (settings.statusLine) {
1024
- return;
1025
- }
1026
-
1027
- // Source templates
1028
- const templatesDir = path.join(sourceCoreDir, 'product', 'templates', 'statusline');
1029
- const scriptSource = path.join(templatesDir, 'statusline-script.js');
1030
- const hookSource = path.join(templatesDir, 'track-agent.sh');
1031
-
1032
- if (!fs.existsSync(scriptSource) || !fs.existsSync(hookSource)) {
1033
- return;
1034
- }
1035
-
1036
- // Target paths
1037
- const scriptTarget = path.join(homeDir, '.claude', 'statusline-script.js');
1038
- const hookTarget = path.join(homeDir, '.claude', 'hooks', 'track-agent.sh');
1039
-
1040
- try {
1041
- await fse.ensureDir(path.join(homeDir, '.claude', 'hooks'));
1042
- await fse.ensureDir(path.join(homeDir, '.claude', 'session-cache'));
1043
- await fse.copy(scriptSource, scriptTarget);
1044
- await fse.copy(hookSource, hookTarget);
1045
- } catch {
1046
- return;
1047
- }
1048
-
1049
- // Add statusLine + hook to settings
1050
- const scriptPathEscaped = scriptTarget.replace(/\\/g, '\\\\');
1051
- settings.statusLine = {
1052
- type: 'command',
1053
- command: `node "${scriptPathEscaped}"`,
1054
- };
1055
-
1056
- if (!settings.hooks) {
1057
- settings.hooks = {};
1058
- }
1059
- if (!Array.isArray(settings.hooks.UserPromptSubmit)) {
1060
- settings.hooks.UserPromptSubmit = [];
1061
- }
1062
-
1063
- const hookPathEscaped = hookTarget.replace(/\\/g, '\\\\');
1064
- const alreadyHasTrackAgent = settings.hooks.UserPromptSubmit.some(entry => {
1065
- if (Array.isArray(entry.hooks)) {
1066
- return entry.hooks.some(h => h.command && h.command.includes('track-agent'));
1067
- }
1068
- return entry.command && entry.command.includes('track-agent');
1069
- });
1070
-
1071
- if (!alreadyHasTrackAgent) {
1072
- settings.hooks.UserPromptSubmit.push({
1073
- matcher: '',
1074
- hooks: [
1075
- {
1076
- type: 'command',
1077
- command: `bash "${hookPathEscaped}"`,
1078
- },
1079
- ],
1080
- });
1081
- }
1082
-
1083
- try {
1084
- await fse.ensureDir(path.dirname(globalSettingsPath));
1085
- await fse.writeFile(globalSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
1086
- } catch {
1087
- // Silent failure — statusline is non-critical
1088
- }
1015
+ const { setupStatusline } = require('./lib/setup-statusline');
1016
+ await setupStatusline({ sourceCoreDir });
1089
1017
  }
1090
1018
 
1091
1019
  // Run installer with error handling
@@ -66,14 +66,27 @@ function globToRegex(glob) {
66
66
 
67
67
  /**
68
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.
69
77
  * @returns {string|null}
70
78
  */
71
79
  function readConfigContent() {
72
- const configPath = path.resolve(__dirname, '../../.sinapse-ai/core-config.yaml');
73
- if (!fs.existsSync(configPath)) {
74
- return null;
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
+ }
75
88
  }
76
- return fs.readFileSync(configPath, 'utf8');
89
+ return null;
77
90
  }
78
91
 
79
92
  /**
@@ -36,6 +36,16 @@ const NAMED_PATTERNS = [
36
36
  { name: 'GitHub Fine-Grained Token', pattern: /github_pat_[A-Za-z0-9_]{22,}/ },
37
37
  { name: 'Slack Token', pattern: /xox[bpors]-[0-9]{10,}-[A-Za-z0-9-]+/ },
38
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).
39
49
  { name: 'OpenAI Key', pattern: new RegExp('sk-[A-Za-z0-9]{20,}'), entropyGated: true },
40
50
  { name: 'Anthropic Key', pattern: new RegExp('sk-ant-[A-Za-z0-9-]{20,}') },
41
51
  { name: 'Supabase Key', pattern: /eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9_-]{50,}/ },
@@ -51,13 +61,28 @@ const NAMED_PATTERNS = [
51
61
  { name: 'Generic Private Key', pattern: new RegExp(BEGIN + PK + END5) },
52
62
 
53
63
  // Connection Strings
54
- { name: 'DB Connection String', pattern: /(?:postgres|mysql|mongodb|redis):\/\/[^:]+:[^@]+@[^/\s]+/i },
55
- { name: 'Supabase DB URL', pattern: /postgresql:\/\/postgres\.[A-Za-z0-9]+:[^@]+@/i },
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 },
56
70
 
57
71
  // Generic Patterns (broader, lower confidence — placeholder-allowlisted)
58
72
  { name: 'Hardcoded Password', pattern: /(?:password|passwd|pwd)\s*[=:]\s*['"][^'"]{8,}['"]/i, lowConfidence: true },
59
73
  { name: 'Bearer Token', pattern: /[Bb]earer\s+[A-Za-z0-9_\-.]{20,}/, entropyGated: true, lowConfidence: true },
60
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 },
61
86
  ];
62
87
 
63
88
  const PLACEHOLDER_TOKENS = [
@@ -86,19 +111,48 @@ const PLACEHOLDER_PATTERNS = [
86
111
 
87
112
  const EXAMPLE_HOST_PATTERN = /(?:example\.(?:com|org|net)|localhost|127\.0\.0\.1|host\b|your-host|placeholder)/i;
88
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
+
89
141
  function isAllowlistPlaceholder(value) {
90
142
  if (value === null || value === undefined) return false;
91
143
  const v = String(value).trim();
92
144
  if (v.length === 0) return true;
93
145
 
94
- const lower = v.toLowerCase();
95
- for (const token of PLACEHOLDER_TOKENS) {
96
- if (lower.includes(token)) return true;
97
- }
146
+ // Structural placeholders are always safe: <...>, [...], {token}, ${VAR},
147
+ // SCREAMING_SNAKE env-var names, and repeated single-symbol fillers.
98
148
  for (const re of PLACEHOLDER_PATTERNS) {
99
149
  if (re.test(v)) return true;
100
150
  }
101
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
+
102
156
  return false;
103
157
  }
104
158
 
@@ -168,10 +222,58 @@ function scanContent(content, options = {}) {
168
222
  if (isAllowlistPlaceholder(valuePart)) continue;
169
223
  }
170
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
+
171
236
  if (descriptor.entropyGated) {
172
237
  const tail = (matched.match(/[A-Za-z0-9_\-/+=]{16,}$/) || [matched])[0];
173
238
  if (isAllowlistPlaceholder(tail)) continue;
174
- if (shannonEntropy(tail) < 2.5) continue; // clearly non-random
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;
175
277
  }
176
278
 
177
279
  findings.push({ name: descriptor.name, redacted: redactMatch(matched), kind: 'pattern' });