sinapse-ai 1.25.0 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +170 -0
- package/.codex/catalog.json +1 -1
- package/.codex/scripts/sinapse-codex.js +0 -5
- package/.codex/scripts/sync-codex-native.js +4 -37
- package/.codex/scripts/validate-codex-native.js +13 -23
- package/.codex/tasks/onboard-sinapse-codex.md +1 -1
- package/.sinapse-ai/data/entity-registry.yaml +44 -18
- package/.sinapse-ai/install-manifest.yaml +3 -3
- package/CHANGELOG.md +8 -16
- package/bin/cli.js +8 -1
- package/bin/commands/help.js +3 -2
- package/bin/commands/install.js +25 -14
- package/bin/commands/status.js +4 -3
- package/bin/commands/uninstall.js +216 -51
- package/bin/commands/update.js +8 -4
- package/bin/lib/global-provider-adapters.js +73 -10
- package/bin/lib/provider-parity.js +22 -0
- package/bin/postinstall.js +11 -11
- package/docs/framework/codex-parity/codex-integration-process.md +6 -7
- package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
- package/docs/framework/codex-parity/codex-parity-program.md +4 -4
- package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
- package/docs/guides/codex-config.md +2 -2
- package/docs/guides/development-setup.md +1 -2
- package/docs/guides/ide-integration.md +6 -6
- package/docs/guides/quality-gates.md +2 -2
- package/docs/ide-integration.md +2 -2
- package/docs/installation/faq.md +1 -1
- package/docs/installation/troubleshooting.md +2 -2
- package/docs/installation/v4-quick-start.md +1 -1
- package/docs/pt/contributing.md +1 -1
- package/docs/pt/guides/development-setup.md +1 -2
- package/docs/pt/guides/quality-gates.md +2 -2
- package/docs/pt/ide-integration.md +6 -6
- package/docs/pt/installation/faq.md +1 -1
- package/docs/pt/installation/troubleshooting.md +2 -3
- package/docs/pt/installation/v4-quick-start.md +1 -2
- package/docs/pt/platforms/claude-code.md +4 -4
- package/docs/sinapse-agent-flows/analyst-system.md +1 -1
- package/docs/sinapse-agent-flows/architect-system.md +1 -1
- package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
- package/docs/sinapse-agent-flows/dev-system.md +1 -1
- package/docs/sinapse-agent-flows/pm-system.md +1 -1
- package/docs/sinapse-agent-flows/qa-system.md +1 -1
- package/docs/sinapse-agent-flows/sm-system.md +1 -1
- package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
- package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
- package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
- package/package.json +21 -16
- package/packages/installer/src/config/ide-configs.js +1 -1
- package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
- package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
- package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
- package/packages/installer/src/updater/index.js +24 -7
- package/packages/installer/src/wizard/ide-config-generator.js +4 -0
- package/packages/installer/src/wizard/index.js +6 -2
- package/scripts/validate-codex-command-registry.js +101 -0
- package/scripts/validate-provider-adapters.js +117 -1
- package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
- package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
- package/squads/squad-design/agents/ux-designer.md +1 -1
- package/.codex/skills/sinapse/SKILL.md +0 -10
- package/.codex/skills/sinapse-agent/SKILL.md +0 -22
- package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
- package/.codex/skills/sinapse-animations/SKILL.md +0 -28
- package/.codex/skills/sinapse-architect/SKILL.md +0 -31
- package/.codex/skills/sinapse-brand/SKILL.md +0 -28
- package/.codex/skills/sinapse-claude/SKILL.md +0 -28
- package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
- package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
- package/.codex/skills/sinapse-content/SKILL.md +0 -28
- package/.codex/skills/sinapse-copy/SKILL.md +0 -28
- package/.codex/skills/sinapse-council/SKILL.md +0 -28
- package/.codex/skills/sinapse-courses/SKILL.md +0 -28
- package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
- package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
- package/.codex/skills/sinapse-design/SKILL.md +0 -28
- package/.codex/skills/sinapse-dev/SKILL.md +0 -31
- package/.codex/skills/sinapse-devops/SKILL.md +0 -31
- package/.codex/skills/sinapse-finance/SKILL.md +0 -28
- package/.codex/skills/sinapse-growth/SKILL.md +0 -28
- package/.codex/skills/sinapse-loop/SKILL.md +0 -35
- package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
- package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
- package/.codex/skills/sinapse-pm/SKILL.md +0 -31
- package/.codex/skills/sinapse-po/SKILL.md +0 -31
- package/.codex/skills/sinapse-product/SKILL.md +0 -28
- package/.codex/skills/sinapse-qa/SKILL.md +0 -31
- package/.codex/skills/sinapse-research/SKILL.md +0 -28
- package/.codex/skills/sinapse-sm/SKILL.md +0 -27
- package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
- package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
- package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
- package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
- package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
- package/.codex/skills/snps/SKILL.md +0 -10
- package/.codex/skills/snps-orqx/SKILL.md +0 -10
- /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
- /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
- /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
package/bin/commands/install.js
CHANGED
|
@@ -49,6 +49,7 @@ const {
|
|
|
49
49
|
regenerateAgentCommands,
|
|
50
50
|
} = require('../lib/command-generator');
|
|
51
51
|
const { deliverGlobalProviderAdapters, getGlobalCommandStagingDir } = require('../lib/global-provider-adapters');
|
|
52
|
+
const { assertProviderAdapterParity } = require('../lib/provider-parity');
|
|
52
53
|
// Follow-up #13 — wire the transactional backup/rollback engine into the
|
|
53
54
|
// installer so an in-place UPGRADE (upsert) that fails mid-flight is restored
|
|
54
55
|
// to its previous state instead of leaving ~/.sinapse half-updated.
|
|
@@ -64,6 +65,11 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
64
65
|
// Story 10.20 — Upsert detection
|
|
65
66
|
const force = Boolean(opts.force);
|
|
66
67
|
const reconfigure = Boolean(opts.reconfigure);
|
|
68
|
+
const globalOnly = Boolean(opts.globalOnly);
|
|
69
|
+
const requestedLlm = opts.llm || null;
|
|
70
|
+
if (requestedLlm && !['claude-code', 'codex', 'both'].includes(requestedLlm)) {
|
|
71
|
+
throw new Error(`Invalid --llm value: ${requestedLlm}. Use claude-code, codex, or both.`);
|
|
72
|
+
}
|
|
67
73
|
const existing = force ? { upsert: false } : detectExistingInstall();
|
|
68
74
|
const isUpsert = existing.upsert;
|
|
69
75
|
|
|
@@ -126,7 +132,9 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
126
132
|
// Story 10.35: --reconfigure forces prompt even in upsert mode
|
|
127
133
|
let llmChoice;
|
|
128
134
|
let llmWasReused = false;
|
|
129
|
-
if (
|
|
135
|
+
if (requestedLlm) {
|
|
136
|
+
llmChoice = requestedLlm;
|
|
137
|
+
} else if (isUpsert && !reconfigure && existing.llm) {
|
|
130
138
|
llmChoice = existing.llm;
|
|
131
139
|
llmWasReused = true;
|
|
132
140
|
const ideLabel = Array.isArray(llmChoice) ? llmChoice.join(', ') : String(llmChoice);
|
|
@@ -222,7 +230,7 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
222
230
|
// project-wizard WARN must NOT revert an otherwise-good global install.
|
|
223
231
|
const tx = createInstallTransaction();
|
|
224
232
|
const txPaths = buildTransactionPaths(llmChoice);
|
|
225
|
-
const {
|
|
233
|
+
const { activeAgentCount, totalDelta, squadsRefreshed, squadsAdded, meta } =
|
|
226
234
|
await runFatalPhasesTransactional({
|
|
227
235
|
tx,
|
|
228
236
|
isUpsert,
|
|
@@ -269,7 +277,10 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
269
277
|
|
|
270
278
|
// Phase 8: Install project-local files (.sinapse-ai/, .claude/, .env)
|
|
271
279
|
logger.always(`\n${CYAN}Phase 8:${NC} Installing project files in current directory`);
|
|
272
|
-
|
|
280
|
+
const skipProjectInstall = globalOnly || path.resolve(process.cwd()) === path.resolve(ROOT);
|
|
281
|
+
if (skipProjectInstall) {
|
|
282
|
+
logger.always(` ${YELLOW}SKIP${NC} Project files (${globalOnly ? '--global-only' : 'source checkout detected'})`);
|
|
283
|
+
} else try {
|
|
273
284
|
const wizardPath = path.join(ROOT, 'packages', 'installer', 'src', 'wizard', 'index.js');
|
|
274
285
|
if (fs.existsSync(wizardPath)) {
|
|
275
286
|
const { runWizard: executeWizard } = require(wizardPath);
|
|
@@ -348,7 +359,7 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
348
359
|
logger.always(`${YELLOW}══════════════════════════════════════════════════════════════${NC}`);
|
|
349
360
|
}
|
|
350
361
|
logger.always('');
|
|
351
|
-
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${
|
|
362
|
+
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${activeAgentCount} agents${NC} | ${BOLD}${activeAgentCount} native adapters${NC}`);
|
|
352
363
|
logger.always(` ${startCmd}`);
|
|
353
364
|
logger.always('');
|
|
354
365
|
logger.always(` ${BOLD}Try an agent:${NC}`);
|
|
@@ -402,6 +413,9 @@ function buildTransactionPaths(llmChoice) {
|
|
|
402
413
|
CLAUDE_COMMANDS_DIR, // Phase 2 — generated /SINAPSE:agents:* command files
|
|
403
414
|
];
|
|
404
415
|
if (llmChoice === 'claude-code' || llmChoice === 'both') dirs.push(path.join(HOME, '.claude', 'agents'));
|
|
416
|
+
// A Claude-only provider switch removes stale Codex aliases during the fatal
|
|
417
|
+
// phases, so that cleanup must be covered by the same upgrade rollback.
|
|
418
|
+
if (llmChoice === 'claude-code') dirs.push(path.join(HOME, '.agents', 'skills'));
|
|
405
419
|
if (llmChoice === 'codex' || llmChoice === 'both') {
|
|
406
420
|
dirs.push(path.join(HOME, '.codex', 'agents'));
|
|
407
421
|
dirs.push(path.join(HOME, '.agents', 'skills'));
|
|
@@ -478,7 +492,7 @@ async function runFatalPhasesTransactional({ tx, isUpsert, paths, runPhases, onR
|
|
|
478
492
|
* @param {string} ctx.llmChoice
|
|
479
493
|
* @param {Object} ctx.existing - detectExistingInstall() result
|
|
480
494
|
* @param {Object} ctx.logger
|
|
481
|
-
* @returns {{ writtenAgents: Set,
|
|
495
|
+
* @returns {{ writtenAgents: Set, activeAgentCount: number, totalDelta: Object, squadsRefreshed: number, squadsAdded: number, meta: Object }}
|
|
482
496
|
*/
|
|
483
497
|
function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing, logger }) {
|
|
484
498
|
// Phase 1: Copy squads to ~/.sinapse/
|
|
@@ -486,7 +500,6 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
486
500
|
fs.mkdirSync(SINAPSE_HOME, { recursive: true });
|
|
487
501
|
|
|
488
502
|
const squadsSrcBase = fs.existsSync(squadsDir) ? squadsDir : ROOT;
|
|
489
|
-
let totalAgents = 0;
|
|
490
503
|
const totalDelta = { added: 0, updated: 0, unchanged: 0, removed: 0 };
|
|
491
504
|
let squadsRefreshed = 0;
|
|
492
505
|
let squadsAdded = 0;
|
|
@@ -507,7 +520,6 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
507
520
|
copyDirSync(src, dest);
|
|
508
521
|
logger.always(` ${GREEN}OK${NC} ${squad.name} (${squad.agents} agents)`);
|
|
509
522
|
}
|
|
510
|
-
totalAgents += squad.agents;
|
|
511
523
|
}
|
|
512
524
|
|
|
513
525
|
// Copy sinapse/ orqx squad
|
|
@@ -525,7 +537,6 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
525
537
|
copyDirSync(sinapseMasterSrc, sinapseMasterDest);
|
|
526
538
|
}
|
|
527
539
|
const masterAgents = getAgentFiles(sinapseMasterDest).length;
|
|
528
|
-
totalAgents += masterAgents;
|
|
529
540
|
logger.always(` ${GREEN}OK${NC} sinapse (master, ${masterAgents} agents)`);
|
|
530
541
|
}
|
|
531
542
|
|
|
@@ -543,14 +554,13 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
543
554
|
copyDirSync(coreDevelopmentSrc, coreDevelopmentDest);
|
|
544
555
|
}
|
|
545
556
|
const coreAgents = getAgentFiles(coreDevelopmentDest).length;
|
|
546
|
-
totalAgents += coreAgents;
|
|
547
557
|
logger.always(` ${GREEN}OK${NC} core development (${coreAgents} agents)`);
|
|
548
558
|
}
|
|
549
559
|
|
|
550
560
|
// Phase 2: Generate agent commands (shared with `update` via command-generator).
|
|
551
561
|
logger.always(`\n${CYAN}Phase 2:${NC} Generating agent commands`);
|
|
552
562
|
const commandStagingDir = getGlobalCommandStagingDir({ llmChoice, sinapseHome: SINAPSE_HOME, claudeCommandsDir: CLAUDE_COMMANDS_DIR });
|
|
553
|
-
const { writtenAgents } = regenerateAgentCommands({
|
|
563
|
+
const { writtenAgents, totalAgents } = regenerateAgentCommands({
|
|
554
564
|
sinapseHome: SINAPSE_HOME,
|
|
555
565
|
commandsDir: commandStagingDir,
|
|
556
566
|
squads,
|
|
@@ -561,6 +571,7 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
561
571
|
|
|
562
572
|
// Phase 2b: Install global agents based on LLM choice
|
|
563
573
|
const globalAdapters = deliverGlobalProviderAdapters({ llmChoice, home: HOME, commandsDir: commandStagingDir });
|
|
574
|
+
const activeAgentCount = assertProviderAdapterParity(llmChoice, globalAdapters, totalAgents);
|
|
564
575
|
const installedAgentFilenames = new Set([...globalAdapters.claude, ...globalAdapters.codex]);
|
|
565
576
|
const installedIdes = [];
|
|
566
577
|
if (globalAdapters.claude.length) installedIdes.push('claude-code');
|
|
@@ -571,7 +582,7 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
571
582
|
// Audit 1 P0 (UN-1) — record manifest so uninstall can remove every file
|
|
572
583
|
// we wrote (not just `*-orqx.md`). Idempotent: re-install overwrites.
|
|
573
584
|
reconcileInstalledAgents(HOME, installedAgentFilenames);
|
|
574
|
-
if (llmChoice === 'claude-code') removeManagedGlobalSkills(HOME);
|
|
585
|
+
if (llmChoice === 'claude-code') removeManagedGlobalSkills(HOME, { providers: ['codex'] });
|
|
575
586
|
recordInstalledAgents(installedAgentFilenames, installedIdes);
|
|
576
587
|
|
|
577
588
|
// Phase 3: Generate squad-awareness.md
|
|
@@ -595,8 +606,8 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
595
606
|
? existing.prevMeta.installedAt
|
|
596
607
|
: nowIso,
|
|
597
608
|
squads: squads.length,
|
|
598
|
-
agents:
|
|
599
|
-
commands:
|
|
609
|
+
agents: activeAgentCount,
|
|
610
|
+
commands: activeAgentCount,
|
|
600
611
|
llm: llmChoice,
|
|
601
612
|
platform: process.platform,
|
|
602
613
|
};
|
|
@@ -605,7 +616,7 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
605
616
|
}
|
|
606
617
|
atomicWriteFileSync(path.join(SINAPSE_HOME, 'metadata.json'), JSON.stringify(meta, null, 2));
|
|
607
618
|
|
|
608
|
-
return { writtenAgents,
|
|
619
|
+
return { writtenAgents, activeAgentCount, totalDelta, squadsRefreshed, squadsAdded, meta };
|
|
609
620
|
}
|
|
610
621
|
|
|
611
622
|
function generateSquadAwareness(sinapseDir, squads) {
|
package/bin/commands/status.js
CHANGED
|
@@ -5,9 +5,9 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { getLogger } = require('../../.sinapse-ai/core/logger');
|
|
7
7
|
const {
|
|
8
|
+
HOME,
|
|
8
9
|
ROOT,
|
|
9
10
|
SINAPSE_HOME,
|
|
10
|
-
CLAUDE_COMMANDS_DIR,
|
|
11
11
|
BIN_DIR,
|
|
12
12
|
CYAN,
|
|
13
13
|
GREEN,
|
|
@@ -18,12 +18,13 @@ const {
|
|
|
18
18
|
} = require('../lib/constants');
|
|
19
19
|
const { header } = require('../lib/header');
|
|
20
20
|
const { getSquads } = require('../lib/squads');
|
|
21
|
+
const { hasManagedInstalledAgents } = require('./uninstall');
|
|
21
22
|
|
|
22
23
|
function verifyInstall() {
|
|
23
24
|
const logger = getLogger();
|
|
24
25
|
const checks = [
|
|
25
26
|
[fs.existsSync(SINAPSE_HOME), `${getSquads(SINAPSE_HOME).length} squads in ~/.sinapse/`],
|
|
26
|
-
[
|
|
27
|
+
[hasManagedInstalledAgents(HOME), 'Native provider agents installed'],
|
|
27
28
|
[fs.existsSync(path.join(BIN_DIR, 'sinapse')), 'Launcher at ~/bin/sinapse'],
|
|
28
29
|
[fs.existsSync(path.join(SINAPSE_HOME, '.claude', 'rules', 'squad-awareness.md')), 'squad-awareness.md'],
|
|
29
30
|
];
|
|
@@ -52,7 +53,7 @@ function cmdList() {
|
|
|
52
53
|
|
|
53
54
|
logger.always('');
|
|
54
55
|
logger.always(` ${BOLD}Total: ${GREEN}${totalAgents} agents${NC} | ${YELLOW}${totalTasks} tasks${NC}`);
|
|
55
|
-
logger.always(` ${BOLD}Invoke: ${CYAN}
|
|
56
|
+
logger.always(` ${BOLD}Invoke: ${CYAN}$sinapse-agent <agent-id>${NC}`);
|
|
56
57
|
logger.always('');
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const legacyClaudeCommandHashes = require('../../packages/installer/src/migrations/legacy-claude-agent-command-hashes.json');
|
|
6
8
|
const { getLogger } = require('../../.sinapse-ai/core/logger');
|
|
7
9
|
const {
|
|
8
10
|
HOME,
|
|
@@ -58,22 +60,61 @@ async function removeGitHooksConfig(projectDir = process.cwd()) {
|
|
|
58
60
|
// and removes only those files (preserving anything the user added by hand).
|
|
59
61
|
const INSTALLED_AGENTS_MANIFEST = path.join(SINAPSE_HOME, 'installed-agents.json');
|
|
60
62
|
|
|
61
|
-
function
|
|
63
|
+
function installedAgentsManifestPath(home = HOME) {
|
|
64
|
+
return home === HOME
|
|
65
|
+
? INSTALLED_AGENTS_MANIFEST
|
|
66
|
+
: path.join(home, '.sinapse', 'installed-agents.json');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function digestFile(filePath) {
|
|
62
70
|
try {
|
|
63
|
-
fs.
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function providerAgentPath(home, provider, filename) {
|
|
78
|
+
const providerDir = provider === 'codex'
|
|
79
|
+
? path.join(home, '.codex', 'agents')
|
|
80
|
+
: path.join(home, '.claude', 'agents');
|
|
81
|
+
return path.join(providerDir, filename);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function inferProviderFromAgentDir(dir) {
|
|
85
|
+
const normalized = path.resolve(dir).replace(/\\/g, '/').toLowerCase();
|
|
86
|
+
if (normalized.includes('/.codex/agents')) return 'codex';
|
|
87
|
+
if (normalized.includes('/.claude/agents')) return 'claude-code';
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function recordInstalledAgents(filenames, ides, home = HOME) {
|
|
92
|
+
try {
|
|
93
|
+
const manifestPath = installedAgentsManifestPath(home);
|
|
94
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
95
|
+
const artifacts = [...filenames].map((filename) => {
|
|
96
|
+
const provider = filename.endsWith('.toml') ? 'codex' : 'claude-code';
|
|
97
|
+
return {
|
|
98
|
+
provider,
|
|
99
|
+
filename,
|
|
100
|
+
sha256: digestFile(providerAgentPath(home, provider, filename)),
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
fs.writeFileSync(manifestPath, JSON.stringify({
|
|
104
|
+
version: 2,
|
|
66
105
|
timestamp: new Date().toISOString(),
|
|
67
106
|
ides,
|
|
68
107
|
filenames: [...filenames].sort(),
|
|
108
|
+
artifacts: artifacts.sort((a, b) => `${a.provider}/${a.filename}`.localeCompare(`${b.provider}/${b.filename}`)),
|
|
69
109
|
}, null, 2));
|
|
70
110
|
} catch { /* non-critical */ }
|
|
71
111
|
}
|
|
72
112
|
|
|
73
|
-
function readInstalledAgentsManifest() {
|
|
113
|
+
function readInstalledAgentsManifest(home = HOME) {
|
|
74
114
|
try {
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
const manifestPath = installedAgentsManifestPath(home);
|
|
116
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
117
|
+
const raw = fs.readFileSync(manifestPath, 'utf8');
|
|
77
118
|
const parsed = JSON.parse(raw);
|
|
78
119
|
if (!parsed || !Array.isArray(parsed.filenames)) return null;
|
|
79
120
|
return parsed;
|
|
@@ -85,35 +126,36 @@ function readInstalledAgentsManifest() {
|
|
|
85
126
|
function removeInstalledAgentsFrom(dir, manifestOverride = null) {
|
|
86
127
|
if (!fs.existsSync(dir)) return { removed: 0, existed: false };
|
|
87
128
|
let removed = 0;
|
|
129
|
+
const manifest = manifestOverride || readInstalledAgentsManifest();
|
|
130
|
+
const provider = inferProviderFromAgentDir(dir);
|
|
88
131
|
let candidates;
|
|
89
132
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
133
|
+
if (manifest?.version >= 2 && Array.isArray(manifest.artifacts)) {
|
|
134
|
+
candidates = manifest.artifacts.filter((artifact) => {
|
|
135
|
+
if (!artifact || typeof artifact.filename !== 'string') return false;
|
|
136
|
+
return provider ? artifact.provider === provider : true;
|
|
137
|
+
});
|
|
94
138
|
} else {
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
try { return /SINAPSE/i.test(fs.readFileSync(path.join(dir, f), 'utf8').slice(0, 4000)); } catch { return false; }
|
|
102
|
-
}
|
|
103
|
-
if (!f.endsWith('.md')) return false;
|
|
104
|
-
if (/-orqx\.md$/.test(f)) return true;
|
|
105
|
-
try {
|
|
106
|
-
const head = fs.readFileSync(path.join(dir, f), 'utf8').slice(0, 600);
|
|
107
|
-
return /^\s*name:\s*sinapse-/im.test(head);
|
|
108
|
-
} catch { return false; }
|
|
109
|
-
});
|
|
110
|
-
} catch { return { removed: 0, existed: true }; }
|
|
139
|
+
// Legacy manifests did not record provider or digests. Ownership must be
|
|
140
|
+
// proven by the marker in the actual provider directory before deletion.
|
|
141
|
+
const filenames = Array.isArray(manifest?.filenames)
|
|
142
|
+
? manifest.filenames
|
|
143
|
+
: fs.readdirSync(dir);
|
|
144
|
+
candidates = filenames.map((filename) => ({ filename, sha256: null, legacy: true }));
|
|
111
145
|
}
|
|
112
146
|
|
|
113
|
-
for (const
|
|
114
|
-
const filepath = path.join(dir,
|
|
147
|
+
for (const { filename, sha256, legacy } of candidates) {
|
|
148
|
+
const filepath = path.join(dir, filename);
|
|
115
149
|
if (!fs.existsSync(filepath)) continue;
|
|
116
150
|
try {
|
|
151
|
+
if (sha256) {
|
|
152
|
+
if (digestFile(filepath) !== sha256) continue;
|
|
153
|
+
} else {
|
|
154
|
+
// A v2 entry without a digest does not prove the installer wrote it.
|
|
155
|
+
if (!legacy) continue;
|
|
156
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
157
|
+
if (!content.includes('SINAPSE-MANAGED:global-agent')) continue;
|
|
158
|
+
}
|
|
117
159
|
fs.unlinkSync(filepath);
|
|
118
160
|
removed++;
|
|
119
161
|
} catch { /* best-effort */ }
|
|
@@ -128,44 +170,166 @@ function removeOrqxAgentsFrom(dir) {
|
|
|
128
170
|
return removeInstalledAgentsFrom(dir);
|
|
129
171
|
}
|
|
130
172
|
|
|
131
|
-
const MANAGED_GLOBAL_SKILL_IDS = ['snps', 'sinapse', 'snps-orqx', 'sinapse-agent'];
|
|
173
|
+
const MANAGED_GLOBAL_SKILL_IDS = ['snps', 'sinapse', 'snps-orqx', 'sinapse-orqx', 'sinapse-agent'];
|
|
132
174
|
const MANAGED_GLOBAL_SKILL_MARKER = 'SINAPSE-MANAGED:global-skill';
|
|
133
175
|
|
|
134
|
-
function removeManagedGlobalSkills(home = HOME) {
|
|
176
|
+
function removeManagedGlobalSkills(home = HOME, options = {}) {
|
|
135
177
|
let removed = 0;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (fs.
|
|
145
|
-
|
|
146
|
-
|
|
178
|
+
const providers = options.providers || ['codex', 'claude-code'];
|
|
179
|
+
const skillRoots = [];
|
|
180
|
+
if (providers.includes('codex')) skillRoots.push(path.join(home, '.agents', 'skills'));
|
|
181
|
+
if (providers.includes('claude-code')) skillRoots.push(path.join(home, '.claude', 'skills'));
|
|
182
|
+
for (const skillsRoot of skillRoots) {
|
|
183
|
+
for (const skillId of MANAGED_GLOBAL_SKILL_IDS) {
|
|
184
|
+
const skillDir = path.join(skillsRoot, skillId);
|
|
185
|
+
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
186
|
+
if (!fs.existsSync(skillPath)) continue;
|
|
187
|
+
try {
|
|
188
|
+
const content = fs.readFileSync(skillPath, 'utf8');
|
|
189
|
+
if (!content.includes(MANAGED_GLOBAL_SKILL_MARKER)) continue;
|
|
190
|
+
fs.unlinkSync(skillPath);
|
|
191
|
+
if (fs.readdirSync(skillDir).length === 0) fs.rmdirSync(skillDir);
|
|
192
|
+
removed++;
|
|
193
|
+
} catch { /* best-effort */ }
|
|
194
|
+
}
|
|
147
195
|
}
|
|
148
196
|
return { removed };
|
|
149
197
|
}
|
|
150
198
|
|
|
151
199
|
function reconcileInstalledAgents(home, desiredFilenames) {
|
|
152
|
-
const manifest = readInstalledAgentsManifest();
|
|
153
|
-
if (!manifest) return { removed: 0 };
|
|
200
|
+
const manifest = readInstalledAgentsManifest(home);
|
|
154
201
|
const desired = new Set(desiredFilenames);
|
|
202
|
+
const desiredByProvider = {
|
|
203
|
+
'claude-code': new Set([...desired].filter((name) => name.endsWith('.md'))),
|
|
204
|
+
codex: new Set([...desired].filter((name) => name.endsWith('.toml'))),
|
|
205
|
+
};
|
|
155
206
|
let removed = 0;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
207
|
+
const legacyArtifacts = (manifest?.filenames || []).map((filename) => ({
|
|
208
|
+
provider: filename.endsWith('.toml') ? 'codex' : 'claude-code',
|
|
209
|
+
filename,
|
|
210
|
+
sha256: null,
|
|
211
|
+
}));
|
|
212
|
+
const manifestArtifacts = Array.isArray(manifest?.artifacts) ? manifest.artifacts : legacyArtifacts;
|
|
213
|
+
const manifestOwned = new Set(manifestArtifacts.map(({ provider, filename }) => `${provider}/${filename}`));
|
|
214
|
+
const versionedOwned = new Set(Array.isArray(manifest?.artifacts)
|
|
215
|
+
? manifestArtifacts.map(({ provider, filename }) => `${provider}/${filename}`)
|
|
216
|
+
: []);
|
|
217
|
+
const managedMarker = 'SINAPSE-MANAGED:global-agent';
|
|
218
|
+
for (const { provider, filename, sha256 } of manifestArtifacts) {
|
|
219
|
+
if (!desiredByProvider[provider] || desiredByProvider[provider].has(filename)) continue;
|
|
220
|
+
const filepath = providerAgentPath(home, provider, filename);
|
|
162
221
|
if (!fs.existsSync(filepath)) continue;
|
|
163
222
|
try {
|
|
223
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
224
|
+
const digestMatches = Boolean(sha256) && digestFile(filepath) === sha256;
|
|
225
|
+
const legacyMarkerMatches = !Array.isArray(manifest?.artifacts) && content.includes(managedMarker);
|
|
226
|
+
if (sha256 ? !digestMatches : !legacyMarkerMatches) continue;
|
|
164
227
|
fs.unlinkSync(filepath);
|
|
165
228
|
removed++;
|
|
166
229
|
} catch { /* best-effort */ }
|
|
167
230
|
}
|
|
168
|
-
|
|
231
|
+
|
|
232
|
+
for (const [provider, providerDir, extension] of [
|
|
233
|
+
['claude-code', path.join(home, '.claude', 'agents'), '.md'],
|
|
234
|
+
['codex', path.join(home, '.codex', 'agents'), '.toml'],
|
|
235
|
+
['codex', path.join(home, '.codex', 'agents'), '.md'],
|
|
236
|
+
]) {
|
|
237
|
+
if (!fs.existsSync(providerDir)) continue;
|
|
238
|
+
for (const filename of fs.readdirSync(providerDir).filter((name) => name.endsWith(extension))) {
|
|
239
|
+
if (desiredByProvider[provider].has(filename)) continue;
|
|
240
|
+
const filepath = path.join(providerDir, filename);
|
|
241
|
+
try {
|
|
242
|
+
const content = fs.readFileSync(filepath, 'utf8');
|
|
243
|
+
if (versionedOwned.has(`${provider}/${filename}`)) continue;
|
|
244
|
+
if (!manifestOwned.has(`${provider}/${filename}`) && !content.includes(managedMarker)) continue;
|
|
245
|
+
// A manifest entry alone is not proof of current ownership: users can
|
|
246
|
+
// replace a formerly managed file. Only marked artifacts reach this
|
|
247
|
+
// sweep; exact recorded content was handled by the digest path above.
|
|
248
|
+
if (!content.includes(managedMarker)) continue;
|
|
249
|
+
fs.unlinkSync(filepath);
|
|
250
|
+
removed++;
|
|
251
|
+
} catch { /* best-effort */ }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const legacyCommands = path.join(home, '.claude', 'commands', 'SINAPSE', 'agents');
|
|
256
|
+
let legacyCommandDirectoryRemoved = false;
|
|
257
|
+
let legacyCommandsRemoved = 0;
|
|
258
|
+
let legacyCommandsPreserved = 0;
|
|
259
|
+
if (fs.existsSync(legacyCommands)) {
|
|
260
|
+
for (const entry of fs.readdirSync(legacyCommands, { withFileTypes: true })) {
|
|
261
|
+
const filePath = path.join(legacyCommands, entry.name);
|
|
262
|
+
if (!entry.isFile()) {
|
|
263
|
+
legacyCommandsPreserved++;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const content = fs.readFileSync(filePath);
|
|
268
|
+
const digest = crypto.createHash('sha256').update(content).digest('hex');
|
|
269
|
+
const knownHashes = legacyClaudeCommandHashes.files[entry.name] || [];
|
|
270
|
+
if (!knownHashes.includes(digest)) {
|
|
271
|
+
legacyCommandsPreserved++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
fs.unlinkSync(filePath);
|
|
275
|
+
legacyCommandsRemoved++;
|
|
276
|
+
} catch {
|
|
277
|
+
legacyCommandsPreserved++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
if (fs.readdirSync(legacyCommands).length === 0) {
|
|
282
|
+
fs.rmdirSync(legacyCommands);
|
|
283
|
+
legacyCommandDirectoryRemoved = true;
|
|
284
|
+
}
|
|
285
|
+
} catch { /* best-effort */ }
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
removed,
|
|
289
|
+
legacyCommandDirectoryRemoved,
|
|
290
|
+
legacyCommandsRemoved,
|
|
291
|
+
legacyCommandsPreserved,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function hasManagedInstalledAgents(home = HOME) {
|
|
296
|
+
const manifest = readInstalledAgentsManifest(home);
|
|
297
|
+
if (!manifest) {
|
|
298
|
+
for (const [providerDir, extensions] of [
|
|
299
|
+
[path.join(home, '.claude', 'agents'), new Set(['.md'])],
|
|
300
|
+
[path.join(home, '.codex', 'agents'), new Set(['.toml', '.md'])],
|
|
301
|
+
]) {
|
|
302
|
+
try {
|
|
303
|
+
for (const filename of fs.readdirSync(providerDir)) {
|
|
304
|
+
if (!extensions.has(path.extname(filename))) continue;
|
|
305
|
+
try {
|
|
306
|
+
const content = fs.readFileSync(path.join(providerDir, filename), 'utf8');
|
|
307
|
+
if (content.includes('SINAPSE-MANAGED:global-agent')) return true;
|
|
308
|
+
} catch { /* unreadable or non-regular entries are not evidence */ }
|
|
309
|
+
}
|
|
310
|
+
} catch { /* missing or unreadable provider dir */ }
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
const versioned = Array.isArray(manifest?.artifacts);
|
|
315
|
+
const artifacts = versioned
|
|
316
|
+
? manifest.artifacts
|
|
317
|
+
: (manifest?.filenames || []).map((filename) => ({
|
|
318
|
+
provider: filename.endsWith('.toml') ? 'codex' : 'claude-code',
|
|
319
|
+
filename,
|
|
320
|
+
sha256: null,
|
|
321
|
+
}));
|
|
322
|
+
return artifacts.some(({ provider, filename, sha256 }) => {
|
|
323
|
+
const filePath = providerAgentPath(home, provider, filename);
|
|
324
|
+
try {
|
|
325
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
326
|
+
return sha256
|
|
327
|
+
? digestFile(filePath) === sha256
|
|
328
|
+
: !versioned && content.includes('SINAPSE-MANAGED:global-agent');
|
|
329
|
+
} catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
});
|
|
169
333
|
}
|
|
170
334
|
|
|
171
335
|
// Story 10.40 — Strip SINAPSE-owned keys from ~/.claude/settings.json without
|
|
@@ -312,6 +476,7 @@ module.exports = {
|
|
|
312
476
|
removeOrqxAgentsFrom,
|
|
313
477
|
removeManagedGlobalSkills,
|
|
314
478
|
reconcileInstalledAgents,
|
|
479
|
+
hasManagedInstalledAgents,
|
|
315
480
|
cleanClaudeSettingsJson,
|
|
316
481
|
removeGitHooksConfig,
|
|
317
482
|
INSTALLED_AGENTS_MANIFEST,
|
package/bin/commands/update.js
CHANGED
|
@@ -36,6 +36,7 @@ const {
|
|
|
36
36
|
} = require('./uninstall');
|
|
37
37
|
const { regenerateAgentCommands } = require('../lib/command-generator');
|
|
38
38
|
const { deliverGlobalProviderAdapters, getGlobalCommandStagingDir } = require('../lib/global-provider-adapters');
|
|
39
|
+
const { assertProviderAdapterParity } = require('../lib/provider-parity');
|
|
39
40
|
const { execSync } = require('child_process');
|
|
40
41
|
|
|
41
42
|
// Query the latest version published to npm. Returns null when npm is unreachable.
|
|
@@ -180,12 +181,14 @@ async function cmdUpdateGlobal() {
|
|
|
180
181
|
|
|
181
182
|
// Phase 2b: Install global agents based on LLM choice
|
|
182
183
|
const globalAdapters = deliverGlobalProviderAdapters({ llmChoice, home: HOME, commandsDir: commandStagingDir });
|
|
184
|
+
const activeAgentCount = assertProviderAdapterParity(llmChoice, globalAdapters, totalAgents);
|
|
183
185
|
if (globalAdapters.claude.length) logger.always(` ${GREEN}OK${NC} Claude Code global agents (${globalAdapters.claude.length})`);
|
|
184
186
|
if (globalAdapters.codex.length) logger.always(` ${GREEN}OK${NC} Codex global agents (${globalAdapters.codex.length} TOML, ${globalAdapters.skills.length} skills)`);
|
|
185
187
|
|
|
186
188
|
const installedAgentFilenames = new Set([...globalAdapters.claude, ...globalAdapters.codex]);
|
|
187
189
|
reconcileInstalledAgents(HOME, installedAgentFilenames);
|
|
188
|
-
if (llmChoice === 'claude-code') removeManagedGlobalSkills(HOME);
|
|
190
|
+
if (llmChoice === 'claude-code') removeManagedGlobalSkills(HOME, { providers: ['codex'] });
|
|
191
|
+
if (llmChoice === 'codex') removeManagedGlobalSkills(HOME, { providers: ['claude-code'] });
|
|
189
192
|
const installedIdes = [];
|
|
190
193
|
if (globalAdapters.claude.length) installedIdes.push('claude-code');
|
|
191
194
|
if (globalAdapters.codex.length) installedIdes.push('codex');
|
|
@@ -203,7 +206,8 @@ async function cmdUpdateGlobal() {
|
|
|
203
206
|
meta.updatedAt = new Date().toISOString();
|
|
204
207
|
meta.version = VERSION;
|
|
205
208
|
meta.squads = squads.length;
|
|
206
|
-
meta.
|
|
209
|
+
meta.agents = activeAgentCount;
|
|
210
|
+
meta.commands = activeAgentCount;
|
|
207
211
|
meta.llm = llmChoice;
|
|
208
212
|
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
209
213
|
|
|
@@ -246,9 +250,9 @@ async function cmdUpdateGlobal() {
|
|
|
246
250
|
logger.always(`${GREEN} SINAPSE AI atualizado para v${VERSION}!${NC}`);
|
|
247
251
|
logger.always(`${GREEN}══════════════════════════════════════════════════════════════${NC}`);
|
|
248
252
|
logger.always('');
|
|
249
|
-
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${
|
|
253
|
+
logger.always(` ${BOLD}${squads.length} squads${NC} | ${BOLD}${activeAgentCount} agents${NC} | ${BOLD}${activeAgentCount} native adapters${NC}`);
|
|
250
254
|
logger.always(` ${startCmd}`);
|
|
251
255
|
logger.always('');
|
|
252
256
|
}
|
|
253
257
|
|
|
254
|
-
module.exports = { cmdUpdateGlobal };
|
|
258
|
+
module.exports = { cmdUpdateGlobal, assertProviderAdapterParity };
|