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
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
generate,
|
|
9
|
-
validateBoundaryPath,
|
|
10
|
-
readBoundaryConfig,
|
|
11
|
-
expandProtectedPaths,
|
|
12
|
-
expandExceptionPaths,
|
|
13
|
-
generatePermissions,
|
|
14
|
-
writeSettingsJson,
|
|
15
|
-
} = require('../../../../../.sinapse-ai/infrastructure/scripts/generate-settings-json');
|
|
16
|
-
|
|
17
|
-
function createTempProject(boundary, existingSettings) {
|
|
18
|
-
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gen-settings-'));
|
|
19
|
-
|
|
20
|
-
// Create core-config.yaml with boundary section
|
|
21
|
-
const sinapseCoreDir = path.join(tmpDir, '.sinapse-ai');
|
|
22
|
-
fs.mkdirSync(sinapseCoreDir, { recursive: true });
|
|
23
|
-
|
|
24
|
-
const yamlContent = [
|
|
25
|
-
'boundary:',
|
|
26
|
-
` frameworkProtection: ${boundary.frameworkProtection}`,
|
|
27
|
-
' protected:',
|
|
28
|
-
...boundary.protected.map(p => ` - ${p}`),
|
|
29
|
-
' exceptions:',
|
|
30
|
-
...boundary.exceptions.map(p => ` - ${p}`),
|
|
31
|
-
].join('\n') + '\n';
|
|
32
|
-
|
|
33
|
-
fs.writeFileSync(path.join(tmpDir, '.sinapse-ai', 'core-config.yaml'), yamlContent, 'utf8');
|
|
34
|
-
|
|
35
|
-
// Create directory structure for expansion tests
|
|
36
|
-
if (boundary.protected.includes('.sinapse-ai/core/**')) {
|
|
37
|
-
const coreDir = path.join(tmpDir, '.sinapse-ai', 'core');
|
|
38
|
-
fs.mkdirSync(coreDir, { recursive: true });
|
|
39
|
-
fs.mkdirSync(path.join(coreDir, 'utils'), { recursive: true });
|
|
40
|
-
fs.mkdirSync(path.join(coreDir, 'events'), { recursive: true });
|
|
41
|
-
fs.writeFileSync(path.join(coreDir, 'index.js'), '', 'utf8');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Create .claude directory and optionally existing settings
|
|
45
|
-
const claudeDir = path.join(tmpDir, '.claude');
|
|
46
|
-
fs.mkdirSync(claudeDir, { recursive: true });
|
|
47
|
-
|
|
48
|
-
if (existingSettings) {
|
|
49
|
-
fs.writeFileSync(
|
|
50
|
-
path.join(claudeDir, 'settings.json'),
|
|
51
|
-
JSON.stringify(existingSettings, null, 2) + '\n',
|
|
52
|
-
'utf8'
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return tmpDir;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function cleanupTempProject(tmpDir) {
|
|
60
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
describe('generate-settings-json', () => {
|
|
64
|
-
describe('readBoundaryConfig', () => {
|
|
65
|
-
test('reads boundary config from core-config.yaml', () => {
|
|
66
|
-
const tmpDir = createTempProject({
|
|
67
|
-
frameworkProtection: true,
|
|
68
|
-
protected: ['.sinapse-ai/core/**', 'bin/sinapse.js'],
|
|
69
|
-
exceptions: ['.sinapse-ai/data/**'],
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const config = readBoundaryConfig(tmpDir);
|
|
74
|
-
|
|
75
|
-
expect(config.frameworkProtection).toBe(true);
|
|
76
|
-
expect(config.protected).toContain('.sinapse-ai/core/**');
|
|
77
|
-
expect(config.protected).toContain('bin/sinapse.js');
|
|
78
|
-
expect(config.exceptions).toContain('.sinapse-ai/data/**');
|
|
79
|
-
} finally {
|
|
80
|
-
cleanupTempProject(tmpDir);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('throws when core-config.yaml not found', () => {
|
|
85
|
-
expect(() => readBoundaryConfig('/nonexistent/path')).toThrow('core-config.yaml not found');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test('rejects path traversal in protected paths', () => {
|
|
89
|
-
const tmpDir = createTempProject({
|
|
90
|
-
frameworkProtection: true,
|
|
91
|
-
protected: ['../../etc/passwd'],
|
|
92
|
-
exceptions: [],
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
expect(() => readBoundaryConfig(tmpDir)).toThrow('Path traversal detected');
|
|
97
|
-
} finally {
|
|
98
|
-
cleanupTempProject(tmpDir);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test('rejects absolute paths in boundary config', () => {
|
|
103
|
-
const tmpDir = createTempProject({
|
|
104
|
-
frameworkProtection: true,
|
|
105
|
-
protected: ['/etc/passwd'],
|
|
106
|
-
exceptions: [],
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
expect(() => readBoundaryConfig(tmpDir)).toThrow('Absolute path not allowed');
|
|
111
|
-
} finally {
|
|
112
|
-
cleanupTempProject(tmpDir);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('generatePermissions — frameworkProtection: true', () => {
|
|
118
|
-
test('generates deny rules covering all protected paths', () => {
|
|
119
|
-
const tmpDir = createTempProject({
|
|
120
|
-
frameworkProtection: true,
|
|
121
|
-
protected: ['.sinapse-ai/core/**', '.sinapse-ai/infrastructure/**', 'bin/sinapse.js'],
|
|
122
|
-
exceptions: ['.sinapse-ai/data/**'],
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Create infrastructure dir (no expansion for non-core paths)
|
|
126
|
-
fs.mkdirSync(path.join(tmpDir, '.sinapse-ai', 'infrastructure'), { recursive: true });
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const boundary = readBoundaryConfig(tmpDir);
|
|
130
|
-
const permissions = generatePermissions(boundary, tmpDir);
|
|
131
|
-
|
|
132
|
-
// Should have deny rules for core subdirs (events/**, utils/**, index.js) + infrastructure/** + bin/sinapse.js
|
|
133
|
-
expect(permissions.deny.length).toBeGreaterThan(0);
|
|
134
|
-
|
|
135
|
-
// Core expansion: events/**, utils/**, index.js → 6 deny rules (3 paths x 2 tools)
|
|
136
|
-
expect(permissions.deny).toContain('Edit(.sinapse-ai/core/events/**)');
|
|
137
|
-
expect(permissions.deny).toContain('Write(.sinapse-ai/core/events/**)');
|
|
138
|
-
expect(permissions.deny).toContain('Edit(.sinapse-ai/core/utils/**)');
|
|
139
|
-
expect(permissions.deny).toContain('Write(.sinapse-ai/core/utils/**)');
|
|
140
|
-
expect(permissions.deny).toContain('Edit(.sinapse-ai/core/index.js)');
|
|
141
|
-
expect(permissions.deny).toContain('Write(.sinapse-ai/core/index.js)');
|
|
142
|
-
|
|
143
|
-
// Non-core paths stay as globs
|
|
144
|
-
expect(permissions.deny).toContain('Edit(.sinapse-ai/infrastructure/**)');
|
|
145
|
-
expect(permissions.deny).toContain('Write(.sinapse-ai/infrastructure/**)');
|
|
146
|
-
expect(permissions.deny).toContain('Edit(bin/sinapse.js)');
|
|
147
|
-
expect(permissions.deny).toContain('Write(bin/sinapse.js)');
|
|
148
|
-
|
|
149
|
-
// Allow rules from exceptions
|
|
150
|
-
expect(permissions.allow).toContain('Edit(.sinapse-ai/data/**)');
|
|
151
|
-
expect(permissions.allow).toContain('Write(.sinapse-ai/data/**)');
|
|
152
|
-
expect(permissions.allow).toContain('Read(.sinapse-ai/**)');
|
|
153
|
-
} finally {
|
|
154
|
-
cleanupTempProject(tmpDir);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
test('all 9 protected paths from core-config produce deny rules', () => {
|
|
159
|
-
const projectRoot = path.resolve(__dirname, '../../../../..');
|
|
160
|
-
const boundary = readBoundaryConfig(projectRoot);
|
|
161
|
-
// Force frameworkProtection: true for this test (core-config may have it disabled for contributor mode)
|
|
162
|
-
boundary.frameworkProtection = true;
|
|
163
|
-
const permissions = generatePermissions(boundary, projectRoot);
|
|
164
|
-
|
|
165
|
-
// Verify all 9 config paths are covered
|
|
166
|
-
const protectedRoots = [
|
|
167
|
-
'.sinapse-ai/core/',
|
|
168
|
-
'.sinapse-ai/development/tasks/',
|
|
169
|
-
'.sinapse-ai/development/templates/',
|
|
170
|
-
'.sinapse-ai/development/checklists/',
|
|
171
|
-
'.sinapse-ai/development/workflows/',
|
|
172
|
-
'.sinapse-ai/infrastructure/',
|
|
173
|
-
'.sinapse-ai/constitution.md',
|
|
174
|
-
'bin/sinapse.js',
|
|
175
|
-
'bin/sinapse-init.js',
|
|
176
|
-
];
|
|
177
|
-
|
|
178
|
-
for (const root of protectedRoots) {
|
|
179
|
-
const hasDenyRule = permissions.deny.some(r => r.includes(root));
|
|
180
|
-
expect(hasDenyRule).toBe(true);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Verify deny rules use only Edit and Write (no MultiEdit)
|
|
184
|
-
for (const rule of permissions.deny) {
|
|
185
|
-
expect(rule).toMatch(/^(Edit|Write)\(/);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe('generatePermissions — frameworkProtection: false', () => {
|
|
191
|
-
test('produces no boundary deny rules', () => {
|
|
192
|
-
const boundary = {
|
|
193
|
-
frameworkProtection: false,
|
|
194
|
-
protected: ['.sinapse-ai/core/**', '.sinapse-ai/infrastructure/**'],
|
|
195
|
-
exceptions: ['.sinapse-ai/data/**'],
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
const permissions = generatePermissions(boundary, '/tmp');
|
|
199
|
-
|
|
200
|
-
expect(permissions.deny).toEqual([]);
|
|
201
|
-
expect(permissions.allow).toEqual([]);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
describe('idempotency', () => {
|
|
206
|
-
test('running generator twice produces identical output', () => {
|
|
207
|
-
const tmpDir = createTempProject({
|
|
208
|
-
frameworkProtection: true,
|
|
209
|
-
protected: ['.sinapse-ai/core/**', 'bin/sinapse.js'],
|
|
210
|
-
exceptions: ['.sinapse-ai/data/**'],
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
// First run
|
|
215
|
-
generate(tmpDir);
|
|
216
|
-
const firstRun = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
217
|
-
|
|
218
|
-
// Second run
|
|
219
|
-
generate(tmpDir);
|
|
220
|
-
const secondRun = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
221
|
-
|
|
222
|
-
expect(firstRun).toBe(secondRun);
|
|
223
|
-
} finally {
|
|
224
|
-
cleanupTempProject(tmpDir);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test('JSON output is valid and parseable', () => {
|
|
229
|
-
const tmpDir = createTempProject({
|
|
230
|
-
frameworkProtection: true,
|
|
231
|
-
protected: ['.sinapse-ai/core/**'],
|
|
232
|
-
exceptions: ['.sinapse-ai/data/**'],
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
generate(tmpDir);
|
|
237
|
-
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
238
|
-
const parsed = JSON.parse(content);
|
|
239
|
-
|
|
240
|
-
expect(parsed).toHaveProperty('permissions');
|
|
241
|
-
expect(parsed.permissions).toHaveProperty('deny');
|
|
242
|
-
expect(parsed.permissions).toHaveProperty('allow');
|
|
243
|
-
expect(Array.isArray(parsed.permissions.deny)).toBe(true);
|
|
244
|
-
expect(Array.isArray(parsed.permissions.allow)).toBe(true);
|
|
245
|
-
} finally {
|
|
246
|
-
cleanupTempProject(tmpDir);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
describe('section preservation', () => {
|
|
252
|
-
test('preserves user-set language key after generator run', () => {
|
|
253
|
-
const tmpDir = createTempProject(
|
|
254
|
-
{
|
|
255
|
-
frameworkProtection: true,
|
|
256
|
-
protected: ['bin/sinapse.js'],
|
|
257
|
-
exceptions: [],
|
|
258
|
-
},
|
|
259
|
-
{ language: 'pt', customSetting: true }
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
generate(tmpDir);
|
|
264
|
-
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
265
|
-
const parsed = JSON.parse(content);
|
|
266
|
-
|
|
267
|
-
expect(parsed.language).toBe('pt');
|
|
268
|
-
expect(parsed.customSetting).toBe(true);
|
|
269
|
-
expect(parsed.permissions).toBeDefined();
|
|
270
|
-
expect(parsed.permissions.deny.length).toBeGreaterThan(0);
|
|
271
|
-
} finally {
|
|
272
|
-
cleanupTempProject(tmpDir);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test('frameworkProtection false preserves user settings and removes permissions', () => {
|
|
277
|
-
const tmpDir = createTempProject(
|
|
278
|
-
{
|
|
279
|
-
frameworkProtection: false,
|
|
280
|
-
protected: ['bin/sinapse.js'],
|
|
281
|
-
exceptions: [],
|
|
282
|
-
},
|
|
283
|
-
{ language: 'pt', permissions: { deny: ['old-rule'], allow: [] } }
|
|
284
|
-
);
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
generate(tmpDir);
|
|
288
|
-
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
289
|
-
const parsed = JSON.parse(content);
|
|
290
|
-
|
|
291
|
-
expect(parsed.language).toBe('pt');
|
|
292
|
-
expect(parsed.permissions).toBeUndefined();
|
|
293
|
-
} finally {
|
|
294
|
-
cleanupTempProject(tmpDir);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
describe('CLI entry point', () => {
|
|
300
|
-
test('module exports required functions', () => {
|
|
301
|
-
expect(typeof generate).toBe('function');
|
|
302
|
-
expect(typeof readBoundaryConfig).toBe('function');
|
|
303
|
-
expect(typeof expandProtectedPaths).toBe('function');
|
|
304
|
-
expect(typeof expandExceptionPaths).toBe('function');
|
|
305
|
-
expect(typeof generatePermissions).toBe('function');
|
|
306
|
-
expect(typeof writeSettingsJson).toBe('function');
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
@@ -1,262 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Stream B (Frente 4.2) — git-hooks-installer propagation tests.
|
|
5
|
-
*
|
|
6
|
-
* Verifies that installGitHooks():
|
|
7
|
-
* - sets git core.hooksPath to the managed directory
|
|
8
|
-
* - writes a Node `.js` pre-commit hook with a `#!/usr/bin/env node` shebang
|
|
9
|
-
* - bundles the staged-secret-scan library so the hook is self-contained
|
|
10
|
-
* - is idempotent (running twice does not duplicate / corrupt)
|
|
11
|
-
* - detects husky and chains to it
|
|
12
|
-
* - actually BLOCKS a commit with a staged secret, and ALLOWS a placeholder
|
|
13
|
-
*
|
|
14
|
-
* All git is driven with `git -C <dir> ...` against a tmpdir created with
|
|
15
|
-
* fs.mkdtempSync(os.tmpdir()) so the global workspace-routing PreToolUse hook
|
|
16
|
-
* (which rewrites bare `git init` / `mkdir` outside the Workspace) is never
|
|
17
|
-
* triggered — we never run `mkdir` or `git init` as a bare cwd-based command.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
const fs = require('fs');
|
|
21
|
-
const os = require('os');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
|
|
24
|
-
const { runSafe } = require('../../../../.sinapse-ai/core/utils/spawn-safe');
|
|
25
|
-
const {
|
|
26
|
-
installGitHooks,
|
|
27
|
-
detectHusky,
|
|
28
|
-
buildPreCommitHook,
|
|
29
|
-
getCoreHooksPath,
|
|
30
|
-
MANAGED_HOOKS_DIRNAME,
|
|
31
|
-
MANAGED_MARKER,
|
|
32
|
-
} = require('../../src/installer/git-hooks-installer');
|
|
33
|
-
|
|
34
|
-
const GIT_IDENTITY_ARGS = [
|
|
35
|
-
'-c', 'user.email=test@sinapse.local',
|
|
36
|
-
'-c', 'user.name=SINAPSE Test',
|
|
37
|
-
'-c', 'commit.gpgsign=false',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/** Create an isolated temp dir (no mkdir command — pure Node fs). */
|
|
41
|
-
function makeTempDir() {
|
|
42
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'snps-githooks-'));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Initialize a git repo at dir using `git -C` (avoids workspace-routing). */
|
|
46
|
-
async function initRepo(dir) {
|
|
47
|
-
const res = await runSafe('git', ['-C', dir, 'init', '-q']);
|
|
48
|
-
if (!res.success) throw new Error(`git init failed: ${res.stderr}`);
|
|
49
|
-
// Ensure a deterministic default branch / identity for commits.
|
|
50
|
-
await runSafe('git', ['-C', dir, 'symbolic-ref', 'HEAD', 'refs/heads/main']);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/** Stage a file (write via Node fs, add via `git -C`). */
|
|
54
|
-
async function writeAndStage(dir, relPath, content) {
|
|
55
|
-
const full = path.join(dir, relPath);
|
|
56
|
-
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
57
|
-
fs.writeFileSync(full, content, 'utf8');
|
|
58
|
-
const res = await runSafe('git', ['-C', dir, 'add', '--', relPath]);
|
|
59
|
-
if (!res.success) throw new Error(`git add failed: ${res.stderr}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Attempt a commit; returns the runSafe result ({success, code, stderr}). */
|
|
63
|
-
async function commit(dir, message) {
|
|
64
|
-
return runSafe('git', [...GIT_IDENTITY_ARGS, '-C', dir, 'commit', '-m', message]);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function rmrf(dir) {
|
|
68
|
-
try {
|
|
69
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
70
|
-
} catch {
|
|
71
|
-
/* best-effort cleanup */
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
describe('git-hooks-installer (Stream B propagation)', () => {
|
|
76
|
-
let gitAvailable = true;
|
|
77
|
-
|
|
78
|
-
beforeAll(async () => {
|
|
79
|
-
const res = await runSafe('git', ['--version']).catch(() => ({ success: false }));
|
|
80
|
-
gitAvailable = res.success === true;
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('buildPreCommitHook (pure)', () => {
|
|
84
|
-
test('emits a Node hook with the node shebang and managed marker', () => {
|
|
85
|
-
const content = buildPreCommitHook();
|
|
86
|
-
expect(content.startsWith('#!/usr/bin/env node')).toBe(true);
|
|
87
|
-
expect(content).toContain(MANAGED_MARKER);
|
|
88
|
-
expect(content).toContain('staged-secret-scan.js');
|
|
89
|
-
// Never a shell or python hook.
|
|
90
|
-
expect(content).not.toMatch(/^#!\/usr\/bin\/env (sh|bash|python)/);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test('bakes an explicit chain path when provided', () => {
|
|
94
|
-
const content = buildPreCommitHook({ chainHookRel: '.git/hooks-old/pre-commit' });
|
|
95
|
-
expect(content).toContain('.git/hooks-old/pre-commit');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('detectHusky', () => {
|
|
100
|
-
test('reports false when no .husky/pre-commit exists', () => {
|
|
101
|
-
const dir = makeTempDir();
|
|
102
|
-
try {
|
|
103
|
-
expect(detectHusky(dir).hasHusky).toBe(false);
|
|
104
|
-
} finally {
|
|
105
|
-
rmrf(dir);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('reports true when .husky/pre-commit exists', () => {
|
|
110
|
-
const dir = makeTempDir();
|
|
111
|
-
try {
|
|
112
|
-
const huskyDir = path.join(dir, '.husky');
|
|
113
|
-
fs.mkdirSync(huskyDir, { recursive: true });
|
|
114
|
-
fs.writeFileSync(path.join(huskyDir, 'pre-commit'), '#!/usr/bin/env sh\nexit 0\n');
|
|
115
|
-
expect(detectHusky(dir).hasHusky).toBe(true);
|
|
116
|
-
} finally {
|
|
117
|
-
rmrf(dir);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('installGitHooks (filesystem + git config)', () => {
|
|
123
|
-
test('writes a managed Node pre-commit and sets core.hooksPath', async () => {
|
|
124
|
-
if (!gitAvailable) return;
|
|
125
|
-
const dir = makeTempDir();
|
|
126
|
-
try {
|
|
127
|
-
await initRepo(dir);
|
|
128
|
-
const result = await installGitHooks({ projectDir: dir });
|
|
129
|
-
|
|
130
|
-
expect(result.success).toBe(true);
|
|
131
|
-
expect(result.coreHooksPathSet).toBe(true);
|
|
132
|
-
|
|
133
|
-
const hookPath = path.join(dir, MANAGED_HOOKS_DIRNAME, 'pre-commit');
|
|
134
|
-
expect(fs.existsSync(hookPath)).toBe(true);
|
|
135
|
-
|
|
136
|
-
const hookContent = fs.readFileSync(hookPath, 'utf8');
|
|
137
|
-
expect(hookContent.startsWith('#!/usr/bin/env node')).toBe(true);
|
|
138
|
-
expect(hookContent).toContain(MANAGED_MARKER);
|
|
139
|
-
|
|
140
|
-
// The scanner lib is bundled next to the hook.
|
|
141
|
-
const libScanner = path.join(dir, MANAGED_HOOKS_DIRNAME, 'lib', 'staged-secret-scan.js');
|
|
142
|
-
expect(fs.existsSync(libScanner)).toBe(true);
|
|
143
|
-
|
|
144
|
-
// git config points at the managed dir (forward-slash, project-relative).
|
|
145
|
-
const configured = await getCoreHooksPath(dir);
|
|
146
|
-
expect(configured).toBe('.sinapse-ai/git-hooks');
|
|
147
|
-
} finally {
|
|
148
|
-
rmrf(dir);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
test('is idempotent — running twice keeps a single managed hook', async () => {
|
|
153
|
-
if (!gitAvailable) return;
|
|
154
|
-
const dir = makeTempDir();
|
|
155
|
-
try {
|
|
156
|
-
await initRepo(dir);
|
|
157
|
-
await installGitHooks({ projectDir: dir });
|
|
158
|
-
const firstContent = fs.readFileSync(path.join(dir, MANAGED_HOOKS_DIRNAME, 'pre-commit'), 'utf8');
|
|
159
|
-
|
|
160
|
-
const second = await installGitHooks({ projectDir: dir });
|
|
161
|
-
expect(second.success).toBe(true);
|
|
162
|
-
|
|
163
|
-
const secondContent = fs.readFileSync(path.join(dir, MANAGED_HOOKS_DIRNAME, 'pre-commit'), 'utf8');
|
|
164
|
-
expect(secondContent).toBe(firstContent);
|
|
165
|
-
|
|
166
|
-
// Exactly one pre-commit file in the managed dir (no .bak duplicates).
|
|
167
|
-
const entries = fs.readdirSync(path.join(dir, MANAGED_HOOKS_DIRNAME));
|
|
168
|
-
expect(entries.filter((e) => e.startsWith('pre-commit'))).toEqual(['pre-commit']);
|
|
169
|
-
|
|
170
|
-
// core.hooksPath is set once, not appended.
|
|
171
|
-
expect(await getCoreHooksPath(dir)).toBe('.sinapse-ai/git-hooks');
|
|
172
|
-
} finally {
|
|
173
|
-
rmrf(dir);
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test('detects husky and chains to it', async () => {
|
|
178
|
-
if (!gitAvailable) return;
|
|
179
|
-
const dir = makeTempDir();
|
|
180
|
-
try {
|
|
181
|
-
await initRepo(dir);
|
|
182
|
-
const huskyDir = path.join(dir, '.husky');
|
|
183
|
-
fs.mkdirSync(huskyDir, { recursive: true });
|
|
184
|
-
fs.writeFileSync(path.join(huskyDir, 'pre-commit'), '#!/usr/bin/env sh\nexit 0\n');
|
|
185
|
-
|
|
186
|
-
const result = await installGitHooks({ projectDir: dir });
|
|
187
|
-
expect(result.success).toBe(true);
|
|
188
|
-
expect(result.huskyDetected).toBe(true);
|
|
189
|
-
|
|
190
|
-
const hookContent = fs.readFileSync(path.join(dir, MANAGED_HOOKS_DIRNAME, 'pre-commit'), 'utf8');
|
|
191
|
-
// The runtime chain auto-detects .husky/pre-commit.
|
|
192
|
-
expect(hookContent).toContain('.husky');
|
|
193
|
-
expect(hookContent).toContain('pre-commit');
|
|
194
|
-
} finally {
|
|
195
|
-
rmrf(dir);
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
test('rejects a missing projectDir', async () => {
|
|
200
|
-
const result = await installGitHooks({});
|
|
201
|
-
expect(result.success).toBe(false);
|
|
202
|
-
expect(result.error).toMatch(/projectDir/);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('end-to-end commit gating', () => {
|
|
207
|
-
test('BLOCKS a commit that stages a real secret', async () => {
|
|
208
|
-
if (!gitAvailable) return;
|
|
209
|
-
const dir = makeTempDir();
|
|
210
|
-
try {
|
|
211
|
-
await initRepo(dir);
|
|
212
|
-
await installGitHooks({ projectDir: dir });
|
|
213
|
-
|
|
214
|
-
// AWS access key — matches the scanner's AKIA pattern.
|
|
215
|
-
await writeAndStage(dir, 'config.js', 'const k = "AKIAIOSFODNN7EXAMPLE";\n');
|
|
216
|
-
|
|
217
|
-
const res = await commit(dir, 'should be blocked');
|
|
218
|
-
expect(res.success).toBe(false);
|
|
219
|
-
expect((res.stderr || '') + (res.stdout || '')).toMatch(/Secret Scan|blocked/i);
|
|
220
|
-
} finally {
|
|
221
|
-
rmrf(dir);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test('ALLOWS a commit of a placeholder .env.example', async () => {
|
|
226
|
-
if (!gitAvailable) return;
|
|
227
|
-
const dir = makeTempDir();
|
|
228
|
-
try {
|
|
229
|
-
await initRepo(dir);
|
|
230
|
-
await installGitHooks({ projectDir: dir });
|
|
231
|
-
|
|
232
|
-
await writeAndStage(dir, '.env.example', 'STRIPE_KEY=your-key-here\nDB_PASSWORD=changeme\n');
|
|
233
|
-
|
|
234
|
-
const res = await commit(dir, 'add example env');
|
|
235
|
-
expect(res.success).toBe(true);
|
|
236
|
-
} finally {
|
|
237
|
-
rmrf(dir);
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
test('fail-CLOSED: a broken scanner lib blocks the commit', async () => {
|
|
242
|
-
if (!gitAvailable) return;
|
|
243
|
-
const dir = makeTempDir();
|
|
244
|
-
try {
|
|
245
|
-
await initRepo(dir);
|
|
246
|
-
await installGitHooks({ projectDir: dir });
|
|
247
|
-
|
|
248
|
-
// Corrupt the bundled scanner so require() throws inside the hook.
|
|
249
|
-
const libScanner = path.join(dir, MANAGED_HOOKS_DIRNAME, 'lib', 'staged-secret-scan.js');
|
|
250
|
-
fs.writeFileSync(libScanner, 'throw new Error("corrupted scanner");\n', 'utf8');
|
|
251
|
-
|
|
252
|
-
await writeAndStage(dir, 'safe.txt', 'totally harmless content\n');
|
|
253
|
-
|
|
254
|
-
const res = await commit(dir, 'should still be blocked');
|
|
255
|
-
expect(res.success).toBe(false);
|
|
256
|
-
expect((res.stderr || '') + (res.stdout || '')).toMatch(/fail-closed|blocking commit/i);
|
|
257
|
-
} finally {
|
|
258
|
-
rmrf(dir);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
});
|