sinapse-ai 1.25.3 → 1.27.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/.agents/skills/react-bits-frontend/SKILL.md +13 -4
- package/.claude/skill-manifest.json +1 -0
- package/.claude/skills/react-bits-frontend/SKILL.md +75 -0
- package/.codex/scripts/sync-codex-native.js +12 -0
- package/.sinapse-ai/install-manifest.yaml +1 -1
- package/CHANGELOG.md +11 -2
- package/README.en.md +98 -411
- package/README.md +99 -458
- package/bin/commands/install.js +48 -11
- package/bin/commands/uninstall.js +1 -1
- package/bin/commands/update.js +19 -5
- package/bin/lib/detection.js +1 -1
- package/bin/lib/global-provider-adapters.js +22 -10
- package/bin/lib/prompts.js +9 -9
- package/bin/lib/provider-contract.js +2 -0
- package/bin/lib/provider-parity.js +11 -5
- package/docs/getting-started.md +2 -2
- package/docs/guides/ide-integration.md +1 -1
- package/package.json +1 -1
- package/packages/installer/src/installer/react-bits-corpus.js +90 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +9 -0
- package/scripts/sync-provider-adapters.js +7 -2
- package/scripts/validate-provider-adapters.js +21 -1
- package/squads/squad-animations/agents/animations-orqx.md +7 -0
- package/squads/squad-animations/squad.yaml +1 -1
package/bin/commands/install.js
CHANGED
|
@@ -50,6 +50,10 @@ const {
|
|
|
50
50
|
} = require('../lib/command-generator');
|
|
51
51
|
const { deliverGlobalProviderAdapters, getGlobalCommandStagingDir } = require('../lib/global-provider-adapters');
|
|
52
52
|
const { assertProviderAdapterParity } = require('../lib/provider-parity');
|
|
53
|
+
const {
|
|
54
|
+
copyReactBitsCapabilitySync,
|
|
55
|
+
REACT_BITS_SKILL_RELATIVE_PATH,
|
|
56
|
+
} = require('../../packages/installer/src/installer/react-bits-corpus');
|
|
53
57
|
// Follow-up #13 — wire the transactional backup/rollback engine into the
|
|
54
58
|
// installer so an in-place UPGRADE (upsert) that fails mid-flight is restored
|
|
55
59
|
// to its previous state instead of leaving ~/.sinapse half-updated.
|
|
@@ -130,17 +134,18 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
130
134
|
|
|
131
135
|
// LLM selection (skipped in upsert mode if previous llm known)
|
|
132
136
|
// Story 10.35: --reconfigure forces prompt even in upsert mode
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
const llmChoice = await resolveLlmChoice({
|
|
138
|
+
requestedLlm,
|
|
139
|
+
isUpsert,
|
|
140
|
+
reconfigure,
|
|
141
|
+
existingLlm: existing.llm,
|
|
142
|
+
});
|
|
143
|
+
const llmWasReused = Boolean(isUpsert && !reconfigure && !requestedLlm && existing.llm);
|
|
144
|
+
if (llmWasReused) {
|
|
140
145
|
const ideLabel = Array.isArray(llmChoice) ? llmChoice.join(', ') : String(llmChoice);
|
|
141
146
|
logger.always(`${DIM} IDE: ${ideLabel} (from saved config; pass --reconfigure to change)${NC}`);
|
|
142
|
-
} else {
|
|
143
|
-
|
|
147
|
+
} else if (!requestedLlm && !reconfigure) {
|
|
148
|
+
logger.always(`${DIM} IDE: Claude Code + Codex (padrao; use --llm para limitar)${NC}`);
|
|
144
149
|
}
|
|
145
150
|
if (languageWasReused || llmWasReused) logger.always('');
|
|
146
151
|
|
|
@@ -317,7 +322,12 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
317
322
|
// rich, frontmatter'd stubs are always the final word. Idempotent.
|
|
318
323
|
try {
|
|
319
324
|
const commandsDir = getGlobalCommandStagingDir({ llmChoice, sinapseHome: SINAPSE_HOME, claudeCommandsDir: CLAUDE_COMMANDS_DIR });
|
|
320
|
-
const reconciled = deliverGlobalProviderAdapters({
|
|
325
|
+
const reconciled = deliverGlobalProviderAdapters({
|
|
326
|
+
llmChoice,
|
|
327
|
+
home: HOME,
|
|
328
|
+
commandsDir,
|
|
329
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
330
|
+
});
|
|
321
331
|
const total = reconciled.claude.length + reconciled.codex.length + reconciled.skills.length;
|
|
322
332
|
if (total) logger.always(` ${GREEN}OK${NC} Global provider adapters reconciled (${total} artifacts)`);
|
|
323
333
|
} catch (e) {
|
|
@@ -373,6 +383,24 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
373
383
|
logger.always('');
|
|
374
384
|
}
|
|
375
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Resolves provider selection independently of filesystem writes and prompts.
|
|
388
|
+
* Explicit flags win; existing configured installs remain stable until the
|
|
389
|
+
* user reconfigures them; fresh or legacy-unconfigured installs use both.
|
|
390
|
+
*/
|
|
391
|
+
async function resolveLlmChoice({
|
|
392
|
+
requestedLlm = null,
|
|
393
|
+
isUpsert = false,
|
|
394
|
+
reconfigure = false,
|
|
395
|
+
existingLlm = null,
|
|
396
|
+
prompt = promptLlmChoice,
|
|
397
|
+
} = {}) {
|
|
398
|
+
if (requestedLlm) return requestedLlm;
|
|
399
|
+
if (isUpsert && !reconfigure && existingLlm) return existingLlm;
|
|
400
|
+
if (reconfigure) return prompt();
|
|
401
|
+
return 'both';
|
|
402
|
+
}
|
|
403
|
+
|
|
376
404
|
// ── Transactional install helpers (follow-up #13) ────────────────────────────
|
|
377
405
|
|
|
378
406
|
/**
|
|
@@ -557,6 +585,9 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
557
585
|
logger.always(` ${GREEN}OK${NC} core development (${coreAgents} agents)`);
|
|
558
586
|
}
|
|
559
587
|
|
|
588
|
+
const reactBits = copyReactBitsCapabilitySync(ROOT, SINAPSE_HOME);
|
|
589
|
+
if (reactBits.files.length) logger.always(` ${GREEN}OK${NC} React Bits corpus (${reactBits.files.length} files)`);
|
|
590
|
+
|
|
560
591
|
// Phase 2: Generate agent commands (shared with `update` via command-generator).
|
|
561
592
|
logger.always(`\n${CYAN}Phase 2:${NC} Generating agent commands`);
|
|
562
593
|
const commandStagingDir = getGlobalCommandStagingDir({ llmChoice, sinapseHome: SINAPSE_HOME, claudeCommandsDir: CLAUDE_COMMANDS_DIR });
|
|
@@ -570,7 +601,12 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
570
601
|
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} total command files`);
|
|
571
602
|
|
|
572
603
|
// Phase 2b: Install global agents based on LLM choice
|
|
573
|
-
const globalAdapters = deliverGlobalProviderAdapters({
|
|
604
|
+
const globalAdapters = deliverGlobalProviderAdapters({
|
|
605
|
+
llmChoice,
|
|
606
|
+
home: HOME,
|
|
607
|
+
commandsDir: commandStagingDir,
|
|
608
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
609
|
+
});
|
|
574
610
|
const activeAgentCount = assertProviderAdapterParity(
|
|
575
611
|
llmChoice,
|
|
576
612
|
globalAdapters,
|
|
@@ -854,6 +890,7 @@ function ensurePathWindows() {
|
|
|
854
890
|
|
|
855
891
|
module.exports = {
|
|
856
892
|
cmdInstallGlobal,
|
|
893
|
+
resolveLlmChoice,
|
|
857
894
|
generateCommandMd,
|
|
858
895
|
generateSquadAwareness,
|
|
859
896
|
createLauncher,
|
|
@@ -170,7 +170,7 @@ function removeOrqxAgentsFrom(dir) {
|
|
|
170
170
|
return removeInstalledAgentsFrom(dir);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
const MANAGED_GLOBAL_SKILL_IDS = ['snps', 'sinapse', 'snps-orqx', 'sinapse-orqx', 'sinapse-agent'];
|
|
173
|
+
const MANAGED_GLOBAL_SKILL_IDS = ['snps', 'sinapse', 'snps-orqx', 'sinapse-orqx', 'sinapse-agent', 'react-bits-frontend'];
|
|
174
174
|
const MANAGED_GLOBAL_SKILL_MARKER = 'SINAPSE-MANAGED:global-skill';
|
|
175
175
|
|
|
176
176
|
function removeManagedGlobalSkills(home = HOME, options = {}) {
|
package/bin/commands/update.js
CHANGED
|
@@ -27,7 +27,6 @@ const {
|
|
|
27
27
|
detectExistingInstall,
|
|
28
28
|
detectStaleness,
|
|
29
29
|
} = require('../lib/detection');
|
|
30
|
-
const { promptLlmChoice } = require('../lib/prompts');
|
|
31
30
|
const { generateSquadAwareness } = require('./install');
|
|
32
31
|
const {
|
|
33
32
|
recordInstalledAgents,
|
|
@@ -36,6 +35,10 @@ const {
|
|
|
36
35
|
} = require('./uninstall');
|
|
37
36
|
const { regenerateAgentCommands } = require('../lib/command-generator');
|
|
38
37
|
const { deliverGlobalProviderAdapters, getGlobalCommandStagingDir } = require('../lib/global-provider-adapters');
|
|
38
|
+
const {
|
|
39
|
+
copyReactBitsCapabilitySync,
|
|
40
|
+
REACT_BITS_SKILL_RELATIVE_PATH,
|
|
41
|
+
} = require('../../packages/installer/src/installer/react-bits-corpus');
|
|
39
42
|
const { assertProviderAdapterParity } = require('../lib/provider-parity');
|
|
40
43
|
const { execSync } = require('child_process');
|
|
41
44
|
|
|
@@ -118,9 +121,12 @@ async function cmdUpdateGlobal() {
|
|
|
118
121
|
logger.always('');
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
// Story 10.22 —
|
|
122
|
-
// run `npx sinapse-ai install --
|
|
123
|
-
const llmChoice = existing.llm ||
|
|
124
|
+
// Story 10.22 — preserve the saved provider selection. To choose again,
|
|
125
|
+
// run `npx sinapse-ai@latest install --reconfigure`.
|
|
126
|
+
const llmChoice = existing.llm || 'both';
|
|
127
|
+
if (!existing.llm) {
|
|
128
|
+
logger.always(`${DIM} IDE: Claude Code + Codex (padrao para instalacoes legadas sem provider salvo)${NC}`);
|
|
129
|
+
}
|
|
124
130
|
|
|
125
131
|
logger.always('');
|
|
126
132
|
logger.always(`${BOLD}Atualizando SINAPSE AI...${NC}\n`);
|
|
@@ -164,6 +170,9 @@ async function cmdUpdateGlobal() {
|
|
|
164
170
|
logger.always(` ${GREEN}OK${NC} core development`);
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
const reactBits = copyReactBitsCapabilitySync(ROOT, SINAPSE_HOME);
|
|
174
|
+
if (reactBits.files.length) logger.always(` ${GREEN}OK${NC} React Bits corpus (${reactBits.files.length} files)`);
|
|
175
|
+
|
|
167
176
|
// Phase 2: Regenerate commands — shared with `install` so `update` produces the
|
|
168
177
|
// SAME complete set (every specialist command + the rich master Imperator stubs),
|
|
169
178
|
// not just the `-orqx` subset it used to write.
|
|
@@ -180,7 +189,12 @@ async function cmdUpdateGlobal() {
|
|
|
180
189
|
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} command files (${totalAgents} agents total)`);
|
|
181
190
|
|
|
182
191
|
// Phase 2b: Install global agents based on LLM choice
|
|
183
|
-
const globalAdapters = deliverGlobalProviderAdapters({
|
|
192
|
+
const globalAdapters = deliverGlobalProviderAdapters({
|
|
193
|
+
llmChoice,
|
|
194
|
+
home: HOME,
|
|
195
|
+
commandsDir: commandStagingDir,
|
|
196
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
197
|
+
});
|
|
184
198
|
const activeAgentCount = assertProviderAdapterParity(
|
|
185
199
|
llmChoice,
|
|
186
200
|
globalAdapters,
|
package/bin/lib/detection.js
CHANGED
|
@@ -82,7 +82,7 @@ function warnNonInteractive() {
|
|
|
82
82
|
nonInteractiveWarned = true;
|
|
83
83
|
// stderr so it never pollutes stdout consumers (CI logs, pipes).
|
|
84
84
|
process.stderr.write(
|
|
85
|
-
`${YELLOW}INFO${NC} ambiente nao-interativo detectado, usando defaults pt +
|
|
85
|
+
`${YELLOW}INFO${NC} ambiente nao-interativo detectado, usando defaults pt + Claude Code + Codex ` +
|
|
86
86
|
`${DIM}(use --interactive pra forcar prompts)${NC}\n`,
|
|
87
87
|
);
|
|
88
88
|
}
|
|
@@ -6,6 +6,7 @@ const crypto = require('crypto');
|
|
|
6
6
|
const {
|
|
7
7
|
MASTER_ALIAS_ENTRY_POINTS,
|
|
8
8
|
GLOBAL_PROVIDER_SKILL_IDS,
|
|
9
|
+
GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS,
|
|
9
10
|
SUPREME_ORCHESTRATOR_ID,
|
|
10
11
|
SUPREME_PUBLIC_ID,
|
|
11
12
|
} = require('./provider-contract');
|
|
@@ -102,7 +103,11 @@ function markGlobalAgent(markdown, fileName = '') {
|
|
|
102
103
|
return `${content.trimEnd()}\n\n<!-- SINAPSE-MANAGED:global-agent -->\n`;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
function renderGlobalSkill(skillId) {
|
|
106
|
+
function renderGlobalSkill(skillId, sourceContent = null) {
|
|
107
|
+
if (skillId === 'react-bits-frontend') {
|
|
108
|
+
if (!sourceContent) throw new Error('React Bits global skill source is required');
|
|
109
|
+
return `${String(sourceContent).trimEnd()}\n\n<!-- SINAPSE-MANAGED:global-skill -->\n`;
|
|
110
|
+
}
|
|
106
111
|
const generic = skillId === 'sinapse-agent';
|
|
107
112
|
return [
|
|
108
113
|
'---',
|
|
@@ -264,9 +269,9 @@ function writeGlobalSkillIfManaged(skillPath, content, home = path.dirname(skill
|
|
|
264
269
|
}
|
|
265
270
|
}
|
|
266
271
|
|
|
267
|
-
function isValidGlobalSkill(skillId, skillPath) {
|
|
272
|
+
function isValidGlobalSkill(skillId, skillPath, sourceContent = null) {
|
|
268
273
|
const content = readRegularFileNoFollowSync(skillPath, 'utf8');
|
|
269
|
-
return content !== null && content === renderGlobalSkill(skillId);
|
|
274
|
+
return content !== null && content === renderGlobalSkill(skillId, sourceContent);
|
|
270
275
|
}
|
|
271
276
|
|
|
272
277
|
function removeStaleManagedAgents(targetDir, expectedFiles, extension, options = {}) {
|
|
@@ -318,7 +323,7 @@ function removeManagedFileWithBinding(home, filePath, beforeDelete) {
|
|
|
318
323
|
}
|
|
319
324
|
}
|
|
320
325
|
|
|
321
|
-
function deliverGlobalProviderAdapters({ llmChoice, home, commandsDir, testHooks = {} }) {
|
|
326
|
+
function deliverGlobalProviderAdapters({ llmChoice, home, commandsDir, reactBitsSkillPath, testHooks = {} }) {
|
|
322
327
|
let commandFiles = fs.existsSync(commandsDir)
|
|
323
328
|
? fs.readdirSync(commandsDir).filter((file) => file.endsWith('.md')).sort()
|
|
324
329
|
: [];
|
|
@@ -334,6 +339,13 @@ function deliverGlobalProviderAdapters({ llmChoice, home, commandsDir, testHooks
|
|
|
334
339
|
skills: [],
|
|
335
340
|
availableSkills: [],
|
|
336
341
|
};
|
|
342
|
+
let reactBitsSkillContent = null;
|
|
343
|
+
const providerSkillIds = [...GLOBAL_PROVIDER_SKILL_IDS];
|
|
344
|
+
if (reactBitsSkillPath) {
|
|
345
|
+
reactBitsSkillContent = readRegularFileNoFollowSync(reactBitsSkillPath, 'utf8');
|
|
346
|
+
if (reactBitsSkillContent === null) throw new Error(`React Bits global skill is missing: ${reactBitsSkillPath}`);
|
|
347
|
+
providerSkillIds.push(...GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS);
|
|
348
|
+
}
|
|
337
349
|
|
|
338
350
|
if (llmChoice === 'claude-code' || llmChoice === 'both') {
|
|
339
351
|
const targetDir = path.join(home, '.claude', 'agents');
|
|
@@ -345,12 +357,12 @@ function deliverGlobalProviderAdapters({ llmChoice, home, commandsDir, testHooks
|
|
|
345
357
|
written.claude.push(file);
|
|
346
358
|
}
|
|
347
359
|
const skillsRoot = path.join(home, '.claude', 'skills');
|
|
348
|
-
for (const skillId of
|
|
360
|
+
for (const skillId of providerSkillIds) {
|
|
349
361
|
const skillPath = path.join(skillsRoot, skillId, 'SKILL.md');
|
|
350
|
-
if (writeGlobalSkillIfManaged(skillPath, renderGlobalSkill(skillId), home, { beforePublish: testHooks.beforeSkillPublish })) {
|
|
362
|
+
if (writeGlobalSkillIfManaged(skillPath, renderGlobalSkill(skillId, reactBitsSkillContent), home, { beforePublish: testHooks.beforeSkillPublish })) {
|
|
351
363
|
written.claudeSkills.push(skillId);
|
|
352
364
|
}
|
|
353
|
-
if (isValidGlobalSkill(skillId, skillPath)) written.claudeAvailableSkills.push(skillId);
|
|
365
|
+
if (isValidGlobalSkill(skillId, skillPath, reactBitsSkillContent)) written.claudeAvailableSkills.push(skillId);
|
|
354
366
|
}
|
|
355
367
|
}
|
|
356
368
|
if (llmChoice === 'codex' || llmChoice === 'both') {
|
|
@@ -373,13 +385,13 @@ function deliverGlobalProviderAdapters({ llmChoice, home, commandsDir, testHooks
|
|
|
373
385
|
written.codex.push(tomlName);
|
|
374
386
|
}
|
|
375
387
|
const skillsRoot = path.join(home, '.agents', 'skills');
|
|
376
|
-
for (const skillId of
|
|
388
|
+
for (const skillId of providerSkillIds) {
|
|
377
389
|
const skillDir = path.join(skillsRoot, skillId);
|
|
378
390
|
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
379
|
-
if (writeGlobalSkillIfManaged(skillPath, renderGlobalSkill(skillId), home, { beforePublish: testHooks.beforeSkillPublish })) {
|
|
391
|
+
if (writeGlobalSkillIfManaged(skillPath, renderGlobalSkill(skillId, reactBitsSkillContent), home, { beforePublish: testHooks.beforeSkillPublish })) {
|
|
380
392
|
written.skills.push(skillId);
|
|
381
393
|
}
|
|
382
|
-
if (isValidGlobalSkill(skillId, skillPath)) written.availableSkills.push(skillId);
|
|
394
|
+
if (isValidGlobalSkill(skillId, skillPath, reactBitsSkillContent)) written.availableSkills.push(skillId);
|
|
383
395
|
}
|
|
384
396
|
}
|
|
385
397
|
return written;
|
package/bin/lib/prompts.js
CHANGED
|
@@ -15,7 +15,7 @@ async function promptLlmChoice() {
|
|
|
15
15
|
// check that silently skipped this prompt in Git Bash + Windows.
|
|
16
16
|
if (!detectInteractiveMode()) {
|
|
17
17
|
warnNonInteractive();
|
|
18
|
-
return '
|
|
18
|
+
return 'both';
|
|
19
19
|
}
|
|
20
20
|
try {
|
|
21
21
|
const inquirer = require('inquirer');
|
|
@@ -24,11 +24,11 @@ async function promptLlmChoice() {
|
|
|
24
24
|
name: 'llms',
|
|
25
25
|
message: 'Escolha sua LLM (espaco seleciona, enter confirma):',
|
|
26
26
|
choices: [
|
|
27
|
-
{ name: 'Claude Code
|
|
28
|
-
{ name: 'Codex CLI', value: 'codex' },
|
|
27
|
+
{ name: 'Claude Code', value: 'claude-code', checked: true },
|
|
28
|
+
{ name: 'Codex CLI', value: 'codex', checked: true },
|
|
29
29
|
],
|
|
30
30
|
}]);
|
|
31
|
-
if (llms.length === 0) return '
|
|
31
|
+
if (llms.length === 0) return 'both'; // default if none selected
|
|
32
32
|
if (llms.length === 2) return 'both';
|
|
33
33
|
return llms[0];
|
|
34
34
|
} catch {
|
|
@@ -37,16 +37,16 @@ async function promptLlmChoice() {
|
|
|
37
37
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
38
38
|
return new Promise((resolve) => {
|
|
39
39
|
logger.always(`${CYAN} Escolha sua LLM:${NC}`);
|
|
40
|
-
logger.always(` ${GREEN}1${NC}) Claude Code
|
|
40
|
+
logger.always(` ${GREEN}1${NC}) Claude Code`);
|
|
41
41
|
logger.always(` ${GREEN}2${NC}) Codex CLI`);
|
|
42
|
-
logger.always(` ${GREEN}3${NC}) Ambos`);
|
|
42
|
+
logger.always(` ${GREEN}3${NC}) Ambos ${DIM}(Recomendado)${NC}`);
|
|
43
43
|
logger.always('');
|
|
44
44
|
rl.question(` ${BOLD}Opcao [1/2/3]:${NC} `, (answer) => {
|
|
45
45
|
rl.close();
|
|
46
|
-
const choice = (answer || '
|
|
46
|
+
const choice = (answer || '3').trim();
|
|
47
47
|
if (choice === '2') resolve('codex');
|
|
48
|
-
else if (choice === '
|
|
49
|
-
else resolve('
|
|
48
|
+
else if (choice === '1') resolve('claude-code');
|
|
49
|
+
else resolve('both');
|
|
50
50
|
});
|
|
51
51
|
});
|
|
52
52
|
}
|
|
@@ -13,6 +13,7 @@ const GLOBAL_PROVIDER_SKILL_IDS = Object.freeze([
|
|
|
13
13
|
SUPREME_ORCHESTRATOR_ID,
|
|
14
14
|
'sinapse-agent',
|
|
15
15
|
]);
|
|
16
|
+
const GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS = Object.freeze(['react-bits-frontend']);
|
|
16
17
|
|
|
17
18
|
module.exports = {
|
|
18
19
|
CANONICAL_AGENT_COUNT,
|
|
@@ -20,4 +21,5 @@ module.exports = {
|
|
|
20
21
|
SUPREME_PUBLIC_ID,
|
|
21
22
|
MASTER_ALIAS_ENTRY_POINTS,
|
|
22
23
|
GLOBAL_PROVIDER_SKILL_IDS,
|
|
24
|
+
GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS,
|
|
23
25
|
};
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
const {
|
|
4
4
|
CANONICAL_AGENT_COUNT,
|
|
5
5
|
GLOBAL_PROVIDER_SKILL_IDS,
|
|
6
|
+
GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS,
|
|
6
7
|
} = require('./provider-contract');
|
|
7
8
|
|
|
9
|
+
const REQUIRED_GLOBAL_PROVIDER_SKILL_IDS = Object.freeze([
|
|
10
|
+
...GLOBAL_PROVIDER_SKILL_IDS,
|
|
11
|
+
...GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS,
|
|
12
|
+
]);
|
|
13
|
+
|
|
8
14
|
function normalizeCanonicalCatalog(canonicalCatalog, extension) {
|
|
9
15
|
if (!canonicalCatalog || typeof canonicalCatalog === 'number') {
|
|
10
16
|
throw new Error('Provider adapter parity requires the canonical agent ID catalog');
|
|
@@ -55,14 +61,14 @@ function assertProviderAdapterParity(llmChoice, adapters, canonicalCatalog) {
|
|
|
55
61
|
if (llmChoice === 'claude-code' || llmChoice === 'both') {
|
|
56
62
|
problems.push(...describeCatalogDivergence('Claude Code', adapters.claude, claudeCatalog));
|
|
57
63
|
const claudeSkills = new Set(adapters.claudeAvailableSkills || adapters.claudeSkills || []);
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
64
|
+
const missingSkills = REQUIRED_GLOBAL_PROVIDER_SKILL_IDS.filter((skillId) => !claudeSkills.has(skillId));
|
|
65
|
+
if (missingSkills.length) problems.push(`Claude Code global skills missing: ${missingSkills.join(', ')}`);
|
|
60
66
|
}
|
|
61
67
|
if (llmChoice === 'codex' || llmChoice === 'both') {
|
|
62
68
|
problems.push(...describeCatalogDivergence('Codex', adapters.codex, codexCatalog));
|
|
63
69
|
const codexSkills = new Set(adapters.availableSkills || adapters.skills || []);
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
70
|
+
const missingSkills = REQUIRED_GLOBAL_PROVIDER_SKILL_IDS.filter((skillId) => !codexSkills.has(skillId));
|
|
71
|
+
if (missingSkills.length) problems.push(`Codex global skills missing: ${missingSkills.join(', ')}`);
|
|
66
72
|
}
|
|
67
73
|
if (problems.length) {
|
|
68
74
|
throw new Error(`Provider adapter parity failed (${problems.join('; ')})`);
|
|
@@ -70,4 +76,4 @@ function assertProviderAdapterParity(llmChoice, adapters, canonicalCatalog) {
|
|
|
70
76
|
return claudeCatalog.count;
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
module.exports = { assertProviderAdapterParity };
|
|
79
|
+
module.exports = { assertProviderAdapterParity, REQUIRED_GLOBAL_PROVIDER_SKILL_IDS };
|
package/docs/getting-started.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## Pré-requisitos
|
|
6
6
|
|
|
7
|
-
- **Node.js** ≥
|
|
7
|
+
- **Node.js** ≥ 18 (Node 22 LTS recomendado)
|
|
8
8
|
- **Claude Code** ou **Codex CLI** instalado
|
|
9
9
|
- **Git** (pra colaboração)
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
npx sinapse-ai@latest install
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
O wizard detecta seu ambiente,
|
|
17
|
+
O wizard detecta seu ambiente e, em instalações novas ou sem provider salvo, instala Claude Code e Codex por padrão, além dos 17 squads e hooks essenciais. Instalações existentes preservam o provider salvo; use `--reconfigure` para alterá-lo. Use `--llm=claude-code` ou `--llm=codex` somente quando quiser limitar a instalação a um provider.
|
|
18
18
|
|
|
19
19
|
## Validar setup
|
|
20
20
|
|
|
@@ -32,7 +32,7 @@ Both providers expose native lifecycle surfaces. Their registration models diffe
|
|
|
32
32
|
| Measured surface | Claude Code | Codex CLI |
|
|
33
33
|
| --- | --- | --- |
|
|
34
34
|
| Native agents | 172 | 172 |
|
|
35
|
-
| Installed skills |
|
|
35
|
+
| Installed skills | 37 | 37 |
|
|
36
36
|
| Registered hooks | 20 native registrations | 9 lifecycle events through the bridge |
|
|
37
37
|
| Activation | `@agent-name` | `$snps` or `$sinapse-agent <id>` |
|
|
38
38
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const REACT_BITS_CORPUS_RELATIVE_PATH = path.join('docs', 'framework', 'react-bits');
|
|
7
|
+
const REACT_BITS_SKILL_RELATIVE_PATH = path.join('.agents', 'skills', 'react-bits-frontend', 'SKILL.md');
|
|
8
|
+
const REQUIRED_REACT_BITS_CORPUS_FILES = Object.freeze([
|
|
9
|
+
'animations.md',
|
|
10
|
+
'audit-findings.md',
|
|
11
|
+
'backgrounds.md',
|
|
12
|
+
'catalog-summary.json',
|
|
13
|
+
'components.md',
|
|
14
|
+
'implementation-playbook.md',
|
|
15
|
+
'index.md',
|
|
16
|
+
'inventory.json',
|
|
17
|
+
'text-animations.md',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
function isRegularFile(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.lstatSync(filePath).isFile();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error.code === 'ENOENT') return false;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function listFilesRecursively(directory, root = directory) {
|
|
30
|
+
if (!fs.existsSync(directory)) return [];
|
|
31
|
+
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
32
|
+
const sourcePath = path.join(directory, entry.name);
|
|
33
|
+
if (entry.isDirectory()) return listFilesRecursively(sourcePath, root);
|
|
34
|
+
return isRegularFile(sourcePath) ? [path.relative(root, sourcePath)] : [];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getMissingReactBitsCorpusFiles(root) {
|
|
39
|
+
return REQUIRED_REACT_BITS_CORPUS_FILES.filter((file) => !isRegularFile(
|
|
40
|
+
path.join(root, REACT_BITS_CORPUS_RELATIVE_PATH, file),
|
|
41
|
+
));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Copy only the shipped React Bits corpus. Existing destination-only files are
|
|
46
|
+
* intentionally left untouched so a project or global installation never
|
|
47
|
+
* deletes user research alongside the managed corpus.
|
|
48
|
+
*/
|
|
49
|
+
function copyReactBitsCorpusSync(sourceRoot, destinationRoot) {
|
|
50
|
+
const source = path.join(sourceRoot, REACT_BITS_CORPUS_RELATIVE_PATH);
|
|
51
|
+
const destination = path.join(destinationRoot, REACT_BITS_CORPUS_RELATIVE_PATH);
|
|
52
|
+
const missingFiles = getMissingReactBitsCorpusFiles(sourceRoot);
|
|
53
|
+
if (missingFiles.length > 0) {
|
|
54
|
+
throw new Error(`Incomplete React Bits corpus source: ${missingFiles.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
const files = listFilesRecursively(source);
|
|
57
|
+
for (const relativePath of files) {
|
|
58
|
+
const target = path.join(destination, relativePath);
|
|
59
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
60
|
+
fs.copyFileSync(path.join(source, relativePath), target);
|
|
61
|
+
}
|
|
62
|
+
return files.map((relativePath) => path.join(REACT_BITS_CORPUS_RELATIVE_PATH, relativePath));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function hasCompleteReactBitsCorpus(root) {
|
|
66
|
+
return getMissingReactBitsCorpusFiles(root).length === 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Copy the corpus plus its canonical skill into a global SINAPSE home. */
|
|
70
|
+
function copyReactBitsCapabilitySync(sourceRoot, destinationRoot) {
|
|
71
|
+
const files = copyReactBitsCorpusSync(sourceRoot, destinationRoot);
|
|
72
|
+
const sourceSkill = path.join(sourceRoot, REACT_BITS_SKILL_RELATIVE_PATH);
|
|
73
|
+
const destinationSkill = path.join(destinationRoot, REACT_BITS_SKILL_RELATIVE_PATH);
|
|
74
|
+
if (!fs.existsSync(sourceSkill)) throw new Error(`Missing React Bits skill: ${sourceSkill}`);
|
|
75
|
+
fs.mkdirSync(path.dirname(destinationSkill), { recursive: true });
|
|
76
|
+
fs.copyFileSync(sourceSkill, destinationSkill);
|
|
77
|
+
return { files, skillPath: destinationSkill };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = {
|
|
81
|
+
REACT_BITS_CORPUS_RELATIVE_PATH,
|
|
82
|
+
REACT_BITS_SKILL_RELATIVE_PATH,
|
|
83
|
+
REQUIRED_REACT_BITS_CORPUS_FILES,
|
|
84
|
+
isRegularFile,
|
|
85
|
+
listFilesRecursively,
|
|
86
|
+
getMissingReactBitsCorpusFiles,
|
|
87
|
+
copyReactBitsCorpusSync,
|
|
88
|
+
copyReactBitsCapabilitySync,
|
|
89
|
+
hasCompleteReactBitsCorpus,
|
|
90
|
+
};
|
|
@@ -14,6 +14,7 @@ const path = require('path');
|
|
|
14
14
|
const crypto = require('crypto');
|
|
15
15
|
const ora = require('ora');
|
|
16
16
|
const { hashFile } = require('./file-hasher');
|
|
17
|
+
const { copyReactBitsCorpusSync } = require('./react-bits-corpus');
|
|
17
18
|
|
|
18
19
|
const NOFOLLOW_READ_FLAGS = nativeFs.constants.O_RDONLY | (nativeFs.constants.O_NOFOLLOW || 0);
|
|
19
20
|
|
|
@@ -705,6 +706,13 @@ async function installSinapseCore(options = {}) {
|
|
|
705
706
|
if (claudeSettings) result.installedFiles.push(claudeSettings);
|
|
706
707
|
}
|
|
707
708
|
|
|
709
|
+
if (includeClaude || includeCodex) {
|
|
710
|
+
spinner.text = 'Copying React Bits corpus...';
|
|
711
|
+
const corpusFiles = copyReactBitsCorpusSync(pkgRoot, targetDir);
|
|
712
|
+
result.reactBitsCorpusFiles = corpusFiles.length;
|
|
713
|
+
result.installedFiles.push(...corpusFiles);
|
|
714
|
+
}
|
|
715
|
+
|
|
708
716
|
// AGENTS.md is the Codex CLI's default context file (Imperator greeting +
|
|
709
717
|
// the full agent roster). It lives at the package ROOT (not under .codex/ or
|
|
710
718
|
// .sinapse-ai/), so neither loop above reaches it. Without it, a Codex user's
|
|
@@ -878,6 +886,7 @@ module.exports = {
|
|
|
878
886
|
deliverClaudeNativeAdapters,
|
|
879
887
|
getClaudeHookName,
|
|
880
888
|
reconcileClaudeHookSettings,
|
|
889
|
+
copyReactBitsCorpusSync,
|
|
881
890
|
FOLDERS_TO_COPY,
|
|
882
891
|
ROOT_FILES_TO_COPY,
|
|
883
892
|
CODEX_ROOT_FILES,
|
|
@@ -11,10 +11,12 @@ const {
|
|
|
11
11
|
syncCodexNativeAgents,
|
|
12
12
|
} = require('../.codex/scripts/sync-codex-native');
|
|
13
13
|
const { resolveCodexAgent } = require('../.codex/scripts/resolve-codex-agent');
|
|
14
|
+
const { GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS } = require('../bin/lib/provider-contract');
|
|
14
15
|
|
|
15
16
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
16
17
|
const CLAUDE_AGENTS_DIR = '.claude/agents';
|
|
17
18
|
const CLAUDE_SKILLS_DIR = '.claude/skills';
|
|
19
|
+
const SUPPLEMENTAL_PROVIDER_SKILL_IDS = GLOBAL_SUPPLEMENTAL_PROVIDER_SKILL_IDS;
|
|
18
20
|
|
|
19
21
|
function writeFileAtomically(filePath, content) {
|
|
20
22
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
@@ -136,11 +138,13 @@ function collectClaudeSkills(projectRoot = PROJECT_ROOT) {
|
|
|
136
138
|
catalog.genericAgentSkillId,
|
|
137
139
|
])].sort();
|
|
138
140
|
|
|
139
|
-
return skillIds.map((skillId) => {
|
|
141
|
+
return [...new Set([...skillIds, ...SUPPLEMENTAL_PROVIDER_SKILL_IDS])].sort().map((skillId) => {
|
|
140
142
|
const nativePath = path.join(projectRoot, '.agents', 'skills', skillId, 'SKILL.md');
|
|
141
143
|
const metadata = parseSkillMetadata(fs.readFileSync(nativePath, 'utf8'));
|
|
142
144
|
let content;
|
|
143
|
-
if (skillId
|
|
145
|
+
if (SUPPLEMENTAL_PROVIDER_SKILL_IDS.includes(skillId)) {
|
|
146
|
+
content = fs.readFileSync(nativePath, 'utf8');
|
|
147
|
+
} else if (skillId === catalog.genericAgentSkillId) {
|
|
144
148
|
content = renderClaudeGenericSkill(skillId, metadata.description);
|
|
145
149
|
} else if (skillId === 'sinapse-loop' || skillId === 'sinapse-spec-driven') {
|
|
146
150
|
content = renderClaudeWorkflowSkill(skillId, metadata.description);
|
|
@@ -195,6 +199,7 @@ if (require.main === module) main();
|
|
|
195
199
|
module.exports = {
|
|
196
200
|
CLAUDE_AGENTS_DIR,
|
|
197
201
|
CLAUDE_SKILLS_DIR,
|
|
202
|
+
SUPPLEMENTAL_PROVIDER_SKILL_IDS,
|
|
198
203
|
claudeAgentFileName,
|
|
199
204
|
renderClaudeAgent,
|
|
200
205
|
collectClaudeSkills,
|
|
@@ -8,6 +8,11 @@ const yaml = require('js-yaml');
|
|
|
8
8
|
const legacyClaudeCommandHashes = require('../packages/installer/src/migrations/legacy-claude-agent-command-hashes.json');
|
|
9
9
|
|
|
10
10
|
const { validateNativeCodex } = require('../.codex/scripts/validate-codex-native');
|
|
11
|
+
const {
|
|
12
|
+
REACT_BITS_CORPUS_RELATIVE_PATH,
|
|
13
|
+
REQUIRED_REACT_BITS_CORPUS_FILES,
|
|
14
|
+
hasCompleteReactBitsCorpus,
|
|
15
|
+
} = require('../packages/installer/src/installer/react-bits-corpus');
|
|
11
16
|
|
|
12
17
|
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
13
18
|
|
|
@@ -125,10 +130,13 @@ function validateClaudeNative(projectRoot = PROJECT_ROOT) {
|
|
|
125
130
|
try {
|
|
126
131
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
127
132
|
expectedSkills = (manifest.skillIds || []).map((skillId) => ({ skillId }));
|
|
128
|
-
if (expectedSkills.length !==
|
|
133
|
+
if (expectedSkills.length !== 37) errors.push(`Claude skill manifest must contain 37 IDs, found ${expectedSkills.length}`);
|
|
129
134
|
for (const alias of ['sinapse', 'sinapse-orqx', 'snps', 'snps-orqx', 'sinapse-agent']) {
|
|
130
135
|
if (!manifest.skillIds.includes(alias)) errors.push(`Claude skill manifest is missing public alias ${alias}`);
|
|
131
136
|
}
|
|
137
|
+
if (!manifest.skillIds.includes('react-bits-frontend')) {
|
|
138
|
+
errors.push('Claude skill manifest is missing react-bits-frontend');
|
|
139
|
+
}
|
|
132
140
|
} catch (error) {
|
|
133
141
|
errors.push(`Claude skill manifest: ${error.message}`);
|
|
134
142
|
}
|
|
@@ -209,6 +217,17 @@ function validateProviderAdapters(projectRoot = PROJECT_ROOT) {
|
|
|
209
217
|
if (!pkg.files.includes(entry)) errors.push(`package.json files[] is missing ${entry}`);
|
|
210
218
|
}
|
|
211
219
|
if (pkg.files.includes('.codex/skills/')) errors.push('package.json must not publish the legacy .codex/skills root');
|
|
220
|
+
if (!hasCompleteReactBitsCorpus(projectRoot)) {
|
|
221
|
+
errors.push(`React Bits corpus is incomplete: expected ${REQUIRED_REACT_BITS_CORPUS_FILES.length} files under ${REACT_BITS_CORPUS_RELATIVE_PATH}`);
|
|
222
|
+
}
|
|
223
|
+
for (const relativePath of [
|
|
224
|
+
path.join('.agents', 'skills', 'react-bits-frontend', 'SKILL.md'),
|
|
225
|
+
path.join('.claude', 'skills', 'react-bits-frontend', 'SKILL.md'),
|
|
226
|
+
]) {
|
|
227
|
+
if (!fs.existsSync(path.join(projectRoot, relativePath))) {
|
|
228
|
+
errors.push(`React Bits provider skill is missing: ${relativePath}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
212
231
|
return {
|
|
213
232
|
ok: errors.length === 0,
|
|
214
233
|
errors,
|
|
@@ -240,4 +259,5 @@ module.exports = {
|
|
|
240
259
|
validateClaudeHookSettings,
|
|
241
260
|
validateClaudeNative,
|
|
242
261
|
validateProviderAdapters,
|
|
262
|
+
REQUIRED_REACT_BITS_CORPUS_FILES,
|
|
243
263
|
};
|
|
@@ -28,6 +28,8 @@ Kinetic e o diretor da squad. Recebe os Animation Briefs do Decoder (animation-i
|
|
|
28
28
|
- Garantir coesao entre animacoes de diferentes agentes
|
|
29
29
|
- Orquestrar o workflow prompt-to-animation-cycle
|
|
30
30
|
- Gerar relatorios de entrega
|
|
31
|
+
- Roteiar trabalho React Bits pelo task `orchestrate-react-bits-frontend` e pelo
|
|
32
|
+
corpus operacional antes de delegar implementacao
|
|
31
33
|
|
|
32
34
|
## Pipeline de Orquestracao
|
|
33
35
|
|
|
@@ -64,6 +66,7 @@ Kinetic → Review final → Entrega
|
|
|
64
66
|
| Hover effects | css-motion-artist | shader-artist |
|
|
65
67
|
| Loading animations | css-motion-artist | motion-choreographer |
|
|
66
68
|
| Camera movements 3D | threejs-architect | motion-choreographer |
|
|
69
|
+
| React Bits frontend | orchestrate-react-bits-frontend | animation-performance-engineer |
|
|
67
70
|
|
|
68
71
|
## Criterios de Qualidade
|
|
69
72
|
|
|
@@ -86,6 +89,7 @@ Toda animacao deve passar por:
|
|
|
86
89
|
| Definir timing e coreografia | motion-choreographer (Tempo) |
|
|
87
90
|
| Implementar scroll animation | scroll-narrative-engineer (Parallax) |
|
|
88
91
|
| Criar sistema de particulas | generative-particle-engineer (Cloud) |
|
|
92
|
+
| Orquestrar frontend React Bits | orchestrate-react-bits-frontend |
|
|
89
93
|
| Otimizar performance | animation-performance-engineer (Benchmark) |
|
|
90
94
|
|
|
91
95
|
## NON-NEGOTIABLE: ORCHESTRATE, DON'T EXECUTE
|
|
@@ -135,6 +139,9 @@ integration:
|
|
|
135
139
|
- agent: "animation-performance-engineer (Benchmark)"
|
|
136
140
|
when: "Quality gate before delivery — perf audit, 60fps validation, mobile check"
|
|
137
141
|
context_passed: "all built animations, target devices, perf budget"
|
|
142
|
+
- agent: "orchestrate-react-bits-frontend"
|
|
143
|
+
when: "Frontend request that needs React Bits discovery, selection, installation or implementation"
|
|
144
|
+
context_passed: "project stack, target surface, desired interaction, accessibility and performance constraints"
|
|
138
145
|
receives_from:
|
|
139
146
|
- agent: "@sinapse-orqx (Imperator)"
|
|
140
147
|
when: "Animation/motion request routed from ecosystem"
|