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
package/bin/cli.js
CHANGED
|
@@ -5,6 +5,15 @@ const path = require('path');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const os = require('os');
|
|
7
7
|
|
|
8
|
+
// Story A.2 — unified logger. Levels: error/warn/info/debug.
|
|
9
|
+
// Flags: --verbose, --debug, --quiet, --json. Default level: warn.
|
|
10
|
+
const {
|
|
11
|
+
getLogger,
|
|
12
|
+
shouldShowHeader,
|
|
13
|
+
markFirstRunDone,
|
|
14
|
+
} = require('../.sinapse-ai/core/logger');
|
|
15
|
+
const logger = getLogger();
|
|
16
|
+
|
|
8
17
|
// ══════════════════════════════════════════════════════════════════════════════
|
|
9
18
|
// Sinapse CLI — Global + Local installer for Claude Code agent squads
|
|
10
19
|
// ══════════════════════════════════════════════════════════════════════════════
|
|
@@ -29,18 +38,31 @@ const NC = '\x1b[0m';
|
|
|
29
38
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
30
39
|
|
|
31
40
|
function header() {
|
|
41
|
+
// Story A.2 AC 7 — ASCII art only on --verbose / --debug OR first-run.
|
|
42
|
+
// Never in --quiet or --json mode.
|
|
43
|
+
if (!shouldShowHeader(logger)) return;
|
|
32
44
|
const W = `${WHITE}${BOLD}`;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
const lines = [
|
|
46
|
+
'',
|
|
47
|
+
`${W} ███████╗██╗███╗ ██╗ █████╗ ██████╗ ███████╗███████╗ █████╗ ██╗${NC}`,
|
|
48
|
+
`${W} ██╔════╝██║████╗ ██║██╔══██╗██╔══██╗██╔════╝██╔════╝ ██╔══██╗██║${NC}`,
|
|
49
|
+
`${W} ███████╗██║██╔██╗ ██║███████║██████╔╝███████╗█████╗ ███████║██║${NC}`,
|
|
50
|
+
`${W} ╚════██║██║██║╚██╗██║██╔══██║██╔═══╝ ╚════██║██╔══╝ ██╔══██║██║${NC}`,
|
|
51
|
+
`${W} ███████║██║██║ ╚████║██║ ██║██║ ███████║███████╗ ██║ ██║██║${NC}`,
|
|
52
|
+
`${W} ╚══════╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝╚═╝${NC}`,
|
|
53
|
+
'',
|
|
54
|
+
`${DIM} Seu copiloto de inteligencia artificial${NC}`,
|
|
55
|
+
`${DIM} v${VERSION}${NC}`,
|
|
56
|
+
'',
|
|
57
|
+
];
|
|
58
|
+
// Write directly to stdout so the header still shows on first-run even when
|
|
59
|
+
// the logger level is `warn` (default). `shouldShowHeader` is the single
|
|
60
|
+
// gate that decides whether we get here at all.
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
try { process.stdout.write(`${line}\n`); } catch { /* ignore */ }
|
|
63
|
+
}
|
|
64
|
+
// Mark first-run done so subsequent runs stay clean unless --verbose.
|
|
65
|
+
markFirstRunDone();
|
|
44
66
|
}
|
|
45
67
|
|
|
46
68
|
function getSquads(baseDir) {
|
|
@@ -144,6 +166,89 @@ function rmDirSync(dir) {
|
|
|
144
166
|
}
|
|
145
167
|
}
|
|
146
168
|
|
|
169
|
+
// syncDirSync — Story 10.20
|
|
170
|
+
// Copies src -> dest, only writing files whose mtime+size differ. Returns a
|
|
171
|
+
// {added, updated, unchanged, removed} delta. Removes orphaned files in dest
|
|
172
|
+
// that are no longer present in src. Used by install upsert mode to avoid
|
|
173
|
+
// the rmDir+copyDir destruction of user-customized files.
|
|
174
|
+
function syncDirSync(src, dest, delta = { added: 0, updated: 0, unchanged: 0, removed: 0 }) {
|
|
175
|
+
if (!fs.existsSync(src)) return delta;
|
|
176
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
177
|
+
const srcEntries = new Set();
|
|
178
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
179
|
+
srcEntries.add(entry.name);
|
|
180
|
+
const sp = path.join(src, entry.name);
|
|
181
|
+
const dp = path.join(dest, entry.name);
|
|
182
|
+
if (entry.isDirectory()) {
|
|
183
|
+
syncDirSync(sp, dp, delta);
|
|
184
|
+
} else {
|
|
185
|
+
const srcStat = fs.statSync(sp);
|
|
186
|
+
let needsWrite = true;
|
|
187
|
+
if (fs.existsSync(dp)) {
|
|
188
|
+
try {
|
|
189
|
+
const dstStat = fs.statSync(dp);
|
|
190
|
+
if (dstStat.size === srcStat.size && dstStat.mtimeMs >= srcStat.mtimeMs) {
|
|
191
|
+
needsWrite = false;
|
|
192
|
+
delta.unchanged += 1;
|
|
193
|
+
} else {
|
|
194
|
+
delta.updated += 1;
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
delta.updated += 1;
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
delta.added += 1;
|
|
201
|
+
}
|
|
202
|
+
if (needsWrite) fs.copyFileSync(sp, dp);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (fs.existsSync(dest)) {
|
|
206
|
+
for (const name of fs.readdirSync(dest)) {
|
|
207
|
+
if (!srcEntries.has(name)) {
|
|
208
|
+
const orphan = path.join(dest, name);
|
|
209
|
+
try {
|
|
210
|
+
const stat = fs.statSync(orphan);
|
|
211
|
+
if (stat.isDirectory()) rmDirSync(orphan);
|
|
212
|
+
else fs.unlinkSync(orphan);
|
|
213
|
+
delta.removed += 1;
|
|
214
|
+
} catch { /* skip */ }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return delta;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// detectExistingInstall — Story 10.20
|
|
222
|
+
// Returns { upsert: true, prevMeta, language, llm } when ~/.sinapse/metadata.json
|
|
223
|
+
// exists, or { upsert: false } otherwise. Reads ~/.claude/settings.json for
|
|
224
|
+
// language reuse.
|
|
225
|
+
function detectExistingInstall() {
|
|
226
|
+
const metaPath = path.join(SINAPSE_HOME, 'metadata.json');
|
|
227
|
+
if (!fs.existsSync(metaPath)) return { upsert: false };
|
|
228
|
+
let prevMeta = null;
|
|
229
|
+
try {
|
|
230
|
+
prevMeta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
231
|
+
} catch {
|
|
232
|
+
return { upsert: false };
|
|
233
|
+
}
|
|
234
|
+
let language = null;
|
|
235
|
+
try {
|
|
236
|
+
const settingsPath = path.join(HOME, '.claude', 'settings.json');
|
|
237
|
+
if (fs.existsSync(settingsPath)) {
|
|
238
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
239
|
+
if (settings && typeof settings.language === 'string') {
|
|
240
|
+
language = settings.language === 'portuguese' ? 'pt' : (settings.language === 'english' ? 'en' : null);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
} catch { /* fall through */ }
|
|
244
|
+
return {
|
|
245
|
+
upsert: true,
|
|
246
|
+
prevMeta,
|
|
247
|
+
language,
|
|
248
|
+
llm: prevMeta && typeof prevMeta.llm === 'string' ? prevMeta.llm : null,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
147
252
|
function toForwardSlash(p) {
|
|
148
253
|
return p.replace(/\\/g, '/');
|
|
149
254
|
}
|
|
@@ -153,6 +258,10 @@ function toForwardSlash(p) {
|
|
|
153
258
|
* @returns {Promise<string>} 'claude-code' | 'codex' | 'both'
|
|
154
259
|
*/
|
|
155
260
|
async function promptLlmChoice() {
|
|
261
|
+
// Non-TTY (CI, Docker, scripts, agents): skip prompt, default to claude-code.
|
|
262
|
+
if (!process.stdin.isTTY) {
|
|
263
|
+
return 'claude-code';
|
|
264
|
+
}
|
|
156
265
|
try {
|
|
157
266
|
const inquirer = require('inquirer');
|
|
158
267
|
const { llms } = await inquirer.prompt([{
|
|
@@ -172,11 +281,11 @@ async function promptLlmChoice() {
|
|
|
172
281
|
const readline = require('readline');
|
|
173
282
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
174
283
|
return new Promise((resolve) => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
284
|
+
logger.always(`${CYAN} Escolha sua LLM:${NC}`);
|
|
285
|
+
logger.always(` ${GREEN}1${NC}) Claude Code ${DIM}(Recomendado)${NC}`);
|
|
286
|
+
logger.always(` ${GREEN}2${NC}) Codex CLI`);
|
|
287
|
+
logger.always(` ${GREEN}3${NC}) Ambos`);
|
|
288
|
+
logger.always('');
|
|
180
289
|
rl.question(` ${BOLD}Opcao [1/2/3]:${NC} `, (answer) => {
|
|
181
290
|
rl.close();
|
|
182
291
|
const choice = (answer || '1').trim();
|
|
@@ -190,40 +299,63 @@ async function promptLlmChoice() {
|
|
|
190
299
|
|
|
191
300
|
// ── Global Install ───────────────────────────────────────────────────────────
|
|
192
301
|
|
|
193
|
-
async function cmdInstallGlobal() {
|
|
302
|
+
async function cmdInstallGlobal(opts = {}) {
|
|
194
303
|
header();
|
|
195
|
-
console.log(`${BOLD} Bem-vindo ao SINAPSE AI!${NC}`);
|
|
196
|
-
console.log(`${DIM} Vamos configurar seu copiloto de inteligencia artificial.${NC}`);
|
|
197
|
-
console.log('');
|
|
198
304
|
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
305
|
+
// Story 10.20 — Upsert detection
|
|
306
|
+
const force = Boolean(opts.force);
|
|
307
|
+
const reconfigure = Boolean(opts.reconfigure);
|
|
308
|
+
const existing = force ? { upsert: false } : detectExistingInstall();
|
|
309
|
+
const isUpsert = existing.upsert;
|
|
310
|
+
|
|
311
|
+
if (force && fs.existsSync(path.join(SINAPSE_HOME, 'metadata.json'))) {
|
|
312
|
+
logger.always(`${YELLOW}--force flag detected: running fresh install (overwriting any existing install).${NC}`);
|
|
313
|
+
logger.always('');
|
|
314
|
+
} else if (isUpsert) {
|
|
315
|
+
const prevVer = existing.prevMeta && existing.prevMeta.version ? existing.prevMeta.version : 'unknown';
|
|
316
|
+
logger.always(`${BOLD} Detected existing install (v${prevVer}). Refreshing in place...${NC}`);
|
|
317
|
+
logger.always(`${DIM} Use --force to wipe and reinstall fresh. Use --reconfigure to re-prompt language/LLM.${NC}`);
|
|
318
|
+
logger.always('');
|
|
319
|
+
} else {
|
|
320
|
+
logger.always(`${BOLD} Bem-vindo ao SINAPSE AI!${NC}`);
|
|
321
|
+
logger.always(`${DIM} Vamos configurar seu copiloto de inteligencia artificial.${NC}`);
|
|
322
|
+
logger.always('');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Language selection (skipped in upsert mode if already known, or non-TTY)
|
|
326
|
+
// Story 10.35: --reconfigure forces prompt even in upsert mode
|
|
327
|
+
let language = (isUpsert && !reconfigure && existing.language) ? existing.language : null;
|
|
328
|
+
if (!language) {
|
|
329
|
+
language = 'pt';
|
|
330
|
+
if (process.stdin.isTTY) {
|
|
331
|
+
try {
|
|
332
|
+
const inquirer = require('inquirer');
|
|
333
|
+
const langAnswer = await inquirer.prompt([{
|
|
334
|
+
type: 'list',
|
|
335
|
+
name: 'language',
|
|
336
|
+
message: 'Language / Idioma:',
|
|
337
|
+
choices: [
|
|
338
|
+
{ name: 'Portugues', value: 'pt' },
|
|
339
|
+
{ name: 'English', value: 'en' },
|
|
340
|
+
],
|
|
341
|
+
default: 'pt',
|
|
342
|
+
}]);
|
|
343
|
+
language = langAnswer.language;
|
|
344
|
+
} catch {
|
|
345
|
+
// Fallback: readline
|
|
346
|
+
const readline = require('readline');
|
|
347
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
348
|
+
language = await new Promise((resolve) => {
|
|
349
|
+
logger.always(` ${CYAN}Language / Idioma:${NC}`);
|
|
350
|
+
logger.always(` ${GREEN}1${NC}) Portugues`);
|
|
351
|
+
logger.always(` ${GREEN}2${NC}) English`);
|
|
352
|
+
rl.question(` ${BOLD}[1/2]:${NC} `, (answer) => {
|
|
353
|
+
rl.close();
|
|
354
|
+
resolve((answer || '1').trim() === '2' ? 'en' : 'pt');
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
227
359
|
}
|
|
228
360
|
|
|
229
361
|
// Save language to ~/.claude/settings.json
|
|
@@ -239,48 +371,71 @@ async function cmdInstallGlobal() {
|
|
|
239
371
|
fs.writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
240
372
|
} catch { /* non-critical */ }
|
|
241
373
|
|
|
242
|
-
// LLM selection (
|
|
243
|
-
|
|
374
|
+
// LLM selection (skipped in upsert mode if previous llm known)
|
|
375
|
+
// Story 10.35: --reconfigure forces prompt even in upsert mode
|
|
376
|
+
const llmChoice = (isUpsert && !reconfigure && existing.llm) ? existing.llm : await promptLlmChoice();
|
|
244
377
|
|
|
245
|
-
|
|
246
|
-
|
|
378
|
+
logger.always('');
|
|
379
|
+
logger.always(`${BOLD}Installing Sinapse globally...${NC}\n`);
|
|
247
380
|
|
|
248
381
|
// Validate package — squads live in squads/ subdirectory
|
|
249
382
|
const squadsDir = path.join(ROOT, 'squads');
|
|
250
383
|
const squads = getSquads(fs.existsSync(squadsDir) ? squadsDir : ROOT);
|
|
251
384
|
if (squads.length === 0) {
|
|
252
|
-
|
|
385
|
+
logger.error(`${RED}ERROR: No squad directories found in package.${NC}`);
|
|
253
386
|
process.exit(1);
|
|
254
387
|
}
|
|
255
388
|
|
|
256
389
|
// Phase 1: Copy squads to ~/.sinapse/
|
|
257
|
-
|
|
390
|
+
logger.always(`${CYAN}Phase 1:${NC} ${isUpsert ? 'Refreshing' : 'Copying'} squads to ~/.sinapse/`);
|
|
258
391
|
fs.mkdirSync(SINAPSE_HOME, { recursive: true });
|
|
259
392
|
|
|
260
393
|
const squadsSrcBase = fs.existsSync(squadsDir) ? squadsDir : ROOT;
|
|
261
394
|
let totalAgents = 0;
|
|
395
|
+
const totalDelta = { added: 0, updated: 0, unchanged: 0, removed: 0 };
|
|
396
|
+
let squadsRefreshed = 0;
|
|
397
|
+
let squadsAdded = 0;
|
|
262
398
|
for (const squad of squads) {
|
|
263
399
|
const src = path.join(squadsSrcBase, squad.name);
|
|
264
400
|
const dest = path.join(SINAPSE_HOME, squad.name);
|
|
265
|
-
|
|
266
|
-
|
|
401
|
+
if (isUpsert) {
|
|
402
|
+
const existedBefore = fs.existsSync(dest);
|
|
403
|
+
const delta = syncDirSync(src, dest);
|
|
404
|
+
totalDelta.added += delta.added;
|
|
405
|
+
totalDelta.updated += delta.updated;
|
|
406
|
+
totalDelta.unchanged += delta.unchanged;
|
|
407
|
+
totalDelta.removed += delta.removed;
|
|
408
|
+
if (existedBefore) squadsRefreshed += 1; else squadsAdded += 1;
|
|
409
|
+
logger.always(` ${GREEN}OK${NC} ${squad.name} (${delta.added} added, ${delta.updated} updated, ${delta.unchanged} unchanged${delta.removed ? ', ' + delta.removed + ' removed' : ''})`);
|
|
410
|
+
} else {
|
|
411
|
+
rmDirSync(dest);
|
|
412
|
+
copyDirSync(src, dest);
|
|
413
|
+
logger.always(` ${GREEN}OK${NC} ${squad.name} (${squad.agents} agents)`);
|
|
414
|
+
}
|
|
267
415
|
totalAgents += squad.agents;
|
|
268
|
-
console.log(` ${GREEN}OK${NC} ${squad.name} (${squad.agents} agents)`);
|
|
269
416
|
}
|
|
270
417
|
|
|
271
418
|
// Copy sinapse/ orqx squad
|
|
272
419
|
const sinapseMasterSrc = path.join(ROOT, 'sinapse');
|
|
273
420
|
const sinapseMasterDest = path.join(SINAPSE_HOME, 'sinapse');
|
|
274
421
|
if (fs.existsSync(sinapseMasterSrc)) {
|
|
275
|
-
|
|
276
|
-
|
|
422
|
+
if (isUpsert) {
|
|
423
|
+
const delta = syncDirSync(sinapseMasterSrc, sinapseMasterDest);
|
|
424
|
+
totalDelta.added += delta.added;
|
|
425
|
+
totalDelta.updated += delta.updated;
|
|
426
|
+
totalDelta.unchanged += delta.unchanged;
|
|
427
|
+
totalDelta.removed += delta.removed;
|
|
428
|
+
} else {
|
|
429
|
+
rmDirSync(sinapseMasterDest);
|
|
430
|
+
copyDirSync(sinapseMasterSrc, sinapseMasterDest);
|
|
431
|
+
}
|
|
277
432
|
const masterAgents = getAgentFiles(sinapseMasterDest).length;
|
|
278
433
|
totalAgents += masterAgents;
|
|
279
|
-
|
|
434
|
+
logger.always(` ${GREEN}OK${NC} sinapse (master, ${masterAgents} agents)`);
|
|
280
435
|
}
|
|
281
436
|
|
|
282
437
|
// Phase 2: Generate orqx commands
|
|
283
|
-
|
|
438
|
+
logger.always(`\n${CYAN}Phase 2:${NC} Generating agent commands`);
|
|
284
439
|
fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
|
|
285
440
|
|
|
286
441
|
// Clear old commands
|
|
@@ -324,7 +479,7 @@ async function cmdInstallGlobal() {
|
|
|
324
479
|
}
|
|
325
480
|
}
|
|
326
481
|
}
|
|
327
|
-
|
|
482
|
+
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} total command files`);
|
|
328
483
|
|
|
329
484
|
// Phase 2b: Install global agents based on LLM choice
|
|
330
485
|
if (llmChoice === 'claude-code' || llmChoice === 'both') {
|
|
@@ -333,7 +488,7 @@ async function cmdInstallGlobal() {
|
|
|
333
488
|
for (const f of fs.readdirSync(CLAUDE_COMMANDS_DIR).filter(f => f.endsWith('.md'))) {
|
|
334
489
|
fs.copyFileSync(path.join(CLAUDE_COMMANDS_DIR, f), path.join(globalAgentsDir, f));
|
|
335
490
|
}
|
|
336
|
-
|
|
491
|
+
logger.always(` ${GREEN}OK${NC} Claude Code global agents (${writtenAgents.size})`);
|
|
337
492
|
}
|
|
338
493
|
|
|
339
494
|
if (llmChoice === 'codex' || llmChoice === 'both') {
|
|
@@ -342,57 +497,76 @@ async function cmdInstallGlobal() {
|
|
|
342
497
|
for (const f of fs.readdirSync(CLAUDE_COMMANDS_DIR).filter(f => f.endsWith('.md'))) {
|
|
343
498
|
fs.copyFileSync(path.join(CLAUDE_COMMANDS_DIR, f), path.join(codexAgentsDir, f));
|
|
344
499
|
}
|
|
345
|
-
|
|
500
|
+
logger.always(` ${GREEN}OK${NC} Codex global agents (${writtenAgents.size})`);
|
|
346
501
|
}
|
|
347
502
|
|
|
348
503
|
// Phase 3: Generate squad-awareness.md
|
|
349
|
-
|
|
504
|
+
logger.always(`\n${CYAN}Phase 3:${NC} Generating squad-awareness rules`);
|
|
350
505
|
generateSquadAwareness(SINAPSE_HOME, squads);
|
|
351
|
-
|
|
506
|
+
logger.always(` ${GREEN}OK${NC} squad-awareness.md`);
|
|
352
507
|
|
|
353
508
|
// Phase 4: Create launcher
|
|
354
|
-
|
|
509
|
+
logger.always(`\n${CYAN}Phase 4:${NC} Creating launcher`);
|
|
355
510
|
createLauncher();
|
|
356
511
|
|
|
357
512
|
// Phase 5: PATH management
|
|
358
|
-
|
|
513
|
+
logger.always(`\n${CYAN}Phase 5:${NC} Configuring PATH`);
|
|
359
514
|
ensurePath();
|
|
360
515
|
|
|
361
|
-
// Phase 6: Write metadata
|
|
516
|
+
// Phase 6: Write metadata (Story 10.20 — preserve installedAt on upsert)
|
|
517
|
+
const nowIso = new Date().toISOString();
|
|
362
518
|
const meta = {
|
|
363
519
|
version: VERSION,
|
|
364
|
-
installedAt:
|
|
520
|
+
installedAt: isUpsert && existing.prevMeta && existing.prevMeta.installedAt
|
|
521
|
+
? existing.prevMeta.installedAt
|
|
522
|
+
: nowIso,
|
|
365
523
|
squads: squads.length,
|
|
366
524
|
agents: totalAgents,
|
|
367
525
|
commands: writtenAgents.size,
|
|
368
526
|
llm: llmChoice,
|
|
369
527
|
platform: process.platform,
|
|
370
528
|
};
|
|
529
|
+
if (isUpsert) {
|
|
530
|
+
meta.updatedAt = nowIso;
|
|
531
|
+
}
|
|
371
532
|
fs.writeFileSync(path.join(SINAPSE_HOME, 'metadata.json'), JSON.stringify(meta, null, 2));
|
|
372
533
|
|
|
534
|
+
// Story 10.20 — Upsert summary block
|
|
535
|
+
if (isUpsert) {
|
|
536
|
+
const prevVer = existing.prevMeta && existing.prevMeta.version ? existing.prevMeta.version : 'unknown';
|
|
537
|
+
logger.always('');
|
|
538
|
+
logger.always(`${BOLD}Upsert complete:${NC}`);
|
|
539
|
+
logger.always(` ${CYAN}Mode:${NC} upsert (--force to reinstall)`);
|
|
540
|
+
logger.always(` ${CYAN}Version:${NC} ${prevVer} -> ${VERSION}`);
|
|
541
|
+
logger.always(` ${CYAN}Squads:${NC} ${squadsRefreshed} refreshed${squadsAdded ? ', ' + squadsAdded + ' added' : ''}`);
|
|
542
|
+
logger.always(` ${CYAN}Files:${NC} ${totalDelta.added} added, ${totalDelta.updated} updated, ${totalDelta.unchanged} unchanged${totalDelta.removed ? ', ' + totalDelta.removed + ' removed' : ''}`);
|
|
543
|
+
logger.always(` ${CYAN}First installed:${NC} ${meta.installedAt}`);
|
|
544
|
+
logger.always(` ${CYAN}Last updated:${NC} ${meta.updatedAt}`);
|
|
545
|
+
}
|
|
546
|
+
|
|
373
547
|
// Chrome Brain: Auto-install browser automation
|
|
374
548
|
if (llmChoice === 'claude-code' || llmChoice === 'both') {
|
|
375
549
|
try {
|
|
376
550
|
const { detectChrome, detectPlatform, installScripts, installHooks, installMcp, installKnowledgeBase } = require('./modules/chrome-brain-installer');
|
|
377
551
|
const chromePath = detectChrome();
|
|
378
552
|
if (chromePath) {
|
|
379
|
-
|
|
553
|
+
logger.always(`\n${CYAN}Phase 7:${NC} Chrome Brain (browser automation)`);
|
|
380
554
|
const platform = detectPlatform();
|
|
381
555
|
installScripts(chromePath, platform);
|
|
382
556
|
installHooks();
|
|
383
557
|
installMcp(platform);
|
|
384
558
|
installKnowledgeBase();
|
|
385
|
-
|
|
559
|
+
logger.always(` ${GREEN}OK${NC} Chrome Brain installed — all agents can control Chrome`);
|
|
386
560
|
} else {
|
|
387
|
-
|
|
561
|
+
logger.always(`\n${YELLOW}SKIP${NC} Chrome Brain — Chrome not found (install Chrome and run: npx sinapse-ai chrome-brain install)`);
|
|
388
562
|
}
|
|
389
563
|
} catch (error) {
|
|
390
|
-
|
|
564
|
+
logger.always(`\n${YELLOW}SKIP${NC} Chrome Brain: ${error.message}`);
|
|
391
565
|
}
|
|
392
566
|
}
|
|
393
567
|
|
|
394
568
|
// Phase 8: Install project-local files (.sinapse-ai/, .claude/, .env)
|
|
395
|
-
|
|
569
|
+
logger.always(`\n${CYAN}Phase 8:${NC} Installing project files in current directory`);
|
|
396
570
|
try {
|
|
397
571
|
const wizardPath = path.join(ROOT, 'packages', 'installer', 'src', 'wizard', 'index.js');
|
|
398
572
|
if (fs.existsSync(wizardPath)) {
|
|
@@ -402,17 +576,17 @@ async function cmdInstallGlobal() {
|
|
|
402
576
|
language: language,
|
|
403
577
|
selectedLLM: llmChoice,
|
|
404
578
|
});
|
|
405
|
-
|
|
579
|
+
logger.always(` ${GREEN}OK${NC} Project files installed (.sinapse-ai/, .claude/)`);
|
|
406
580
|
} else {
|
|
407
|
-
|
|
581
|
+
logger.always(` ${YELLOW}SKIP${NC} Project installer not available`);
|
|
408
582
|
}
|
|
409
583
|
} catch (error) {
|
|
410
|
-
|
|
411
|
-
|
|
584
|
+
logger.always(` ${YELLOW}WARN${NC} Project files: ${error.message}`);
|
|
585
|
+
logger.always(` ${DIM}Run 'npx sinapse-ai install' in your project later to complete setup${NC}`);
|
|
412
586
|
}
|
|
413
587
|
|
|
414
588
|
// Verify
|
|
415
|
-
|
|
589
|
+
logger.always(`\n${CYAN}Verification:${NC}`);
|
|
416
590
|
verifyInstall();
|
|
417
591
|
|
|
418
592
|
// Success message
|
|
@@ -421,18 +595,18 @@ async function cmdInstallGlobal() {
|
|
|
421
595
|
else if (llmChoice === 'both') startCmd = `Run ${CYAN}sinapse${NC} or ${CYAN}codex${NC} to start`;
|
|
422
596
|
else startCmd = `Run ${CYAN}sinapse${NC} to start Claude Code with all agents`;
|
|
423
597
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
598
|
+
logger.always('');
|
|
599
|
+
logger.always(`${GREEN}══════════════════════════════════════════════════════════════${NC}`);
|
|
600
|
+
logger.always(`${GREEN} Sinapse installed successfully!${NC}`);
|
|
601
|
+
logger.always(`${GREEN}══════════════════════════════════════════════════════════════${NC}`);
|
|
602
|
+
logger.always('');
|
|
603
|
+
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${totalAgents} agents${NC} | ${BOLD}${writtenAgents.size} orqx commands${NC}`);
|
|
604
|
+
logger.always(` ${startCmd}`);
|
|
605
|
+
logger.always('');
|
|
606
|
+
logger.always(` ${BOLD}Try an agent:${NC}`);
|
|
607
|
+
logger.always(` ${CYAN}/SINAPSE:agents:sinapse-orqx${NC}`);
|
|
608
|
+
logger.always(` ${CYAN}/SINAPSE:agents:brand-orqx${NC}`);
|
|
609
|
+
logger.always('');
|
|
436
610
|
}
|
|
437
611
|
|
|
438
612
|
function generateCommandMd(agentId, agentName, agentIcon, squadName, squadPath, agentFile) {
|
|
@@ -622,13 +796,13 @@ exec claude --add-dir "${sinapsePathForBash}" --agent sinapse-orqx "$@"
|
|
|
622
796
|
const bashPath = path.join(BIN_DIR, 'sinapse');
|
|
623
797
|
fs.writeFileSync(bashPath, bashLauncher);
|
|
624
798
|
try { fs.chmodSync(bashPath, 0o755); } catch {}
|
|
625
|
-
|
|
799
|
+
logger.always(` ${GREEN}OK${NC} ~/bin/sinapse`);
|
|
626
800
|
|
|
627
801
|
// Windows CMD launcher
|
|
628
802
|
if (IS_WIN) {
|
|
629
803
|
const cmdLauncher = `@echo off\r\nclaude --add-dir "%USERPROFILE%\\.sinapse" --agent sinapse-orqx %*\r\n`;
|
|
630
804
|
fs.writeFileSync(path.join(BIN_DIR, 'sinapse.cmd'), cmdLauncher);
|
|
631
|
-
|
|
805
|
+
logger.always(` ${GREEN}OK${NC} ~/bin/sinapse.cmd`);
|
|
632
806
|
}
|
|
633
807
|
}
|
|
634
808
|
|
|
@@ -638,7 +812,7 @@ function ensurePath() {
|
|
|
638
812
|
const alreadyInPath = pathDirs.some(p => path.normalize(p) === binNorm);
|
|
639
813
|
|
|
640
814
|
if (alreadyInPath) {
|
|
641
|
-
|
|
815
|
+
logger.always(` ${YELLOW}SKIP${NC} ~/bin already in PATH`);
|
|
642
816
|
return;
|
|
643
817
|
}
|
|
644
818
|
|
|
@@ -648,7 +822,7 @@ function ensurePath() {
|
|
|
648
822
|
ensurePathUnix();
|
|
649
823
|
}
|
|
650
824
|
|
|
651
|
-
|
|
825
|
+
logger.always(` ${YELLOW}NOTE${NC} Restart your terminal for PATH changes`);
|
|
652
826
|
}
|
|
653
827
|
|
|
654
828
|
function ensurePathUnix() {
|
|
@@ -662,13 +836,13 @@ function ensurePathUnix() {
|
|
|
662
836
|
const content = fs.readFileSync(rcPath, 'utf8');
|
|
663
837
|
if (content.includes(marker) || content.includes(exportLine)) continue;
|
|
664
838
|
fs.appendFileSync(rcPath, `\n${marker}\n${exportLine}\n`);
|
|
665
|
-
|
|
839
|
+
logger.always(` ${GREEN}OK${NC} Updated ~/${rc}`);
|
|
666
840
|
return;
|
|
667
841
|
}
|
|
668
842
|
|
|
669
843
|
// If no RC file found, create .profile
|
|
670
844
|
fs.writeFileSync(path.join(HOME, '.profile'), `${marker}\n${exportLine}\n`, { flag: 'a' });
|
|
671
|
-
|
|
845
|
+
logger.always(` ${GREEN}OK${NC} Created ~/.profile`);
|
|
672
846
|
}
|
|
673
847
|
|
|
674
848
|
function ensurePathWindows() {
|
|
@@ -678,24 +852,24 @@ function ensurePathWindows() {
|
|
|
678
852
|
const currentPath = match ? match[1].trim() : '';
|
|
679
853
|
|
|
680
854
|
if (currentPath.includes('%USERPROFILE%\\bin') || currentPath.includes(BIN_DIR.replace(/\//g, '\\'))) {
|
|
681
|
-
|
|
855
|
+
logger.always(` ${YELLOW}SKIP${NC} ~/bin already in Windows PATH`);
|
|
682
856
|
return;
|
|
683
857
|
}
|
|
684
858
|
|
|
685
859
|
const newPath = currentPath ? `${currentPath};%USERPROFILE%\\bin` : '%USERPROFILE%\\bin';
|
|
686
860
|
if (newPath.length > 900) {
|
|
687
|
-
|
|
861
|
+
logger.always(` ${YELLOW}WARN${NC} PATH too long for setx. Add manually: ${BIN_DIR}`);
|
|
688
862
|
return;
|
|
689
863
|
}
|
|
690
864
|
|
|
691
865
|
execSync(`setx PATH "${newPath}"`, { encoding: 'utf8', stdio: 'pipe' });
|
|
692
|
-
|
|
866
|
+
logger.always(` ${GREEN}OK${NC} Added ~/bin to Windows User PATH`);
|
|
693
867
|
} catch {
|
|
694
868
|
try {
|
|
695
869
|
execSync(`setx PATH "%USERPROFILE%\\bin"`, { encoding: 'utf8', stdio: 'pipe' });
|
|
696
|
-
|
|
870
|
+
logger.always(` ${GREEN}OK${NC} Created Windows User PATH with ~/bin`);
|
|
697
871
|
} catch {
|
|
698
|
-
|
|
872
|
+
logger.always(` ${YELLOW}WARN${NC} Could not modify PATH. Add manually: ${BIN_DIR}`);
|
|
699
873
|
}
|
|
700
874
|
}
|
|
701
875
|
}
|
|
@@ -709,7 +883,7 @@ function verifyInstall() {
|
|
|
709
883
|
];
|
|
710
884
|
|
|
711
885
|
for (const [ok, msg] of checks) {
|
|
712
|
-
|
|
886
|
+
logger.always(` ${ok ? GREEN + '✓' : RED + '✗'}${NC} ${msg}`);
|
|
713
887
|
}
|
|
714
888
|
}
|
|
715
889
|
|
|
@@ -719,44 +893,56 @@ async function cmdUpdateGlobal() {
|
|
|
719
893
|
header();
|
|
720
894
|
|
|
721
895
|
if (!fs.existsSync(path.join(SINAPSE_HOME, 'metadata.json'))) {
|
|
722
|
-
|
|
896
|
+
logger.always(`${YELLOW}Sinapse not installed globally. Run: npx sinapse-ai install${NC}\n`);
|
|
723
897
|
process.exit(1);
|
|
724
898
|
}
|
|
725
899
|
|
|
900
|
+
// Story 10.22 — reuse settings from existing install
|
|
901
|
+
const existing = detectExistingInstall();
|
|
902
|
+
const prevVer = existing.prevMeta && existing.prevMeta.version ? existing.prevMeta.version : 'unknown';
|
|
903
|
+
|
|
726
904
|
// Welcome back screen
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
905
|
+
logger.always(`${BOLD} Que bom que voce voltou!${NC}`);
|
|
906
|
+
logger.always(`${DIM} Atualizando SINAPSE AI: v${prevVer} -> v${VERSION}${NC}`);
|
|
907
|
+
logger.always('');
|
|
730
908
|
|
|
731
|
-
// LLM
|
|
732
|
-
|
|
909
|
+
// Story 10.22 — skip LLM prompt when previous llm known. To re-prompt,
|
|
910
|
+
// run `npx sinapse-ai install --force`.
|
|
911
|
+
const llmChoice = existing.llm || await promptLlmChoice();
|
|
733
912
|
|
|
734
|
-
|
|
735
|
-
|
|
913
|
+
logger.always('');
|
|
914
|
+
logger.always(`${BOLD}Atualizando SINAPSE AI...${NC}\n`);
|
|
736
915
|
|
|
737
916
|
const squadsDir = path.join(ROOT, 'squads');
|
|
738
917
|
const squadsSrcBase = fs.existsSync(squadsDir) ? squadsDir : ROOT;
|
|
739
918
|
const squads = getSquads(squadsSrcBase);
|
|
740
919
|
|
|
741
|
-
// Phase 1:
|
|
742
|
-
|
|
920
|
+
// Phase 1: Sync squads (Story 10.22 — replaces rmDir+copy with syncDirSync)
|
|
921
|
+
logger.always(`${CYAN}Phase 1:${NC} Refreshing squads`);
|
|
922
|
+
const totalDelta = { added: 0, updated: 0, unchanged: 0, removed: 0 };
|
|
743
923
|
for (const squad of squads) {
|
|
744
924
|
const src = path.join(squadsSrcBase, squad.name);
|
|
745
925
|
const dest = path.join(SINAPSE_HOME, squad.name);
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
926
|
+
const delta = syncDirSync(src, dest);
|
|
927
|
+
totalDelta.added += delta.added;
|
|
928
|
+
totalDelta.updated += delta.updated;
|
|
929
|
+
totalDelta.unchanged += delta.unchanged;
|
|
930
|
+
totalDelta.removed += delta.removed;
|
|
931
|
+
logger.always(` ${GREEN}OK${NC} ${squad.name} (${delta.added} added, ${delta.updated} updated, ${delta.unchanged} unchanged${delta.removed ? ', ' + delta.removed + ' removed' : ''})`);
|
|
749
932
|
}
|
|
750
933
|
|
|
751
934
|
const sinapseMasterSrc = path.join(ROOT, 'sinapse');
|
|
752
935
|
if (fs.existsSync(sinapseMasterSrc)) {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
936
|
+
const delta = syncDirSync(sinapseMasterSrc, path.join(SINAPSE_HOME, 'sinapse'));
|
|
937
|
+
totalDelta.added += delta.added;
|
|
938
|
+
totalDelta.updated += delta.updated;
|
|
939
|
+
totalDelta.unchanged += delta.unchanged;
|
|
940
|
+
totalDelta.removed += delta.removed;
|
|
941
|
+
logger.always(` ${GREEN}OK${NC} sinapse (orqx)`);
|
|
756
942
|
}
|
|
757
943
|
|
|
758
944
|
// Phase 2: Regenerate commands (reuse install logic)
|
|
759
|
-
|
|
945
|
+
logger.always(`\n${CYAN}Phase 2:${NC} Regenerating commands`);
|
|
760
946
|
// Clear and regenerate
|
|
761
947
|
rmDirSync(CLAUDE_COMMANDS_DIR);
|
|
762
948
|
fs.mkdirSync(CLAUDE_COMMANDS_DIR, { recursive: true });
|
|
@@ -795,7 +981,7 @@ async function cmdUpdateGlobal() {
|
|
|
795
981
|
writtenAgents.add(file);
|
|
796
982
|
}
|
|
797
983
|
}
|
|
798
|
-
|
|
984
|
+
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} command files (${totalAgents} agents total)`);
|
|
799
985
|
|
|
800
986
|
// Phase 2b: Install global agents based on LLM choice
|
|
801
987
|
if (llmChoice === 'claude-code' || llmChoice === 'both') {
|
|
@@ -805,7 +991,7 @@ async function cmdUpdateGlobal() {
|
|
|
805
991
|
for (const f of fs.readdirSync(CLAUDE_COMMANDS_DIR).filter(f => f.endsWith('.md'))) {
|
|
806
992
|
fs.copyFileSync(path.join(CLAUDE_COMMANDS_DIR, f), path.join(globalAgentsDir, f));
|
|
807
993
|
}
|
|
808
|
-
|
|
994
|
+
logger.always(` ${GREEN}OK${NC} Claude Code global agents (${writtenAgents.size})`);
|
|
809
995
|
}
|
|
810
996
|
|
|
811
997
|
if (llmChoice === 'codex' || llmChoice === 'both') {
|
|
@@ -815,24 +1001,36 @@ async function cmdUpdateGlobal() {
|
|
|
815
1001
|
for (const f of fs.readdirSync(CLAUDE_COMMANDS_DIR).filter(f => f.endsWith('.md'))) {
|
|
816
1002
|
fs.copyFileSync(path.join(CLAUDE_COMMANDS_DIR, f), path.join(codexAgentsDir, f));
|
|
817
1003
|
}
|
|
818
|
-
|
|
1004
|
+
logger.always(` ${GREEN}OK${NC} Codex global agents (${writtenAgents.size})`);
|
|
819
1005
|
}
|
|
820
1006
|
|
|
821
1007
|
// Phase 3: Regenerate awareness
|
|
822
|
-
|
|
1008
|
+
logger.always(`\n${CYAN}Phase 3:${NC} Updating squad-awareness`);
|
|
823
1009
|
generateSquadAwareness(SINAPSE_HOME, squads);
|
|
824
|
-
|
|
1010
|
+
logger.always(` ${GREEN}OK${NC} squad-awareness.md`);
|
|
825
1011
|
|
|
826
|
-
// Update metadata
|
|
1012
|
+
// Update metadata (Story 10.22 — preserve installedAt, bump version + updatedAt)
|
|
827
1013
|
const metaPath = path.join(SINAPSE_HOME, 'metadata.json');
|
|
828
1014
|
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
1015
|
+
// installedAt preserved by NOT touching it
|
|
829
1016
|
meta.updatedAt = new Date().toISOString();
|
|
1017
|
+
meta.version = VERSION;
|
|
830
1018
|
meta.squads = squads.length;
|
|
831
1019
|
meta.commands = writtenAgents.size;
|
|
1020
|
+
meta.llm = llmChoice;
|
|
832
1021
|
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
833
1022
|
|
|
1023
|
+
// Story 10.22 — Update summary block (mirrors Story 10.20 install upsert)
|
|
1024
|
+
logger.always('');
|
|
1025
|
+
logger.always(`${BOLD}Update complete:${NC}`);
|
|
1026
|
+
logger.always(` ${CYAN}Version:${NC} ${prevVer} -> ${VERSION}`);
|
|
1027
|
+
logger.always(` ${CYAN}Squads:${NC} ${squads.length} refreshed`);
|
|
1028
|
+
logger.always(` ${CYAN}Files:${NC} ${totalDelta.added} added, ${totalDelta.updated} updated, ${totalDelta.unchanged} unchanged${totalDelta.removed ? ', ' + totalDelta.removed + ' removed' : ''}`);
|
|
1029
|
+
logger.always(` ${CYAN}First installed:${NC} ${meta.installedAt}`);
|
|
1030
|
+
logger.always(` ${CYAN}Last updated:${NC} ${meta.updatedAt}`);
|
|
1031
|
+
|
|
834
1032
|
// Phase 4: Update project-local files (.sinapse-ai/, .claude/)
|
|
835
|
-
|
|
1033
|
+
logger.always(`\n${CYAN}Phase 4:${NC} Updating project files in current directory`);
|
|
836
1034
|
try {
|
|
837
1035
|
const wizardPath = path.join(ROOT, 'packages', 'installer', 'src', 'wizard', 'index.js');
|
|
838
1036
|
if (fs.existsSync(wizardPath)) {
|
|
@@ -842,13 +1040,13 @@ async function cmdUpdateGlobal() {
|
|
|
842
1040
|
language: meta.language || 'pt',
|
|
843
1041
|
selectedLLM: llmChoice,
|
|
844
1042
|
});
|
|
845
|
-
|
|
1043
|
+
logger.always(` ${GREEN}OK${NC} Project files updated (.sinapse-ai/, .claude/)`);
|
|
846
1044
|
} else {
|
|
847
|
-
|
|
1045
|
+
logger.always(` ${YELLOW}SKIP${NC} Project installer not available`);
|
|
848
1046
|
}
|
|
849
1047
|
} catch (error) {
|
|
850
|
-
|
|
851
|
-
|
|
1048
|
+
logger.always(` ${YELLOW}WARN${NC} Project files: ${error.message}`);
|
|
1049
|
+
logger.always(` ${DIM}Run 'npx sinapse-ai install' in your project later to complete update${NC}`);
|
|
852
1050
|
}
|
|
853
1051
|
|
|
854
1052
|
let startCmd;
|
|
@@ -856,21 +1054,21 @@ async function cmdUpdateGlobal() {
|
|
|
856
1054
|
else if (llmChoice === 'both') startCmd = `Digite ${CYAN}sinapse${NC} ou ${CYAN}codex${NC} para comecar`;
|
|
857
1055
|
else startCmd = `Digite ${CYAN}sinapse${NC} para comecar`;
|
|
858
1056
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1057
|
+
logger.always('');
|
|
1058
|
+
logger.always(`${GREEN}══════════════════════════════════════════════════════════════${NC}`);
|
|
1059
|
+
logger.always(`${GREEN} SINAPSE AI atualizado para v${VERSION}!${NC}`);
|
|
1060
|
+
logger.always(`${GREEN}══════════════════════════════════════════════════════════════${NC}`);
|
|
1061
|
+
logger.always('');
|
|
1062
|
+
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${totalAgents} agents${NC} | ${BOLD}${writtenAgents.size} orqx commands${NC}`);
|
|
1063
|
+
logger.always(` ${startCmd}`);
|
|
1064
|
+
logger.always('');
|
|
867
1065
|
}
|
|
868
1066
|
|
|
869
1067
|
// ── Uninstall ────────────────────────────────────────────────────────────────
|
|
870
1068
|
|
|
871
1069
|
function cmdUninstall() {
|
|
872
1070
|
header();
|
|
873
|
-
|
|
1071
|
+
logger.always(`${BOLD}Uninstalling Sinapse...${NC}\n`);
|
|
874
1072
|
|
|
875
1073
|
const items = [
|
|
876
1074
|
[SINAPSE_HOME, '~/.sinapse/'],
|
|
@@ -887,14 +1085,14 @@ function cmdUninstall() {
|
|
|
887
1085
|
} else {
|
|
888
1086
|
fs.unlinkSync(p);
|
|
889
1087
|
}
|
|
890
|
-
|
|
1088
|
+
logger.always(` ${GREEN}✓${NC} Removed ${label}`);
|
|
891
1089
|
} else {
|
|
892
|
-
|
|
1090
|
+
logger.always(` ${YELLOW}-${NC} ${label} (not found)`);
|
|
893
1091
|
}
|
|
894
1092
|
}
|
|
895
1093
|
|
|
896
|
-
|
|
897
|
-
|
|
1094
|
+
logger.always(`\n${GREEN}Sinapse uninstalled.${NC}`);
|
|
1095
|
+
logger.always(`${YELLOW}Note:${NC} PATH entry in shell RC files was not removed. Clean up manually if desired.\n`);
|
|
898
1096
|
}
|
|
899
1097
|
|
|
900
1098
|
// ── Local Install (existing behavior) ────────────────────────────────────────
|
|
@@ -909,7 +1107,7 @@ function toPosixPath(p) {
|
|
|
909
1107
|
function runBash(script) {
|
|
910
1108
|
const scriptPath = path.join(ROOT, script);
|
|
911
1109
|
if (!fs.existsSync(scriptPath)) {
|
|
912
|
-
|
|
1110
|
+
logger.error(`${RED}Script not found: ${script}${NC}`);
|
|
913
1111
|
process.exit(1);
|
|
914
1112
|
}
|
|
915
1113
|
|
|
@@ -941,13 +1139,13 @@ function runBash(script) {
|
|
|
941
1139
|
|
|
942
1140
|
function cmdInstallLocal() {
|
|
943
1141
|
header();
|
|
944
|
-
|
|
1142
|
+
logger.always(`${CYAN}▸ Installing squads in current project...${NC}\n`);
|
|
945
1143
|
runBash('scripts/install-squads.sh');
|
|
946
1144
|
}
|
|
947
1145
|
|
|
948
1146
|
function cmdUpdateLocal() {
|
|
949
1147
|
header();
|
|
950
|
-
|
|
1148
|
+
logger.always(`${CYAN}▸ Updating squads in current project...${NC}\n`);
|
|
951
1149
|
runBash('scripts/update-squads.sh');
|
|
952
1150
|
}
|
|
953
1151
|
|
|
@@ -959,81 +1157,197 @@ function cmdList() {
|
|
|
959
1157
|
const squads = getSquads(baseDir);
|
|
960
1158
|
let totalAgents = 0, totalTasks = 0;
|
|
961
1159
|
|
|
962
|
-
|
|
1160
|
+
logger.always(`${BOLD}Squads available:${NC}\n`);
|
|
963
1161
|
|
|
964
1162
|
for (const s of squads) {
|
|
965
1163
|
totalAgents += s.agents;
|
|
966
1164
|
totalTasks += s.tasks;
|
|
967
1165
|
const agents = `${s.agents} agents`.padStart(10);
|
|
968
1166
|
const tasks = `${s.tasks} tasks`.padStart(10);
|
|
969
|
-
|
|
1167
|
+
logger.always(` ${CYAN}${s.name.padEnd(25)}${NC} ${GREEN}${agents}${NC} ${YELLOW}${tasks}${NC} ${s.desc}`);
|
|
970
1168
|
}
|
|
971
1169
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1170
|
+
logger.always('');
|
|
1171
|
+
logger.always(` ${BOLD}Total: ${GREEN}${totalAgents} agents${NC} | ${YELLOW}${totalTasks} tasks${NC}`);
|
|
1172
|
+
logger.always(` ${BOLD}Invoke: ${CYAN}/SINAPSE:agents:{agent-id}${NC}`);
|
|
1173
|
+
logger.always('');
|
|
976
1174
|
}
|
|
977
1175
|
|
|
978
1176
|
function cmdStatus() {
|
|
979
1177
|
header();
|
|
980
1178
|
|
|
981
1179
|
// Check global install
|
|
982
|
-
|
|
1180
|
+
logger.always(`${BOLD}Global Install:${NC}\n`);
|
|
983
1181
|
|
|
984
1182
|
if (fs.existsSync(path.join(SINAPSE_HOME, 'metadata.json'))) {
|
|
985
1183
|
const meta = JSON.parse(fs.readFileSync(path.join(SINAPSE_HOME, 'metadata.json'), 'utf8'));
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if (meta.updatedAt)
|
|
1184
|
+
logger.always(` ${GREEN}✓${NC} Installed (v${meta.version})`);
|
|
1185
|
+
logger.always(` ${GREEN}✓${NC} ${meta.squads} squads, ${meta.commands || '?'} agents`);
|
|
1186
|
+
logger.always(` ${GREEN}✓${NC} Installed: ${meta.installedAt}`);
|
|
1187
|
+
if (meta.updatedAt) logger.always(` ${GREEN}✓${NC} Updated: ${meta.updatedAt}`);
|
|
990
1188
|
} else {
|
|
991
|
-
|
|
992
|
-
|
|
1189
|
+
logger.always(` ${RED}✗${NC} Not installed globally`);
|
|
1190
|
+
logger.always(` ${YELLOW}Run:${NC} npx sinapse-ai install`);
|
|
993
1191
|
}
|
|
994
1192
|
|
|
995
1193
|
verifyInstall();
|
|
996
|
-
|
|
1194
|
+
logger.always('');
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// cmdDoctor — Story 10.21
|
|
1198
|
+
// Wires the existing modular doctor (.sinapse-ai/core/doctor) into the
|
|
1199
|
+
// canonical sinapse-ai CLI. Mirrors bin/sinapse.js runDoctor() but uses
|
|
1200
|
+
// process.exitCode (rather than process.exit) to let stdout flush cleanly.
|
|
1201
|
+
async function cmdDoctor(opts = {}) {
|
|
1202
|
+
if (opts.help) {
|
|
1203
|
+
logger.always(`Usage: npx sinapse-ai doctor [options]
|
|
1204
|
+
|
|
1205
|
+
Run health checks against the SINAPSE environment.
|
|
1206
|
+
|
|
1207
|
+
Options:
|
|
1208
|
+
--fix Auto-correct fixable issues
|
|
1209
|
+
--dry-run Show what --fix would do without applying
|
|
1210
|
+
--json Output as JSON (machine-readable)
|
|
1211
|
+
--quiet Minimal output
|
|
1212
|
+
--deep Run deep checks too (slower)
|
|
1213
|
+
--help, -h Show this help
|
|
1214
|
+
|
|
1215
|
+
Exit codes (Story A.3):
|
|
1216
|
+
0 PASS — all checks passed, no warnings
|
|
1217
|
+
1 WARN only — non-blocking issues detected
|
|
1218
|
+
2 FAIL — at least one blocking check failed
|
|
1219
|
+
3 Internal error — doctor runner crashed
|
|
1220
|
+
`);
|
|
1221
|
+
return { ok: true, formatted: '', data: null };
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const doctorModulePath = path.join(__dirname, '..', '.sinapse-ai', 'core', 'doctor');
|
|
1225
|
+
// eslint-disable-next-line global-require
|
|
1226
|
+
const { runDoctorChecks, resolveExitCode } = require(doctorModulePath);
|
|
1227
|
+
const result = await runDoctorChecks({
|
|
1228
|
+
fix: Boolean(opts.fix),
|
|
1229
|
+
json: Boolean(opts.json),
|
|
1230
|
+
dryRun: Boolean(opts.dryRun),
|
|
1231
|
+
quiet: Boolean(opts.quiet),
|
|
1232
|
+
deep: Boolean(opts.deep),
|
|
1233
|
+
projectRoot: process.cwd(),
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
if (result && result.formatted) {
|
|
1237
|
+
logger.always(result.formatted);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Story A.3 — precise exit code mapping:
|
|
1241
|
+
// 0 PASS | 1 WARN only | 2 FAIL | 3 internal runner error
|
|
1242
|
+
// Fall back to resolveExitCode when available (module may be mocked in tests
|
|
1243
|
+
// that pre-date A.3; in that case, keep the legacy 0/1 behavior).
|
|
1244
|
+
if (typeof resolveExitCode === 'function') {
|
|
1245
|
+
const code = resolveExitCode(result);
|
|
1246
|
+
if (code !== 0) {
|
|
1247
|
+
process.exitCode = code;
|
|
1248
|
+
}
|
|
1249
|
+
} else if (result && result.data && result.data.summary && result.data.summary.fail > 0) {
|
|
1250
|
+
process.exitCode = 1;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return result;
|
|
997
1254
|
}
|
|
998
1255
|
|
|
999
1256
|
function cmdHelp() {
|
|
1000
1257
|
header();
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1258
|
+
logger.always(`${BOLD}Commands:${NC}\n`);
|
|
1259
|
+
logger.always(` ${CYAN}npx sinapse-ai install${NC} Install SINAPSE (idempotent — re-runs are upserts)`);
|
|
1260
|
+
logger.always(` ${CYAN}npx sinapse-ai install --force${NC} Wipe and reinstall fresh, even if already installed`);
|
|
1261
|
+
logger.always(` ${CYAN}npx sinapse-ai install --reconfigure${NC} Re-prompt language/LLM without wiping existing install`);
|
|
1262
|
+
logger.always(` ${CYAN}npx sinapse-ai update${NC} Update SINAPSE to the latest version`);
|
|
1263
|
+
logger.always(` ${CYAN}npx sinapse-ai uninstall${NC} Remove SINAPSE from current project`);
|
|
1264
|
+
logger.always('');
|
|
1265
|
+
logger.always(` ${DIM}Works in CI / non-interactive environments (uses sensible defaults).${NC}`);
|
|
1266
|
+
logger.always('');
|
|
1267
|
+
logger.always(`${BOLD}Diagnostics:${NC}\n`);
|
|
1268
|
+
logger.always(` ${CYAN}npx sinapse-ai status${NC} Check installation status`);
|
|
1269
|
+
logger.always(` ${CYAN}npx sinapse-ai doctor${NC} Run health checks (--fix --dry-run --json --deep)`);
|
|
1270
|
+
logger.always(` ${CYAN}npx sinapse-ai list${NC} List all squads and agents`);
|
|
1271
|
+
logger.always(` ${CYAN}npx sinapse-ai help${NC} Show this help`);
|
|
1272
|
+
logger.always('');
|
|
1273
|
+
logger.always(`${BOLD}After install:${NC}\n`);
|
|
1274
|
+
logger.always(` ${CYAN}sinapse${NC} Start Claude Code with all agents`);
|
|
1275
|
+
logger.always(` ${CYAN}sinapse --continue${NC} Continue last session`);
|
|
1276
|
+
logger.always('');
|
|
1277
|
+
logger.always(`${BOLD}Agents:${NC}\n`);
|
|
1278
|
+
logger.always(` All agents use: ${CYAN}/SINAPSE:agents:{agent-id}${NC}`);
|
|
1279
|
+
logger.always(` Example: ${CYAN}/SINAPSE:agents:brand-orqx${NC}`);
|
|
1280
|
+
logger.always('');
|
|
1018
1281
|
}
|
|
1019
1282
|
|
|
1020
1283
|
// ── Router ───────────────────────────────────────────────────────────────────
|
|
1021
1284
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1285
|
+
// Story 10.20 — Export helpers for unit tests. Router only runs when this
|
|
1286
|
+
// file is invoked directly by Node; importing it from a test gets the
|
|
1287
|
+
// helpers without triggering the switch statement.
|
|
1288
|
+
module.exports = {
|
|
1289
|
+
syncDirSync,
|
|
1290
|
+
detectExistingInstall,
|
|
1291
|
+
cmdDoctor,
|
|
1292
|
+
promptLlmChoice,
|
|
1293
|
+
SINAPSE_HOME,
|
|
1294
|
+
HOME,
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
if (require.main === module) {
|
|
1298
|
+
runRouter();
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
function runRouter() {
|
|
1302
|
+
const args = process.argv.slice(2);
|
|
1303
|
+
const command = args[0] || 'help';
|
|
1304
|
+
const isLocal = args.includes('--local');
|
|
1305
|
+
const isForce = args.includes('--force');
|
|
1306
|
+
const isReconfigure = args.includes('--reconfigure');
|
|
1307
|
+
|
|
1308
|
+
switch (command) {
|
|
1309
|
+
case 'install': isLocal ? cmdInstallLocal() : cmdInstallGlobal({ force: isForce, reconfigure: isReconfigure }).catch(e => { logger.error(e.message); process.exit(1); }); break;
|
|
1310
|
+
case 'update': isLocal ? cmdUpdateLocal() : cmdUpdateGlobal().catch(e => { logger.error(e.message); process.exit(1); }); break;
|
|
1311
|
+
case 'uninstall': cmdUninstall(); break;
|
|
1312
|
+
case 'list': cmdList(); break;
|
|
1313
|
+
case 'status': cmdStatus(); break;
|
|
1314
|
+
case 'doctor': {
|
|
1315
|
+
// Story 10.21 — wires the modular doctor into the canonical CLI
|
|
1316
|
+
const doctorArgs = args.slice(1);
|
|
1317
|
+
const doctorOpts = {
|
|
1318
|
+
help: doctorArgs.includes('--help') || doctorArgs.includes('-h'),
|
|
1319
|
+
fix: doctorArgs.includes('--fix'),
|
|
1320
|
+
json: doctorArgs.includes('--json'),
|
|
1321
|
+
dryRun: doctorArgs.includes('--dry-run'),
|
|
1322
|
+
quiet: doctorArgs.includes('--quiet'),
|
|
1323
|
+
deep: doctorArgs.includes('--deep'),
|
|
1324
|
+
};
|
|
1325
|
+
cmdDoctor(doctorOpts).catch(e => { logger.error(`${RED}doctor error:${NC} ${e.message}`); process.exit(1); });
|
|
1326
|
+
break;
|
|
1327
|
+
}
|
|
1328
|
+
case 'chrome-brain': {
|
|
1329
|
+
// Story 10.13 — chrome-brain is the canonical sub-capability for browser
|
|
1330
|
+
// automation. Delegating to the shared chrome-brain-installer module keeps
|
|
1331
|
+
// `npx sinapse-ai chrome-brain <install|uninstall|status>` working without
|
|
1332
|
+
// requiring users to fall back to legacy binaries.
|
|
1333
|
+
const chromeBrainArgs = args.slice(1);
|
|
1334
|
+
(async () => {
|
|
1335
|
+
try {
|
|
1336
|
+
const { runChromeBrain } = require('./modules/chrome-brain-installer');
|
|
1337
|
+
await runChromeBrain(chromeBrainArgs);
|
|
1338
|
+
} catch (e) {
|
|
1339
|
+
logger.error(`${RED}chrome-brain error:${NC} ${e.message}`);
|
|
1340
|
+
process.exit(1);
|
|
1341
|
+
}
|
|
1342
|
+
})();
|
|
1343
|
+
break;
|
|
1344
|
+
}
|
|
1345
|
+
case 'help':
|
|
1346
|
+
case '--help':
|
|
1347
|
+
case '-h': cmdHelp(); break;
|
|
1348
|
+
default:
|
|
1349
|
+
logger.error(`${RED}Unknown command:${NC} ${command}`);
|
|
1350
|
+
logger.error(`Run ${CYAN}npx sinapse-ai help${NC}`);
|
|
1351
|
+
process.exit(1);
|
|
1352
|
+
}
|
|
1039
1353
|
}
|