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.
- package/.claude/hooks/mind-clone-governance.py +212 -212
- package/.claude/hooks/read-protection.py +152 -152
- package/.claude/hooks/slug-validation.py +175 -175
- package/.claude/hooks/sql-governance.py +183 -183
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +1 -1
- package/.claude/rules/mandatory-delegation.md +1 -1
- package/.claude/rules/project-intelligence.md +1 -1
- package/.codex/agents/analyst.md +4 -371
- package/.codex/agents/animations-orqx.md +4 -57
- package/.codex/agents/architect.md +4 -560
- package/.codex/agents/brand-orqx.md +4 -95
- package/.codex/agents/claude-mastery-chief.md +4 -0
- package/.codex/agents/cloning-orqx.md +4 -70
- package/.codex/agents/commercial-orqx.md +4 -67
- package/.codex/agents/config-engineer.md +2 -2
- package/.codex/agents/content-orqx.md +4 -77
- package/.codex/agents/copy-orqx.md +4 -65
- package/.codex/agents/cost-optimizer.md +4 -0
- package/.codex/agents/council-orqx.md +4 -68
- package/.codex/agents/courses-orqx.md +4 -64
- package/.codex/agents/cro-persuasion.md +4 -0
- package/.codex/agents/cyber-orqx.md +4 -67
- package/.codex/agents/data-engineer.md +4 -542
- package/.codex/agents/design-orqx.md +4 -65
- package/.codex/agents/design-system.md +4 -210
- package/.codex/agents/developer.md +4 -666
- package/.codex/agents/devops.md +4 -668
- package/.codex/agents/finance-orqx.md +4 -57
- package/.codex/agents/fiscal-compliance-br.md +4 -0
- package/.codex/agents/forecast-strategist.md +4 -0
- package/.codex/agents/growth-orqx.md +4 -75
- package/.codex/agents/hooks-architect.md +2 -2
- package/.codex/agents/mcp-integrator.md +2 -2
- package/.codex/agents/paidmedia-orqx.md +4 -67
- package/.codex/agents/platform-aesthetic-director.md +4 -0
- package/.codex/agents/premium-packaging-strategist.md +4 -0
- package/.codex/agents/product-lead.md +4 -371
- package/.codex/agents/product-orqx.md +4 -57
- package/.codex/agents/product-surface-director.md +4 -0
- package/.codex/agents/project-integrator.md +2 -2
- package/.codex/agents/project-lead.md +4 -414
- package/.codex/agents/quality-gate.md +4 -547
- package/.codex/agents/research-orqx.md +4 -67
- package/.codex/agents/roadmap-sentinel.md +2 -2
- package/.codex/agents/skill-craftsman.md +2 -2
- package/.codex/agents/snps-orqx.md +4 -684
- package/.codex/agents/sop-extractor.md +4 -61
- package/.codex/agents/sprint-lead.md +4 -324
- package/.codex/agents/squad-creator.md +4 -402
- package/.codex/agents/storytelling-orqx.md +4 -65
- package/.codex/agents/swarm-orqx.md +4 -64
- package/.codex/agents/ux-design-expert.md +4 -532
- package/.codex/agents/ux-designer.md +4 -124
- package/.codex/command-registry.json +9 -9
- package/.codex/delegation-matrix.json +375 -839
- package/.codex/delegation-parity.json +658 -0
- package/.codex/handoff-packet.parity.schema.json +148 -0
- package/.codex/handoff-packet.template.json +26 -0
- package/.codex/instructions.md +8 -8
- package/.codex/scripts/resolve-codex-agent.js +482 -0
- package/.codex/scripts/resolve-codex-command.js +75 -12
- package/.codex/scripts/resolve-codex-delegation.js +131 -92
- package/.codex/skills/sinapse-claude/SKILL.md +3 -3
- package/.codex/skills/sinapse-po/SKILL.md +1 -1
- package/.codex/tasks/resolve-sinapse-conflict.md +1 -1
- package/.sinapse-ai/constitution.md +5 -5
- package/.sinapse-ai/core/doctor/checks/git-hooks.js +163 -19
- package/.sinapse-ai/core/events/dashboard-emitter.js +30 -9
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +1 -1
- package/.sinapse-ai/core/synapse/engine.js +15 -0
- package/.sinapse-ai/core/ui/observability-panel.js +240 -0
- package/.sinapse-ai/core-config.yaml +0 -20
- package/.sinapse-ai/data/entity-registry.yaml +185 -236
- package/.sinapse-ai/development/agents/snps-orqx.md +16 -26
- package/.sinapse-ai/development/tasks/build-autonomous.md +11 -1
- package/.sinapse-ai/development/tasks/build-resume.md +8 -0
- package/.sinapse-ai/development/tasks/build-status.md +8 -0
- package/.sinapse-ai/development/tasks/build.md +8 -0
- package/.sinapse-ai/development/tasks/cleanup-worktrees.md +8 -1
- package/.sinapse-ai/development/tasks/gotcha.md +8 -0
- package/.sinapse-ai/development/tasks/gotchas.md +8 -0
- package/.sinapse-ai/development/tasks/ids-health.md +14 -6
- package/.sinapse-ai/development/tasks/list-mcps.md +15 -0
- package/.sinapse-ai/development/tasks/merge-worktree.md +8 -1
- package/.sinapse-ai/development/tasks/qa-review-build.md +18 -0
- package/.sinapse-ai/development/tasks/remove-mcp.md +8 -1
- package/.sinapse-ai/development/tasks/validate-agents.md +26 -14
- package/.sinapse-ai/development/templates/service-template/README.md.hbs +159 -159
- package/.sinapse-ai/development/templates/service-template/__tests__/index.test.ts.hbs +238 -238
- package/.sinapse-ai/development/templates/service-template/client.ts.hbs +404 -404
- package/.sinapse-ai/development/templates/service-template/errors.ts.hbs +183 -183
- package/.sinapse-ai/development/templates/service-template/index.ts.hbs +121 -121
- package/.sinapse-ai/development/templates/service-template/package.json.hbs +88 -88
- package/.sinapse-ai/development/templates/service-template/types.ts.hbs +146 -146
- package/.sinapse-ai/development/templates/squad-template/LICENSE +22 -22
- package/.sinapse-ai/git-hooks/lib/framework-guard.js +258 -0
- package/.sinapse-ai/git-hooks/lib/secret-scanner-core.js +355 -0
- package/.sinapse-ai/git-hooks/lib/staged-secret-scan.js +179 -0
- package/.sinapse-ai/git-hooks/lib/staged-sql-guard.js +204 -0
- package/.sinapse-ai/git-hooks/post-commit +28 -0
- package/.sinapse-ai/git-hooks/pre-commit +81 -0
- package/.sinapse-ai/git-hooks/pre-push +83 -0
- package/.sinapse-ai/hooks/ids-post-commit.js +13 -11
- package/.sinapse-ai/hooks/ids-pre-push.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/codex-parity/resolve.js +161 -0
- package/.sinapse-ai/infrastructure/scripts/dashboard-status-writer.js +6 -2
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +65 -68
- package/.sinapse-ai/infrastructure/scripts/sync-codex-local-first.js +156 -1
- package/.sinapse-ai/infrastructure/scripts/validate-codex-delegation.js +1 -4
- package/.sinapse-ai/infrastructure/scripts/validate-codex-integration.js +41 -5
- package/.sinapse-ai/infrastructure/templates/coderabbit.yaml.template +280 -280
- package/.sinapse-ai/infrastructure/templates/config/env.example +16 -16
- package/.sinapse-ai/infrastructure/templates/config/gitignore-additions.tmpl +59 -59
- package/.sinapse-ai/infrastructure/templates/github/CODEOWNERS.template +12 -12
- package/.sinapse-ai/infrastructure/templates/github-workflows/ci.yml.template +170 -170
- package/.sinapse-ai/infrastructure/templates/github-workflows/pr-automation.yml.template +331 -331
- package/.sinapse-ai/infrastructure/templates/github-workflows/release.yml.template +197 -197
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +19 -19
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-node.tmpl +86 -86
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-python.tmpl +146 -146
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-sinapse-base.tmpl +64 -64
- package/.sinapse-ai/infrastructure/templates/safe-collab/CODEOWNERS.template +16 -16
- package/.sinapse-ai/infrastructure/templates/sinapse-sync.yaml.template +183 -183
- package/.sinapse-ai/install-manifest.yaml +112 -164
- package/.sinapse-ai/local-config.yaml.template +65 -65
- package/.sinapse-ai/product/templates/adr.hbs +126 -126
- package/.sinapse-ai/product/templates/dbdr.hbs +242 -242
- package/.sinapse-ai/product/templates/epic.hbs +213 -213
- package/.sinapse-ai/product/templates/ide-rules/codex-rules.md +30 -0
- package/.sinapse-ai/product/templates/pmdr.hbs +187 -187
- package/.sinapse-ai/product/templates/prd-v2.0.hbs +217 -217
- package/.sinapse-ai/product/templates/prd.hbs +202 -202
- package/.sinapse-ai/product/templates/statusline/statusline-script.js +31 -8
- package/.sinapse-ai/product/templates/statusline/track-agent-clear.cjs +79 -0
- package/.sinapse-ai/product/templates/statusline/track-agent.cjs +218 -0
- package/.sinapse-ai/product/templates/story.hbs +264 -264
- package/.sinapse-ai/product/templates/task.hbs +171 -171
- package/.sinapse-ai/product/templates/tmpl-comment-on-examples.sql +159 -159
- package/.sinapse-ai/product/templates/tmpl-migration-script.sql +92 -92
- package/.sinapse-ai/product/templates/tmpl-rls-granular-policies.sql +105 -105
- package/.sinapse-ai/product/templates/tmpl-rls-kiss-policy.sql +11 -11
- package/.sinapse-ai/product/templates/tmpl-rls-roles.sql +136 -136
- package/.sinapse-ai/product/templates/tmpl-rls-simple.sql +78 -78
- package/.sinapse-ai/product/templates/tmpl-rls-tenant.sql +153 -153
- package/.sinapse-ai/product/templates/tmpl-rollback-script.sql +78 -78
- package/.sinapse-ai/product/templates/tmpl-seed-data.sql +141 -141
- package/.sinapse-ai/product/templates/tmpl-smoke-test.sql +17 -17
- package/.sinapse-ai/product/templates/tmpl-staging-copy-merge.sql +140 -140
- package/.sinapse-ai/product/templates/tmpl-stored-proc.sql +141 -141
- package/.sinapse-ai/product/templates/tmpl-trigger.sql +153 -153
- package/.sinapse-ai/product/templates/tmpl-view-materialized.sql +134 -134
- package/.sinapse-ai/product/templates/tmpl-view.sql +178 -178
- package/AGENTS.md +193 -0
- package/CHANGELOG.md +1247 -0
- package/LICENSE +63 -63
- package/README.en.md +17 -18
- package/README.md +18 -19
- package/bin/cli.js +1 -1
- package/bin/commands/install.js +194 -22
- package/bin/commands/status.js +14 -1
- package/bin/commands/uninstall.js +2 -2
- package/bin/commands/update.js +52 -0
- package/bin/lib/setup-statusline.js +191 -0
- package/bin/sinapse-init.js +11 -83
- package/bin/utils/framework-guard.js +17 -4
- package/bin/utils/secret-scanner-core.js +109 -7
- package/bin/utils/staged-sql-guard.js +204 -0
- package/bin/utils/validate-publish.js +63 -0
- package/docs/agent-reference-guide.md +5 -7
- package/docs/framework/agent-prefix-convention.md +58 -0
- package/docs/framework/architecture-overview.md +4 -4
- package/docs/framework/collaboration-activation.md +45 -0
- package/docs/framework/guiding-principles.md +9 -9
- package/docs/getting-started.md +1 -1
- package/docs/guides/agent-reference.md +1 -1
- package/docs/guides/codex-config.md +4 -5
- package/docs/pt/architecture/sub-orqx-pattern.md +20 -18
- package/docs/security/overview.md +1 -1
- package/package.json +16 -12
- package/packages/installer/src/index.js +26 -0
- package/packages/installer/src/installer/git-hooks-installer.js +211 -47
- package/packages/installer/src/installer/sinapse-ai-installer.js +71 -0
- package/packages/installer/src/wizard/feedback.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +26 -26
- package/packages/installer/src/wizard/index.js +53 -4
- package/packages/sinapse-install/bin/edmcp.js +0 -0
- package/packages/sinapse-install/bin/sinapse-install.js +0 -0
- package/scripts/audit-tasks.cjs +112 -91
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/prepare-hooks.js +58 -0
- package/scripts/regenerate-orqx-stubs.ps1 +2 -3
- package/scripts/sync-counts.js +10 -2
- package/scripts/sync-squad-yaml-components.js +108 -6
- package/scripts/validate-agents-md.js +128 -0
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-squad-orqx.js +19 -9
- package/sinapse/agents/sinapse-orqx.md +16 -26
- package/sinapse/agents/snps-orqx.md +15 -25
- package/sinapse/knowledge-base/routing-catalog.md +1 -1
- package/sinapse/tasks/diagnose-and-route.md +1 -1
- package/sinapse/tasks/squad-status-report.md +1 -1
- package/squads/claude-code-mastery/agents/claude-mastery-chief.md +1 -1
- package/squads/claude-code-mastery/agents/hooks-architect.md +60 -68
- package/squads/claude-code-mastery/knowledge-base/swarm-orchestration-patterns.md +1 -1
- package/squads/claude-code-mastery/squad.yaml +8 -0
- package/squads/claude-code-mastery/tasks/audit-setup.md +1 -1
- package/squads/claude-code-mastery/workflows/optimization-cycle.yaml +4 -4
- package/squads/claude-code-mastery/workflows/project-setup-cycle.yaml +4 -4
- package/squads/squad-animations/README.md +1 -1
- package/squads/squad-animations/squad.yaml +1 -1
- package/squads/squad-brand/squad.yaml +1 -1
- package/squads/squad-cloning/README.md +1 -1
- package/squads/squad-cloning/squad.yaml +1 -1
- package/squads/squad-commercial/README.md +1 -1
- package/squads/squad-commercial/squad.yaml +2 -3
- package/squads/squad-content/README.md +1 -1
- package/squads/squad-content/squad.yaml +1 -1
- package/squads/squad-copy/README.md +1 -1
- package/squads/squad-copy/squad.yaml +2 -3
- package/squads/squad-council/README.md +1 -1
- package/squads/squad-courses/README.md +1 -1
- package/squads/squad-courses/squad.yaml +1 -1
- package/squads/squad-cybersecurity/README.md +1 -1
- package/squads/squad-cybersecurity/squad.yaml +2 -3
- package/squads/squad-design/README.md +1 -1
- package/squads/{squad-artdir → squad-design}/agents/cro-persuasion.md +1 -1
- package/squads/{squad-artdir → squad-design}/agents/platform-aesthetic-director.md +2 -2
- package/squads/{squad-artdir → squad-design}/agents/premium-packaging-strategist.md +2 -2
- package/squads/{squad-artdir → squad-design}/agents/product-surface-director.md +3 -3
- package/squads/squad-design/squad.yaml +6 -3
- package/squads/squad-finance/README.md +1 -1
- package/squads/squad-finance/squad.yaml +7 -1
- package/squads/squad-growth/README.md +1 -1
- package/squads/squad-growth/squad.yaml +1 -1
- package/squads/squad-paidmedia/README.md +1 -1
- package/squads/squad-paidmedia/squad.yaml +2 -3
- package/squads/squad-product/README.md +1 -1
- package/squads/squad-product/squad.yaml +1 -1
- package/squads/squad-research/README.md +1 -1
- package/squads/squad-research/squad.yaml +2 -3
- package/squads/squad-storytelling/README.md +1 -1
- package/squads/squad-storytelling/squad.yaml +2 -3
- package/.codex/agents/brad-frost.md +0 -46
- package/.codex/agents/claude-orqx.md +0 -72
- package/.codex/agents/copy-chief.md +0 -162
- package/.codex/agents/cyber-chief.md +0 -169
- package/.codex/agents/dan-mall.md +0 -43
- package/.codex/agents/data-chief.md +0 -198
- package/.codex/agents/dave-malouf.md +0 -43
- package/.codex/agents/db-sage.md +0 -152
- package/.codex/agents/design-chief.md +0 -226
- package/.codex/agents/dev.md +0 -102
- package/.codex/agents/legal-chief.md +0 -199
- package/.codex/agents/nano-banana-generator.md +0 -42
- package/.codex/agents/pm.md +0 -81
- package/.codex/agents/po.md +0 -85
- package/.codex/agents/qa.md +0 -98
- package/.codex/agents/sm.md +0 -77
- package/.codex/agents/squad-chief.md +0 -1553
- package/.codex/agents/squad.md +0 -66
- package/.codex/agents/story-chief.md +0 -180
- package/.codex/agents/tools-orqx.md +0 -219
- package/.codex/agents/traffic-masters-chief.md +0 -211
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +0 -265
- package/.sinapse-ai/core/permissions/__tests__/permission-mode.test.js +0 -293
- package/.sinapse-ai/data/registry-update-log.jsonl +0 -158
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +0 -298
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +0 -121
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +0 -119
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +0 -191
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +0 -448
- package/.sinapse-ai/infrastructure/tests/project-status-loader.test.js +0 -569
- package/.sinapse-ai/infrastructure/tests/regression-suite-v2.md +0 -622
- package/.sinapse-ai/infrastructure/tests/validate-module.js +0 -98
- package/.sinapse-ai/infrastructure/tests/worktree-manager.test.js +0 -620
- package/.sinapse-ai/monitor/hooks/lib/__init__.py +0 -2
- package/.sinapse-ai/monitor/hooks/lib/enrich.py +0 -59
- package/.sinapse-ai/monitor/hooks/lib/send_event.py +0 -48
- package/.sinapse-ai/monitor/hooks/notification.py +0 -30
- package/.sinapse-ai/monitor/hooks/post_tool_use.py +0 -46
- package/.sinapse-ai/monitor/hooks/pre_compact.py +0 -30
- package/.sinapse-ai/monitor/hooks/pre_tool_use.py +0 -41
- package/.sinapse-ai/monitor/hooks/stop.py +0 -30
- package/.sinapse-ai/monitor/hooks/subagent_stop.py +0 -30
- package/.sinapse-ai/monitor/hooks/user_prompt_submit.py +0 -39
- package/.sinapse-ai/product/templates/statusline/track-agent.sh +0 -69
- package/.sinapse-ai/workflow-intelligence/__tests__/confidence-scorer.test.js +0 -335
- package/.sinapse-ai/workflow-intelligence/__tests__/integration.test.js +0 -340
- package/.sinapse-ai/workflow-intelligence/__tests__/suggestion-engine.test.js +0 -438
- package/.sinapse-ai/workflow-intelligence/__tests__/wave-analyzer.test.js +0 -448
- package/.sinapse-ai/workflow-intelligence/__tests__/workflow-registry.test.js +0 -303
- package/bin/sinapse-graph.js +0 -19
- package/docs/codex-integration-process.md +0 -22
- package/docs/codex-parity-program.md +0 -27
- package/packages/installer/src/__tests__/performance-benchmark.js +0 -383
- package/packages/installer/tests/integration/environment-configuration.test.js +0 -332
- package/packages/installer/tests/integration/wizard-detection.test.js +0 -352
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +0 -383
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +0 -193
- package/packages/installer/tests/unit/config-validator.test.js +0 -315
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +0 -539
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +0 -636
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +0 -192
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +0 -186
- package/packages/installer/tests/unit/env-template.test.js +0 -187
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +0 -310
- package/packages/installer/tests/unit/git-hooks-installer.test.js +0 -262
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +0 -231
- package/packages/installer/tests/unit/merger/env-merger.test.js +0 -191
- package/packages/installer/tests/unit/merger/markdown-merger.test.js +0 -262
- package/packages/installer/tests/unit/merger/strategies.test.js +0 -154
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +0 -328
- package/packages/sinapse-install/tests/unit/chrome-brain.smoke.test.js +0 -66
- package/scripts/install-monitor-hooks.sh +0 -82
- package/squads/squad-artdir/README.md +0 -90
- package/squads/squad-artdir/agents/accessibility-guardian.md +0 -184
- package/squads/squad-artdir/agents/artdir-orqx.md +0 -222
- package/squads/squad-artdir/agents/color-psychologist.md +0 -166
- package/squads/squad-artdir/agents/design-system-architect.md +0 -100
- package/squads/squad-artdir/agents/ia-architect.md +0 -169
- package/squads/squad-artdir/agents/interaction-designer.md +0 -162
- package/squads/squad-artdir/agents/layout-engineer.md +0 -163
- package/squads/squad-artdir/agents/motion-architect.md +0 -185
- package/squads/squad-artdir/agents/type-systemist.md +0 -138
- package/squads/squad-artdir/agents/visual-strategist.md +0 -127
- package/squads/squad-artdir/checklists/seven-pillars-validation-checklist.md +0 -172
- package/squads/squad-artdir/knowledge-base/case-nyo-ia-reference.md +0 -289
- package/squads/squad-artdir/knowledge-base/deliverables-templates.md +0 -457
- package/squads/squad-artdir/knowledge-base/motion-technique-catalog.md +0 -247
- package/squads/squad-artdir/knowledge-base/premium-packaging-principles.md +0 -133
- package/squads/squad-artdir/knowledge-base/psychological-toolkit.md +0 -229
- package/squads/squad-artdir/knowledge-base/saas-art-direction-canon.md +0 -242
- package/squads/squad-artdir/knowledge-base/seven-pillars-framework.md +0 -289
- package/squads/squad-artdir/knowledge-base/ten-pillars-framework.md +0 -221
- package/squads/squad-artdir/package.json +0 -20
- package/squads/squad-artdir/squad.yaml +0 -299
- package/squads/squad-artdir/tasks/audit-conversion.md +0 -97
- package/squads/squad-artdir/tasks/audit-drift-multi-surface.md +0 -55
- package/squads/squad-artdir/tasks/consult-saas-canon.md +0 -54
- package/squads/squad-artdir/tasks/create-art-direction-brief.md +0 -110
- package/squads/squad-artdir/tasks/create-premium-packaging-brief.md +0 -61
- package/squads/squad-artdir/tasks/create-wireflow.md +0 -84
- package/squads/squad-artdir/tasks/design-color-system.md +0 -81
- package/squads/squad-artdir/tasks/design-product-surface.md +0 -60
- package/squads/squad-artdir/tasks/design-token-system.md +0 -58
- package/squads/squad-artdir/tasks/diagnose-visual-language.md +0 -92
- package/squads/squad-artdir/tasks/first-5-minutes-choreography.md +0 -65
- package/squads/squad-artdir/tasks/specify-motion-system.md +0 -84
- package/squads/squad-artdir/tasks/validate-against-pillars.md +0 -143
- package/squads/squad-artdir/templates/art-direction-brief-template.md +0 -215
- package/squads/squad-artdir/workflows/conversion-audit-cycle.yaml +0 -142
- package/squads/squad-artdir/workflows/full-art-direction-cycle.yaml +0 -179
- package/squads/squad-artdir/workflows/saas-platform-art-direction-cycle.yaml +0 -338
- package/squads/squad-commercial/agents/legal-chief.md +0 -199
- package/squads/squad-copy/agents/copy-chief.md +0 -162
- package/squads/squad-cybersecurity/agents/cyber-chief.md +0 -169
- package/squads/squad-design/agents/design-chief.md +0 -226
- package/squads/squad-paidmedia/agents/traffic-masters-chief.md +0 -211
- package/squads/squad-research/agents/data-chief.md +0 -198
- package/squads/squad-storytelling/agents/story-chief.md +0 -180
package/bin/commands/update.js
CHANGED
|
@@ -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
|
+
};
|
package/bin/sinapse-init.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
1009
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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' });
|