sinapse-ai 9.4.0 → 9.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +10 -4
- package/.claude/hooks/enforce-architecture-first.py +197 -197
- package/.claude/hooks/enforce-git-push-authority.sh +25 -4
- package/.claude/hooks/mind-clone-governance.py +193 -193
- package/.claude/hooks/read-protection.py +152 -152
- package/.claude/hooks/sql-governance.py +183 -183
- package/.claude/hooks/verify-packages.cjs +83 -0
- package/.claude/hooks/write-path-validation.py +195 -195
- package/.claude/rules/hook-governance.md +1 -0
- package/.claude/rules/mandatory-delegation.md +24 -0
- package/.claude/rules/project-intelligence.md +63 -0
- package/.claude/rules/response-format.md +4 -0
- package/.claude/rules/safe-collaboration.md +4 -2
- package/.claude/rules/security-data-protection.md +18 -0
- package/.claude/rules/squad-awareness.md +93 -67
- package/.claude/rules/token-economy.md +148 -0
- package/.codex/agents/analyst.md +90 -0
- package/.codex/agents/architect.md +78 -0
- package/.codex/agents/data-engineer.md +38 -0
- package/.codex/agents/developer.md +97 -0
- package/.codex/agents/devops.md +121 -0
- package/.codex/agents/product-lead.md +27 -0
- package/.codex/agents/project-lead.md +28 -0
- package/.codex/agents/quality-gate.md +89 -0
- package/.codex/agents/sprint-lead.md +28 -0
- package/.codex/agents/squad-creator.md +58 -0
- package/.codex/agents/ux-design-expert.md +28 -0
- package/.sinapse-ai/core/code-intel/registry-syncer.js +56 -3
- package/.sinapse-ai/core/doctor/checks/agent-memory.js +5 -1
- package/.sinapse-ai/core/doctor/checks/claude-md.js +4 -1
- package/.sinapse-ai/core/doctor/checks/code-intel.js +5 -1
- package/.sinapse-ai/core/doctor/checks/commands-count.js +4 -1
- package/.sinapse-ai/core/doctor/checks/constitution-consistency.js +4 -1
- package/.sinapse-ai/core/doctor/checks/core-config.js +4 -1
- package/.sinapse-ai/core/doctor/checks/entity-registry.js +6 -1
- package/.sinapse-ai/core/doctor/checks/git-hooks.js +5 -1
- package/.sinapse-ai/core/doctor/checks/graph-dashboard.js +4 -1
- package/.sinapse-ai/core/doctor/checks/hooks-claude-count.js +5 -1
- package/.sinapse-ai/core/doctor/checks/ide-sync.js +4 -1
- package/.sinapse-ai/core/doctor/checks/node-version.js +4 -1
- package/.sinapse-ai/core/doctor/checks/npm-packages.js +4 -1
- package/.sinapse-ai/core/doctor/checks/rules-files.js +4 -1
- package/.sinapse-ai/core/doctor/checks/settings-json.js +4 -1
- package/.sinapse-ai/core/doctor/checks/skills-count.js +4 -1
- package/.sinapse-ai/core/doctor/index.js +157 -50
- package/.sinapse-ai/core/ids/registry-updater.js +6 -1
- package/.sinapse-ai/core/logger/index.js +319 -0
- package/.sinapse-ai/core/orchestration/terminal-spawner.js +2 -2
- package/.sinapse-ai/core/telemetry/index.js +247 -0
- package/.sinapse-ai/data/entity-registry.yaml +1384 -944
- package/.sinapse-ai/development/agents/architect.md +5 -0
- package/.sinapse-ai/development/agents/data-engineer.md +38 -0
- package/.sinapse-ai/development/agents/developer.md +28 -0
- package/.sinapse-ai/development/agents/devops.md +4 -0
- package/.sinapse-ai/development/agents/product-lead.md +27 -0
- package/.sinapse-ai/development/agents/project-lead.md +28 -0
- package/.sinapse-ai/development/agents/quality-gate.md +4 -0
- package/.sinapse-ai/development/agents/sprint-lead/MEMORY.md +8 -0
- package/.sinapse-ai/development/agents/sprint-lead.md +28 -0
- package/.sinapse-ai/development/agents/squad-creator.md +58 -0
- package/.sinapse-ai/development/agents/ux-design-expert.md +28 -0
- package/.sinapse-ai/development/knowledge-base/agent-communication-protocol.md +127 -0
- package/.sinapse-ai/development/knowledge-base/database-scaling-patterns.md +374 -0
- package/.sinapse-ai/development/knowledge-base/environment-deployment-patterns.md +353 -0
- package/.sinapse-ai/development/knowledge-base/gotchas-patterns.md +224 -0
- package/.sinapse-ai/development/knowledge-base/infrastructure-decision-framework.md +221 -0
- package/.sinapse-ai/development/knowledge-base/security-pre-deploy-checklist.md +410 -0
- package/.sinapse-ai/development/knowledge-base/software-architecture-patterns.md +299 -0
- package/.sinapse-ai/development/knowledge-base/token-economy-guide.md +198 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +5 -1
- package/.sinapse-ai/development/skills/captcha-handler.md +82 -0
- package/.sinapse-ai/development/skills/chrome-brain.md +81 -0
- package/.sinapse-ai/development/skills/deploy-readiness.md +93 -0
- package/.sinapse-ai/development/skills/model-router.md +92 -0
- package/.sinapse-ai/development/skills/sinapse-methodology.md +175 -0
- package/.sinapse-ai/development/skills/story-fast-track.md +71 -0
- package/.sinapse-ai/development/tasks/dev-develop-story.md +10 -0
- package/.sinapse-ai/development/tasks/environment-promotion-pipeline.md +582 -0
- package/.sinapse-ai/development/tasks/generate-agent-handoff.md +223 -0
- package/.sinapse-ai/development/tasks/infrastructure-assessment.md +432 -0
- package/.sinapse-ai/development/tasks/load-testing-setup.md +611 -0
- package/.sinapse-ai/development/tasks/observability-blueprint.md +562 -0
- package/.sinapse-ai/development/templates/legal/breach-notification-tmpl.md +113 -0
- package/.sinapse-ai/development/templates/legal/privacy-policy-tmpl.md +93 -0
- package/.sinapse-ai/development/templates/legal/terms-of-service-tmpl.md +85 -0
- 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/development/workflows/story-development-cycle.yaml +40 -1
- package/.sinapse-ai/hooks/ids-post-commit.js +22 -0
- package/.sinapse-ai/infrastructure/contracts/compatibility/README.md +42 -0
- package/.sinapse-ai/infrastructure/contracts/compatibility/sinapse-current.yaml +35 -0
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -127
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -71
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -51
- package/.sinapse-ai/infrastructure/scripts/pr-review-ai.js +16 -13
- package/.sinapse-ai/infrastructure/scripts/setup-project-infra.js +128 -0
- package/.sinapse-ai/infrastructure/scripts/test-discovery.js +8 -3
- package/.sinapse-ai/infrastructure/scripts/validate-manifest-parity.js +380 -0
- package/.sinapse-ai/infrastructure/scripts/validate-parity.js +76 -25
- package/.sinapse-ai/infrastructure/templates/coderabbit.yaml.template +280 -280
- package/.sinapse-ai/infrastructure/templates/config/env.example +16 -0
- package/.sinapse-ai/infrastructure/templates/config/gitignore-additions.tmpl +59 -0
- package/.sinapse-ai/infrastructure/templates/github/CODEOWNERS.template +12 -0
- package/.sinapse-ai/infrastructure/templates/github/PULL_REQUEST_TEMPLATE.md +29 -0
- package/.sinapse-ai/infrastructure/templates/github/ci-template.yml +77 -0
- package/.sinapse-ai/infrastructure/templates/github/issue-templates/bug_report.md +34 -0
- package/.sinapse-ai/infrastructure/templates/github/issue-templates/feature_request.md +19 -0
- 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/sinapse-sync.yaml.template +183 -183
- package/.sinapse-ai/install-manifest.yaml +275 -140
- package/.sinapse-ai/local-config.yaml.template +65 -65
- package/.sinapse-ai/monitor/hooks/lib/__init__.py +2 -2
- package/.sinapse-ai/monitor/hooks/lib/enrich.py +59 -59
- package/.sinapse-ai/monitor/hooks/lib/send_event.py +48 -48
- package/.sinapse-ai/monitor/hooks/notification.py +30 -30
- package/.sinapse-ai/monitor/hooks/post_tool_use.py +46 -46
- package/.sinapse-ai/monitor/hooks/pre_compact.py +30 -30
- package/.sinapse-ai/monitor/hooks/pre_tool_use.py +41 -41
- package/.sinapse-ai/monitor/hooks/stop.py +30 -30
- package/.sinapse-ai/monitor/hooks/subagent_stop.py +30 -30
- package/.sinapse-ai/monitor/hooks/user_prompt_submit.py +39 -39
- 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/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/story-tmpl.yaml +59 -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/.sinapse-ai/scripts/diagnostics/health-dashboard/package-lock.json +427 -355
- package/LICENSE +34 -34
- package/README.en.md +167 -20
- package/README.md +190 -22
- package/bin/cli.js +510 -196
- package/bin/postinstall.js +564 -0
- package/bin/sinapse-cli +283 -283
- package/bin/sinapse-graph.js +9 -0
- package/bin/sinapse-init.js +36 -4
- package/bin/sinapse-minimal.js +20 -9
- package/bin/sinapse.js +202 -122
- package/bin/utils/deprecation-warning.js +46 -0
- package/bin/utils/pre-push-safety.js +14 -0
- package/docs/TELEMETRY.md +131 -0
- package/docs/chrome-brain-upgrade-plan.md +624 -0
- package/docs/framework/orqx-plan.md +1 -1
- package/docs/installation/chrome-brain.md +17 -7
- package/docs/mega-upgrade-orchestration-plan.md +71 -0
- package/docs/pt/contributing.md +20 -0
- package/docs/research-synthesis-for-upgrade.md +511 -0
- package/docs/security-audit-report.md +306 -0
- package/package.json +20 -8
- package/packages/installer/src/config/configure-environment.js +19 -44
- package/packages/installer/src/detection/detect-project-type.js +181 -63
- package/packages/installer/src/installer/manifest-signature.js +32 -17
- package/packages/installer/src/wizard/i18n.js +12 -0
- package/packages/installer/src/wizard/ide-config-generator.js +8 -39
- package/packages/installer/src/wizard/index.js +119 -14
- package/packages/installer/src/wizard/questions.js +2 -3
- package/packages/installer/tests/integration/environment-configuration.test.js +7 -5
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +138 -1
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +3 -3
- package/packages/sinapse-install/bin/edmcp.js +0 -0
- package/packages/sinapse-install/bin/sinapse-install.js +0 -0
- package/packages/sinapse-pro-cli/bin/sinapse-pro.js +0 -0
- package/scripts/check-markdown-links.py +353 -353
- package/scripts/coverage-report-summary.js +169 -0
- package/scripts/generate-install-manifest.js +6 -2
- package/scripts/release-readiness.js +169 -0
- package/scripts/test-install-matrix-local.sh +153 -0
- package/scripts/validate-install-docs.js +394 -0
- package/scripts/validate-no-external-refs.js +376 -0
- package/scripts/validate-squad-orqx.js +302 -0
- package/scripts/validate-story-meta.js +263 -0
- package/squads/claude-code-mastery/CHANGELOG.md +1 -1
- package/squads/claude-code-mastery/README.md +2 -2
- package/squads/claude-code-mastery/squad.yaml +1 -1
- package/squads/squad-artdir/README.md +90 -0
- package/squads/squad-artdir/agents/accessibility-guardian.md +184 -0
- package/squads/squad-artdir/agents/artdir-orqx.md +145 -0
- package/squads/squad-artdir/agents/color-psychologist.md +166 -0
- package/squads/squad-artdir/agents/cro-persuasion.md +161 -0
- package/squads/squad-artdir/agents/design-system-architect.md +100 -0
- package/squads/squad-artdir/agents/ia-architect.md +169 -0
- package/squads/squad-artdir/agents/interaction-designer.md +162 -0
- package/squads/squad-artdir/agents/layout-engineer.md +163 -0
- package/squads/squad-artdir/agents/motion-architect.md +185 -0
- package/squads/squad-artdir/agents/platform-aesthetic-director.md +84 -0
- package/squads/squad-artdir/agents/premium-packaging-strategist.md +107 -0
- package/squads/squad-artdir/agents/product-surface-director.md +86 -0
- package/squads/squad-artdir/agents/type-systemist.md +138 -0
- package/squads/squad-artdir/agents/visual-strategist.md +127 -0
- package/squads/squad-artdir/checklists/seven-pillars-validation-checklist.md +172 -0
- package/squads/squad-artdir/knowledge-base/case-nyo-ia-reference.md +289 -0
- package/squads/squad-artdir/knowledge-base/deliverables-templates.md +457 -0
- package/squads/squad-artdir/knowledge-base/motion-technique-catalog.md +247 -0
- package/squads/squad-artdir/knowledge-base/premium-packaging-principles.md +133 -0
- package/squads/squad-artdir/knowledge-base/psychological-toolkit.md +229 -0
- package/squads/squad-artdir/knowledge-base/saas-art-direction-canon.md +242 -0
- package/squads/squad-artdir/knowledge-base/seven-pillars-framework.md +289 -0
- package/squads/squad-artdir/knowledge-base/ten-pillars-framework.md +221 -0
- package/squads/squad-artdir/package.json +20 -0
- package/squads/squad-artdir/squad.yaml +271 -0
- package/squads/squad-artdir/tasks/audit-conversion.md +97 -0
- package/squads/squad-artdir/tasks/audit-drift-multi-surface.md +55 -0
- package/squads/squad-artdir/tasks/consult-saas-canon.md +54 -0
- package/squads/squad-artdir/tasks/create-art-direction-brief.md +110 -0
- package/squads/squad-artdir/tasks/create-premium-packaging-brief.md +61 -0
- package/squads/squad-artdir/tasks/create-wireflow.md +84 -0
- package/squads/squad-artdir/tasks/design-color-system.md +81 -0
- package/squads/squad-artdir/tasks/design-product-surface.md +60 -0
- package/squads/squad-artdir/tasks/design-token-system.md +58 -0
- package/squads/squad-artdir/tasks/diagnose-visual-language.md +92 -0
- package/squads/squad-artdir/tasks/first-5-minutes-choreography.md +65 -0
- package/squads/squad-artdir/tasks/specify-motion-system.md +84 -0
- package/squads/squad-artdir/tasks/validate-against-pillars.md +143 -0
- package/squads/squad-artdir/templates/art-direction-brief-template.md +215 -0
- package/squads/squad-artdir/workflows/conversion-audit-cycle.yaml +78 -0
- package/squads/squad-artdir/workflows/full-art-direction-cycle.yaml +98 -0
- package/squads/squad-artdir/workflows/saas-platform-art-direction-cycle.yaml +174 -0
- package/squads/squad-brand/knowledge-base/ai-visual-generation-canon.md +234 -0
- package/squads/squad-brand/squad.yaml +20 -6
- package/squads/squad-claude/knowledge-base/context-window-optimization.md +1 -1
- package/squads/squad-claude/knowledge-base/swarm-orchestration-patterns.md +2 -2
- package/squads/squad-content/knowledge-base/ai-native-content-loop.md +220 -0
- package/squads/squad-content/knowledge-base/signal-intelligence-v2.md +234 -0
- package/squads/squad-content/knowledge-base/task-ownership-map.md +235 -0
- package/squads/squad-content/squad.yaml +187 -27
- package/squads/squad-copy/knowledge-base/ai-copy-human-loop-canon.md +235 -0
- package/squads/squad-copy/squad.yaml +19 -4
- package/squads/squad-design/knowledge-base/cross-surface-token-canon.md +209 -0
- package/squads/squad-design/squad.yaml +19 -4
- package/.sinapse-ai/core/registry/service-registry.json +0 -6346
- package/.sinapse-ai/data/registry-update-log.jsonl +0 -1323
- package/.sinapse-ai/manifests/agents.csv +0 -29
- package/.sinapse-ai/manifests/tasks.csv +0 -204
- package/.sinapse-ai/manifests/workers.csv +0 -196
- package/squads/squad-growth/tasks/calculate-sample-size.md +0 -121
- package/squads/squad-paidmedia/tasks/calculate-sample-size.md +0 -57
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* setup-project-infra.js — Apply gitflow & infrastructure templates to a project
|
|
4
|
+
*
|
|
5
|
+
* Checks which templates are missing from the target project and copies only
|
|
6
|
+
* missing ones (never overwrites). Run via: sinapse setup-infra [target-dir]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const C = {
|
|
13
|
+
reset: '\x1b[0m', green: '\x1b[32m', yellow: '\x1b[33m',
|
|
14
|
+
cyan: '\x1b[36m', bold: '\x1b[1m',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function log(color, symbol, msg) {
|
|
18
|
+
console.log(`${color}${symbol} ${msg}${C.reset}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const TEMPLATES_DIR = path.resolve(__dirname, '..', 'templates');
|
|
22
|
+
|
|
23
|
+
// Mapping: template source -> project destination
|
|
24
|
+
const TEMPLATE_MAP = [
|
|
25
|
+
// GitHub templates
|
|
26
|
+
{ src: 'github/PULL_REQUEST_TEMPLATE.md', dest: '.github/PULL_REQUEST_TEMPLATE.md' },
|
|
27
|
+
{ src: 'github/issue-templates/bug_report.md', dest: '.github/ISSUE_TEMPLATE/bug_report.md' },
|
|
28
|
+
{ src: 'github/issue-templates/feature_request.md', dest: '.github/ISSUE_TEMPLATE/feature_request.md' },
|
|
29
|
+
{ src: 'github/ci-template.yml', dest: '.github/workflows/ci.yml' },
|
|
30
|
+
{ src: 'github/CODEOWNERS.template', dest: '.github/CODEOWNERS', transform: true },
|
|
31
|
+
// Config templates
|
|
32
|
+
{ src: 'config/env.example', dest: '.env.example' },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function ensureDir(filePath) {
|
|
36
|
+
const dir = path.dirname(filePath);
|
|
37
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function detectProjectName(targetDir) {
|
|
41
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
42
|
+
if (fs.existsSync(pkgPath)) {
|
|
43
|
+
try { return JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).name || path.basename(targetDir); }
|
|
44
|
+
catch { /* fall through */ }
|
|
45
|
+
}
|
|
46
|
+
return path.basename(targetDir);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectMaintainer(targetDir) {
|
|
50
|
+
try {
|
|
51
|
+
const { execSync } = require('child_process');
|
|
52
|
+
const name = execSync('git config user.name', { cwd: targetDir, encoding: 'utf-8' }).trim();
|
|
53
|
+
return name.toLowerCase().replace(/\s+/g, '') || 'maintainer';
|
|
54
|
+
} catch { return 'maintainer'; }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function applyGitignoreAdditions(targetDir) {
|
|
58
|
+
const additions = fs.readFileSync(path.join(TEMPLATES_DIR, 'config', 'gitignore-additions.tmpl'), 'utf-8');
|
|
59
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
60
|
+
const marker = '# SINAPSE Project Ignores';
|
|
61
|
+
|
|
62
|
+
if (fs.existsSync(gitignorePath)) {
|
|
63
|
+
const existing = fs.readFileSync(gitignorePath, 'utf-8');
|
|
64
|
+
if (existing.includes(marker)) {
|
|
65
|
+
log(C.yellow, '[SKIP]', '.gitignore already has SINAPSE patterns');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
fs.appendFileSync(gitignorePath, '\n' + additions);
|
|
69
|
+
log(C.green, '[ADD]', '.gitignore updated with SINAPSE patterns');
|
|
70
|
+
} else {
|
|
71
|
+
fs.writeFileSync(gitignorePath, additions);
|
|
72
|
+
log(C.green, '[NEW]', '.gitignore created');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function run(targetDir) {
|
|
77
|
+
targetDir = path.resolve(targetDir || process.cwd());
|
|
78
|
+
const projectName = detectProjectName(targetDir);
|
|
79
|
+
const maintainer = detectMaintainer(targetDir);
|
|
80
|
+
|
|
81
|
+
console.log(`\n${C.cyan}${C.bold}=== SINAPSE Project Infrastructure Setup ===${C.reset}`);
|
|
82
|
+
console.log(`Target: ${targetDir}`);
|
|
83
|
+
console.log(`Project: ${projectName}`);
|
|
84
|
+
console.log(`Maintainer: ${maintainer}\n`);
|
|
85
|
+
|
|
86
|
+
let added = 0, skipped = 0;
|
|
87
|
+
|
|
88
|
+
for (const entry of TEMPLATE_MAP) {
|
|
89
|
+
const destPath = path.join(targetDir, entry.dest);
|
|
90
|
+
|
|
91
|
+
if (fs.existsSync(destPath)) {
|
|
92
|
+
log(C.yellow, '[SKIP]', `${entry.dest} (already exists)`);
|
|
93
|
+
skipped++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ensureDir(destPath);
|
|
98
|
+
let content = fs.readFileSync(path.join(TEMPLATES_DIR, entry.src), 'utf-8');
|
|
99
|
+
|
|
100
|
+
if (entry.transform) {
|
|
101
|
+
content = content
|
|
102
|
+
.replace(/\{\{PROJECT_NAME\}\}/g, projectName)
|
|
103
|
+
.replace(/\{\{MAINTAINER\}\}/g, maintainer);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Replace common variables in all templates
|
|
107
|
+
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
108
|
+
content = content.replace(/\{\{NODE_VERSION\}\}/g, '20');
|
|
109
|
+
|
|
110
|
+
fs.writeFileSync(destPath, content);
|
|
111
|
+
log(C.green, '[ADD]', entry.dest);
|
|
112
|
+
added++;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Handle .gitignore separately (merge, don't overwrite)
|
|
116
|
+
applyGitignoreAdditions(targetDir);
|
|
117
|
+
|
|
118
|
+
console.log(`\n${C.cyan}${C.bold}=== Summary ===${C.reset}`);
|
|
119
|
+
console.log(`Added: ${added} | Skipped: ${skipped}`);
|
|
120
|
+
console.log(`\nRun again safely anytime — existing files are never overwritten.\n`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// CLI entry
|
|
124
|
+
if (require.main === module) {
|
|
125
|
+
run(process.argv[2]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { run };
|
|
@@ -720,10 +720,15 @@ class CoverageAnalyzer {
|
|
|
720
720
|
const match = content.match(/module\.exports\s*=\s*(\{[\s\S]*\})/);
|
|
721
721
|
if (match) {
|
|
722
722
|
try {
|
|
723
|
-
//
|
|
724
|
-
return
|
|
723
|
+
// Try JSON.parse first (safe — no code execution)
|
|
724
|
+
return JSON.parse(match[1]);
|
|
725
725
|
} catch {
|
|
726
|
-
|
|
726
|
+
// If not valid JSON (e.g., contains JS expressions), fall back to require()
|
|
727
|
+
try {
|
|
728
|
+
return require(fullPath);
|
|
729
|
+
} catch {
|
|
730
|
+
return {};
|
|
731
|
+
}
|
|
727
732
|
}
|
|
728
733
|
}
|
|
729
734
|
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Validate Install Manifest Parity
|
|
4
|
+
*
|
|
5
|
+
* @script .sinapse-ai/infrastructure/scripts/validate-manifest-parity.js
|
|
6
|
+
* @story A.4 - Install Manifest Parity & Regeneration (epic-install-ux-hardening)
|
|
7
|
+
*
|
|
8
|
+
* Purpose:
|
|
9
|
+
* Catches drift between the real repository and the two source-of-truth
|
|
10
|
+
* files that track framework entities:
|
|
11
|
+
* - .sinapse-ai/install-manifest.yaml
|
|
12
|
+
* - .sinapse-ai/data/entity-registry.yaml
|
|
13
|
+
*
|
|
14
|
+
* Unlike scripts/validate-manifest.js (which hashes every file in the
|
|
15
|
+
* manifest), this script focuses on domain-level parity for the 4
|
|
16
|
+
* domains most likely to drift silently:
|
|
17
|
+
* - agents → .sinapse-ai/development/agents/*.md (top-level)
|
|
18
|
+
* - tasks → .sinapse-ai/development/tasks/*.md
|
|
19
|
+
* - templates → .sinapse-ai/development/templates/*.{md,yaml}
|
|
20
|
+
* - checklists → .sinapse-ai/development/checklists/*.md
|
|
21
|
+
*
|
|
22
|
+
* It also cross-validates that entity-registry.yaml agrees with the
|
|
23
|
+
* manifest and with reality on the number of agents and their names.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* node .sinapse-ai/infrastructure/scripts/validate-manifest-parity.js
|
|
27
|
+
* npm run validate:manifest:parity
|
|
28
|
+
*
|
|
29
|
+
* Options:
|
|
30
|
+
* --json Emit machine-readable JSON (for CI)
|
|
31
|
+
* --root <dir> Override the repo root (for tests)
|
|
32
|
+
*
|
|
33
|
+
* Exit codes:
|
|
34
|
+
* 0 - Parity OK
|
|
35
|
+
* 1 - Drift detected
|
|
36
|
+
* 2 - Script/internal error
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
'use strict';
|
|
40
|
+
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const yaml = require('js-yaml');
|
|
44
|
+
|
|
45
|
+
// Domains to check. Each entry describes one logical domain of entities
|
|
46
|
+
// and how to find its real files on disk.
|
|
47
|
+
const DOMAINS = [
|
|
48
|
+
{
|
|
49
|
+
name: 'agents',
|
|
50
|
+
dir: 'development/agents',
|
|
51
|
+
// Only top-level .md files count as "agents". Sub-folder MEMORY.md
|
|
52
|
+
// files belong to the agent's memory, not the agent roster.
|
|
53
|
+
recursive: false,
|
|
54
|
+
exts: ['.md'],
|
|
55
|
+
// Exclude internal files that live alongside agents but are not agents.
|
|
56
|
+
exclude: (name) => /^(README|CHANGELOG|INDEX|MEMORY)\.md$/i.test(name),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'tasks',
|
|
60
|
+
dir: 'development/tasks',
|
|
61
|
+
recursive: false,
|
|
62
|
+
exts: ['.md'],
|
|
63
|
+
exclude: (name) => /^(README|CHANGELOG|INDEX)\.md$/i.test(name),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'templates',
|
|
67
|
+
dir: 'development/templates',
|
|
68
|
+
recursive: false,
|
|
69
|
+
exts: ['.md', '.yaml', '.yml'],
|
|
70
|
+
exclude: (name) => /^README\.md$/i.test(name),
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'checklists',
|
|
74
|
+
dir: 'development/checklists',
|
|
75
|
+
recursive: false,
|
|
76
|
+
exts: ['.md'],
|
|
77
|
+
exclude: (name) => /^(README|INDEX)\.md$/i.test(name),
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Find every file on disk that belongs to a given domain.
|
|
83
|
+
* @param {string} sinapseCoreDir - Path to `.sinapse-ai/`
|
|
84
|
+
* @param {object} domain - Domain descriptor from DOMAINS
|
|
85
|
+
* @returns {Set<string>} Set of POSIX-style relative paths (e.g.
|
|
86
|
+
* `development/agents/developer.md`)
|
|
87
|
+
*/
|
|
88
|
+
function scanDomain(sinapseCoreDir, domain) {
|
|
89
|
+
const domainDir = path.join(sinapseCoreDir, domain.dir);
|
|
90
|
+
const found = new Set();
|
|
91
|
+
|
|
92
|
+
if (!fs.existsSync(domainDir)) return found;
|
|
93
|
+
|
|
94
|
+
const entries = fs.readdirSync(domainDir, { withFileTypes: true });
|
|
95
|
+
for (const entry of entries) {
|
|
96
|
+
if (entry.isDirectory()) continue; // not recursing
|
|
97
|
+
if (!entry.isFile()) continue;
|
|
98
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
99
|
+
if (!domain.exts.includes(ext)) continue;
|
|
100
|
+
if (domain.exclude && domain.exclude(entry.name)) continue;
|
|
101
|
+
found.add(`${domain.dir}/${entry.name}`);
|
|
102
|
+
}
|
|
103
|
+
return found;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Collect every manifest entry whose path matches a given domain directory.
|
|
108
|
+
* @param {Array<{path:string}>} manifestFiles
|
|
109
|
+
* @param {object} domain
|
|
110
|
+
* @returns {Set<string>}
|
|
111
|
+
*/
|
|
112
|
+
function collectManifestEntries(manifestFiles, domain) {
|
|
113
|
+
const set = new Set();
|
|
114
|
+
const prefix = `${domain.dir}/`;
|
|
115
|
+
for (const entry of manifestFiles) {
|
|
116
|
+
const p = String(entry.path || '').replace(/\\/g, '/');
|
|
117
|
+
if (!p.startsWith(prefix)) continue;
|
|
118
|
+
// Only keep top-level (same depth filter as scanDomain) unless
|
|
119
|
+
// the domain is configured as recursive.
|
|
120
|
+
const rest = p.slice(prefix.length);
|
|
121
|
+
if (!domain.recursive && rest.includes('/')) continue;
|
|
122
|
+
const ext = path.extname(rest).toLowerCase();
|
|
123
|
+
if (!domain.exts.includes(ext)) continue;
|
|
124
|
+
if (domain.exclude && domain.exclude(path.basename(rest))) continue;
|
|
125
|
+
set.add(p);
|
|
126
|
+
}
|
|
127
|
+
return set;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Diff two sets, returning extras and missings relative to disk.
|
|
132
|
+
* @param {Set<string>} onDisk
|
|
133
|
+
* @param {Set<string>} inManifest
|
|
134
|
+
* @returns {{missingFromManifest:string[], missingFromDisk:string[]}}
|
|
135
|
+
*/
|
|
136
|
+
function diffSets(onDisk, inManifest) {
|
|
137
|
+
const missingFromManifest = [];
|
|
138
|
+
const missingFromDisk = [];
|
|
139
|
+
for (const p of onDisk) {
|
|
140
|
+
if (!inManifest.has(p)) missingFromManifest.push(p);
|
|
141
|
+
}
|
|
142
|
+
for (const p of inManifest) {
|
|
143
|
+
if (!onDisk.has(p)) missingFromDisk.push(p);
|
|
144
|
+
}
|
|
145
|
+
missingFromManifest.sort();
|
|
146
|
+
missingFromDisk.sort();
|
|
147
|
+
return { missingFromManifest, missingFromDisk };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Load a YAML file, returning null if it does not exist.
|
|
152
|
+
*/
|
|
153
|
+
function loadYaml(filePath) {
|
|
154
|
+
if (!fs.existsSync(filePath)) return null;
|
|
155
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
156
|
+
return yaml.load(raw);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Extract the set of top-level agent names from entity-registry.yaml.
|
|
161
|
+
* Registry shape: entities.agents.{agentName}: { ... }
|
|
162
|
+
* @returns {Set<string>}
|
|
163
|
+
*/
|
|
164
|
+
function getRegistryAgentNames(registry) {
|
|
165
|
+
const names = new Set();
|
|
166
|
+
const agents = registry && registry.entities && registry.entities.agents;
|
|
167
|
+
if (!agents || typeof agents !== 'object') return names;
|
|
168
|
+
for (const key of Object.keys(agents)) names.add(key);
|
|
169
|
+
return names;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Run the full parity check.
|
|
174
|
+
* @param {string} repoRoot - Repo root directory
|
|
175
|
+
* @returns {object} report
|
|
176
|
+
*/
|
|
177
|
+
function runParityCheck(repoRoot) {
|
|
178
|
+
const sinapseCoreDir = path.join(repoRoot, '.sinapse-ai');
|
|
179
|
+
const manifestPath = path.join(sinapseCoreDir, 'install-manifest.yaml');
|
|
180
|
+
const registryPath = path.join(sinapseCoreDir, 'data', 'entity-registry.yaml');
|
|
181
|
+
|
|
182
|
+
const report = {
|
|
183
|
+
ok: true,
|
|
184
|
+
errors: [],
|
|
185
|
+
domains: {},
|
|
186
|
+
registry: {
|
|
187
|
+
checked: false,
|
|
188
|
+
agentCountManifest: 0,
|
|
189
|
+
agentCountRegistry: 0,
|
|
190
|
+
agentCountDisk: 0,
|
|
191
|
+
extraInRegistry: [],
|
|
192
|
+
missingInRegistry: [],
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const manifest = loadYaml(manifestPath);
|
|
197
|
+
if (!manifest) {
|
|
198
|
+
report.ok = false;
|
|
199
|
+
report.errors.push(`install-manifest.yaml not found at ${manifestPath}`);
|
|
200
|
+
return report;
|
|
201
|
+
}
|
|
202
|
+
if (!Array.isArray(manifest.files)) {
|
|
203
|
+
report.ok = false;
|
|
204
|
+
report.errors.push('install-manifest.yaml has no `files` array');
|
|
205
|
+
return report;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const domain of DOMAINS) {
|
|
209
|
+
const onDisk = scanDomain(sinapseCoreDir, domain);
|
|
210
|
+
const inManifest = collectManifestEntries(manifest.files, domain);
|
|
211
|
+
const { missingFromManifest, missingFromDisk } = diffSets(onDisk, inManifest);
|
|
212
|
+
|
|
213
|
+
const domainOk = missingFromManifest.length === 0 && missingFromDisk.length === 0;
|
|
214
|
+
if (!domainOk) report.ok = false;
|
|
215
|
+
|
|
216
|
+
report.domains[domain.name] = {
|
|
217
|
+
ok: domainOk,
|
|
218
|
+
dir: domain.dir,
|
|
219
|
+
diskCount: onDisk.size,
|
|
220
|
+
manifestCount: inManifest.size,
|
|
221
|
+
missingFromManifest,
|
|
222
|
+
missingFromDisk,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Cross-validate entity-registry.yaml against manifest + disk for agents.
|
|
227
|
+
const registry = loadYaml(registryPath);
|
|
228
|
+
if (!registry) {
|
|
229
|
+
report.ok = false;
|
|
230
|
+
report.errors.push(`entity-registry.yaml not found at ${registryPath}`);
|
|
231
|
+
} else {
|
|
232
|
+
report.registry.checked = true;
|
|
233
|
+
const agentsDomain = report.domains.agents;
|
|
234
|
+
const diskAgentNames = new Set(
|
|
235
|
+
Array.from(scanDomain(sinapseCoreDir, DOMAINS[0])).map((p) =>
|
|
236
|
+
path.basename(p, '.md'),
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
const registryAgentNames = getRegistryAgentNames(registry);
|
|
240
|
+
report.registry.agentCountDisk = diskAgentNames.size;
|
|
241
|
+
report.registry.agentCountManifest = agentsDomain ? agentsDomain.manifestCount : 0;
|
|
242
|
+
report.registry.agentCountRegistry = registryAgentNames.size;
|
|
243
|
+
|
|
244
|
+
// Registry should mention exactly the same agent names as disk.
|
|
245
|
+
for (const name of registryAgentNames) {
|
|
246
|
+
if (!diskAgentNames.has(name)) report.registry.extraInRegistry.push(name);
|
|
247
|
+
}
|
|
248
|
+
for (const name of diskAgentNames) {
|
|
249
|
+
if (!registryAgentNames.has(name)) report.registry.missingInRegistry.push(name);
|
|
250
|
+
}
|
|
251
|
+
report.registry.extraInRegistry.sort();
|
|
252
|
+
report.registry.missingInRegistry.sort();
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
report.registry.extraInRegistry.length > 0 ||
|
|
256
|
+
report.registry.missingInRegistry.length > 0 ||
|
|
257
|
+
report.registry.agentCountRegistry !== report.registry.agentCountDisk
|
|
258
|
+
) {
|
|
259
|
+
report.ok = false;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return report;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Render a human-readable report.
|
|
268
|
+
*/
|
|
269
|
+
function printReport(report) {
|
|
270
|
+
const line = '='.repeat(60);
|
|
271
|
+
console.log(line);
|
|
272
|
+
console.log('SINAPSE Install Manifest Parity Report');
|
|
273
|
+
console.log(line);
|
|
274
|
+
|
|
275
|
+
if (report.errors.length > 0) {
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log('ERRORS:');
|
|
278
|
+
for (const err of report.errors) console.log(` - ${err}`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log('Domains:');
|
|
283
|
+
for (const [name, info] of Object.entries(report.domains)) {
|
|
284
|
+
const status = info.ok ? 'OK' : 'DRIFT';
|
|
285
|
+
console.log(
|
|
286
|
+
` ${name.padEnd(11)} ${status.padEnd(6)} disk=${info.diskCount} manifest=${info.manifestCount}`,
|
|
287
|
+
);
|
|
288
|
+
if (info.missingFromManifest.length > 0) {
|
|
289
|
+
console.log(` Missing from manifest (${info.missingFromManifest.length}):`);
|
|
290
|
+
for (const p of info.missingFromManifest.slice(0, 20)) console.log(` + ${p}`);
|
|
291
|
+
if (info.missingFromManifest.length > 20) {
|
|
292
|
+
console.log(` ... and ${info.missingFromManifest.length - 20} more`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (info.missingFromDisk.length > 0) {
|
|
296
|
+
console.log(` In manifest but missing on disk (${info.missingFromDisk.length}):`);
|
|
297
|
+
for (const p of info.missingFromDisk.slice(0, 20)) console.log(` - ${p}`);
|
|
298
|
+
if (info.missingFromDisk.length > 20) {
|
|
299
|
+
console.log(` ... and ${info.missingFromDisk.length - 20} more`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (report.registry.checked) {
|
|
305
|
+
const r = report.registry;
|
|
306
|
+
const status =
|
|
307
|
+
r.extraInRegistry.length === 0 &&
|
|
308
|
+
r.missingInRegistry.length === 0 &&
|
|
309
|
+
r.agentCountDisk === r.agentCountRegistry
|
|
310
|
+
? 'OK'
|
|
311
|
+
: 'DRIFT';
|
|
312
|
+
console.log('');
|
|
313
|
+
console.log(`Entity Registry cross-check: ${status}`);
|
|
314
|
+
console.log(
|
|
315
|
+
` agents: disk=${r.agentCountDisk} manifest=${r.agentCountManifest} registry=${r.agentCountRegistry}`,
|
|
316
|
+
);
|
|
317
|
+
if (r.extraInRegistry.length > 0) {
|
|
318
|
+
console.log(` Extra in registry (${r.extraInRegistry.length}):`);
|
|
319
|
+
for (const n of r.extraInRegistry) console.log(` ! ${n}`);
|
|
320
|
+
}
|
|
321
|
+
if (r.missingInRegistry.length > 0) {
|
|
322
|
+
console.log(` Missing from registry (${r.missingInRegistry.length}):`);
|
|
323
|
+
for (const n of r.missingInRegistry) console.log(` ? ${n}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log('-'.repeat(60));
|
|
329
|
+
if (report.ok) {
|
|
330
|
+
console.log('PARITY OK — manifest, disk, and registry agree.');
|
|
331
|
+
} else {
|
|
332
|
+
console.log('PARITY DRIFT — fix with:');
|
|
333
|
+
console.log(' 1. npm run generate:manifest');
|
|
334
|
+
console.log(' 2. review entity-registry.yaml agents section');
|
|
335
|
+
console.log(' 3. rerun npm run validate:manifest:parity');
|
|
336
|
+
}
|
|
337
|
+
console.log('-'.repeat(60));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function parseArgs(argv) {
|
|
341
|
+
const opts = { json: false, root: process.cwd() };
|
|
342
|
+
for (let i = 2; i < argv.length; i++) {
|
|
343
|
+
const a = argv[i];
|
|
344
|
+
if (a === '--json') opts.json = true;
|
|
345
|
+
else if (a === '--root' && argv[i + 1]) {
|
|
346
|
+
opts.root = argv[++i];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return opts;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function main() {
|
|
353
|
+
const opts = parseArgs(process.argv);
|
|
354
|
+
try {
|
|
355
|
+
const report = runParityCheck(opts.root);
|
|
356
|
+
if (opts.json) {
|
|
357
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
358
|
+
} else {
|
|
359
|
+
printReport(report);
|
|
360
|
+
}
|
|
361
|
+
process.exit(report.ok ? 0 : 1);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
console.error('validate-manifest-parity: internal error:', err && err.message);
|
|
364
|
+
if (process.env.DEBUG) console.error(err && err.stack);
|
|
365
|
+
process.exit(2);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (require.main === module) {
|
|
370
|
+
main();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
module.exports = {
|
|
374
|
+
runParityCheck,
|
|
375
|
+
scanDomain,
|
|
376
|
+
collectManifestEntries,
|
|
377
|
+
diffSets,
|
|
378
|
+
getRegistryAgentNames,
|
|
379
|
+
DOMAINS,
|
|
380
|
+
};
|
|
@@ -20,34 +20,71 @@ function parseArgs(argv = process.argv.slice(2)) {
|
|
|
20
20
|
return {
|
|
21
21
|
quiet: args.has('--quiet') || args.has('-q'),
|
|
22
22
|
json: args.has('--json'),
|
|
23
|
+
fast: args.has('--fast'),
|
|
23
24
|
contractPath: contractArg ? contractArg.slice('--contract='.length) : null,
|
|
24
25
|
diffPath: diffArg ? diffArg.slice('--diff='.length) : null,
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
function extractSyncFailureDetails(rawOutput) {
|
|
30
|
+
if (!rawOutput || typeof rawOutput !== 'string') {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const details = [];
|
|
34
|
+
const driftMatch = rawOutput.match(/\|\s*Drift\s*\|\s*(\d+)\s*\|/i);
|
|
35
|
+
const missingMatch = rawOutput.match(/\|\s*Missing\s*\|\s*(\d+)\s*\|/i);
|
|
36
|
+
const orphanedMatch = rawOutput.match(/\|\s*Orphaned\s*\|\s*(\d+)\s*\|/i);
|
|
37
|
+
if (driftMatch && Number(driftMatch[1]) > 0) {
|
|
38
|
+
details.push(`${driftMatch[1]} file(s) drifted from source-of-truth`);
|
|
39
|
+
}
|
|
40
|
+
if (missingMatch && Number(missingMatch[1]) > 0) {
|
|
41
|
+
details.push(`${missingMatch[1]} file(s) missing from IDE mirror`);
|
|
42
|
+
}
|
|
43
|
+
if (orphanedMatch && Number(orphanedMatch[1]) > 0) {
|
|
44
|
+
details.push(`${orphanedMatch[1]} orphaned file(s) in IDE mirror`);
|
|
45
|
+
}
|
|
46
|
+
const fileBlocks = rawOutput.match(/(?:^|\n)-\s+`?([^`\n]+\.md)`?/g);
|
|
47
|
+
if (fileBlocks) {
|
|
48
|
+
for (const block of fileBlocks.slice(0, 5)) {
|
|
49
|
+
const name = block.replace(/^[\s\-`]+|`+\s*$/g, '');
|
|
50
|
+
if (name) details.push(` - ${name}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return details;
|
|
54
|
+
}
|
|
55
|
+
|
|
28
56
|
function runSyncValidate(ide, projectRoot) {
|
|
29
57
|
const script = path.join('.sinapse-ai', 'infrastructure', 'scripts', 'ide-sync', 'index.js');
|
|
30
58
|
const result = spawnSync('node', [script, 'validate', '--ide', ide, '--strict'], {
|
|
31
59
|
cwd: projectRoot,
|
|
32
60
|
encoding: 'utf8',
|
|
33
61
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
62
|
+
const raw = result.stdout || result.stderr || '';
|
|
63
|
+
if (result.status === 0) {
|
|
64
|
+
return { ok: true, errors: [], warnings: [], raw };
|
|
65
|
+
}
|
|
66
|
+
const details = extractSyncFailureDetails(raw);
|
|
67
|
+
const errors = [
|
|
68
|
+
`Sync validation failed for ${ide}`,
|
|
69
|
+
...details,
|
|
70
|
+
`Run \`npm run sync:ide\` then re-stage the .claude/ and .codex/ changes`,
|
|
71
|
+
];
|
|
72
|
+
return { ok: false, errors, warnings: [], raw };
|
|
40
73
|
}
|
|
41
74
|
|
|
42
75
|
function getDefaultContractPath(projectRoot = process.cwd()) {
|
|
43
|
-
|
|
76
|
+
const contractsDir = path.join(
|
|
44
77
|
projectRoot,
|
|
45
78
|
'.sinapse-ai',
|
|
46
79
|
'infrastructure',
|
|
47
80
|
'contracts',
|
|
48
81
|
'compatibility',
|
|
49
|
-
'sinapse-4.0.4.yaml',
|
|
50
82
|
);
|
|
83
|
+
const currentPath = path.join(contractsDir, 'sinapse-current.yaml');
|
|
84
|
+
if (fs.existsSync(currentPath)) {
|
|
85
|
+
return currentPath;
|
|
86
|
+
}
|
|
87
|
+
return path.join(contractsDir, 'sinapse-4.0.4.yaml');
|
|
51
88
|
}
|
|
52
89
|
|
|
53
90
|
function loadCompatibilityContract(contractPath) {
|
|
@@ -218,6 +255,7 @@ function diffCompatibilityContracts(currentContract, previousContract) {
|
|
|
218
255
|
|
|
219
256
|
function runParityValidation(options = {}, deps = {}) {
|
|
220
257
|
const projectRoot = options.projectRoot || process.cwd();
|
|
258
|
+
const fast = Boolean(options.fast);
|
|
221
259
|
const runSync = deps.runSyncValidate || runSyncValidate;
|
|
222
260
|
const runCodexSync = deps.validateCodexSync || validateCodexSync;
|
|
223
261
|
const runClaudeIntegration = deps.validateClaudeIntegration || validateClaudeIntegration;
|
|
@@ -228,12 +266,12 @@ function runParityValidation(options = {}, deps = {}) {
|
|
|
228
266
|
? path.resolve(projectRoot, options.contractPath)
|
|
229
267
|
: getDefaultContractPath(projectRoot);
|
|
230
268
|
const loadContract = deps.loadCompatibilityContract || loadCompatibilityContract;
|
|
231
|
-
const contract = loadContract(resolvedContractPath);
|
|
269
|
+
const contract = fast ? null : loadContract(resolvedContractPath);
|
|
232
270
|
const resolvedDiffPath = options.diffPath ? path.resolve(projectRoot, options.diffPath) : null;
|
|
233
|
-
const previousContract = resolvedDiffPath ? loadContract(resolvedDiffPath) : null;
|
|
271
|
+
const previousContract = resolvedDiffPath && !fast ? loadContract(resolvedDiffPath) : null;
|
|
234
272
|
const docsPath = path.join(projectRoot, 'docs', 'ide-integration.md');
|
|
235
273
|
const docsPathRelative = path.relative(projectRoot, docsPath);
|
|
236
|
-
const
|
|
274
|
+
const fullChecks = [
|
|
237
275
|
{ id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
|
|
238
276
|
{ id: 'claude-integration', exec: () => runClaudeIntegration({ projectRoot }) },
|
|
239
277
|
{ id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
|
|
@@ -241,31 +279,42 @@ function runParityValidation(options = {}, deps = {}) {
|
|
|
241
279
|
{ id: 'codex-skills', exec: () => runCodexSkills({ projectRoot, strict: true, quiet: true }) },
|
|
242
280
|
{ id: 'paths', exec: () => runPaths({ projectRoot }) },
|
|
243
281
|
];
|
|
282
|
+
const fastChecks = [
|
|
283
|
+
{ id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
|
|
284
|
+
{ id: 'codex-sync', exec: () => runCodexSync({ projectRoot, quiet: true }) },
|
|
285
|
+
{ id: 'paths', exec: () => runPaths({ projectRoot }) },
|
|
286
|
+
];
|
|
287
|
+
const checks = fast ? fastChecks : fullChecks;
|
|
244
288
|
|
|
245
289
|
const results = checks.map((check) => {
|
|
246
290
|
const normalized = normalizeResult(check.exec());
|
|
247
291
|
return { id: check.id, ...normalized };
|
|
248
292
|
});
|
|
249
293
|
const resultById = Object.fromEntries(results.map((r) => [r.id, r]));
|
|
250
|
-
const contractViolations =
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
294
|
+
const contractViolations = fast
|
|
295
|
+
? []
|
|
296
|
+
: validateCompatibilityContract(contract, resultById, {
|
|
297
|
+
docsPath,
|
|
298
|
+
docsPathRelative,
|
|
299
|
+
});
|
|
300
|
+
const contractSummary = fast
|
|
301
|
+
? null
|
|
302
|
+
: contract
|
|
303
|
+
? {
|
|
304
|
+
release: contract.release || null,
|
|
305
|
+
path: path.relative(projectRoot, resolvedContractPath),
|
|
306
|
+
}
|
|
307
|
+
: {
|
|
308
|
+
release: null,
|
|
309
|
+
path: path.relative(projectRoot, resolvedContractPath),
|
|
310
|
+
};
|
|
263
311
|
|
|
264
312
|
return {
|
|
265
313
|
ok: results.every((r) => r.ok) && contractViolations.length === 0,
|
|
314
|
+
fast,
|
|
266
315
|
checks: results,
|
|
267
316
|
contract: contractSummary,
|
|
268
|
-
contractDiff: diffCompatibilityContracts(contract, previousContract),
|
|
317
|
+
contractDiff: fast ? null : diffCompatibilityContracts(contract, previousContract),
|
|
269
318
|
contractViolations,
|
|
270
319
|
};
|
|
271
320
|
}
|
|
@@ -347,4 +396,6 @@ module.exports = {
|
|
|
347
396
|
normalizeResult,
|
|
348
397
|
formatHumanReport,
|
|
349
398
|
diffCompatibilityContracts,
|
|
399
|
+
getDefaultContractPath,
|
|
400
|
+
extractSyncFailureDetails,
|
|
350
401
|
};
|