sinapse-ai 1.25.3 → 1.26.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 +1 -1
- package/README.md +1 -1
- package/bin/commands/install.js +19 -2
- package/bin/commands/uninstall.js +1 -1
- package/bin/commands/update.js +13 -1
- package/bin/lib/global-provider-adapters.js +22 -10
- package/bin/lib/provider-contract.js +2 -0
- package/bin/lib/provider-parity.js +11 -5
- 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
|
@@ -10,8 +10,16 @@ not as a black-box library.
|
|
|
10
10
|
|
|
11
11
|
## Locate the corpus
|
|
12
12
|
|
|
13
|
-
Resolve the
|
|
14
|
-
|
|
13
|
+
Resolve the corpus in this exact order, then read `index.md` from the first
|
|
14
|
+
location that contains the complete corpus:
|
|
15
|
+
|
|
16
|
+
1. `<project>/docs/framework/react-bits/`
|
|
17
|
+
2. `<installed-package>/docs/framework/react-bits/`
|
|
18
|
+
3. `~/.sinapse/docs/framework/react-bits/`
|
|
19
|
+
|
|
20
|
+
The project location wins so a project can pin or extend its installed research.
|
|
21
|
+
The package location supports package-managed runs, and the global SINAPSE home
|
|
22
|
+
supports `--global-only` installs. Read only the category needed:
|
|
15
23
|
|
|
16
24
|
- `animations.md` for wrappers, cursors, trails, transitions and interactive effects.
|
|
17
25
|
- `text-animations.md` for headline, counter, scramble, marquee and reveal work.
|
|
@@ -21,8 +29,9 @@ Resolve the SINAPSE package/repository root, then read
|
|
|
21
29
|
- `audit-findings.md` when dependency, prop, variant or upstream accuracy matters.
|
|
22
30
|
- `inventory.json` for exact machine-readable props, dependencies and variant state.
|
|
23
31
|
|
|
24
|
-
If
|
|
25
|
-
|
|
32
|
+
If none of those locations has all nine files, state which locations were checked,
|
|
33
|
+
ask the user to run the relevant SINAPSE install/update, then verify current facts
|
|
34
|
+
against the official registry and source before implementation.
|
|
26
35
|
|
|
27
36
|
## Workflow
|
|
28
37
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-bits-frontend
|
|
3
|
+
description: Select, install, adapt, combine, optimize, and audit official React Bits components for React, Next.js, Vite, Astro islands, and SINAPSE frontend projects. Use for animated text, cursors, reveals, cards, navigation, galleries, WebGL backgrounds, shaders, particles, motion systems, React Bits CLI/MCP commands, component props/dependencies, or when improving an existing page with React Bits while preserving accessibility, mobile performance, lifecycle cleanup, design-system fit, and license compliance.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Bits Frontend
|
|
7
|
+
|
|
8
|
+
Use the source-grounded SINAPSE corpus to implement React Bits as application code,
|
|
9
|
+
not as a black-box library.
|
|
10
|
+
|
|
11
|
+
## Locate the corpus
|
|
12
|
+
|
|
13
|
+
Resolve the corpus in this exact order, then read `index.md` from the first
|
|
14
|
+
location that contains the complete corpus:
|
|
15
|
+
|
|
16
|
+
1. `<project>/docs/framework/react-bits/`
|
|
17
|
+
2. `<installed-package>/docs/framework/react-bits/`
|
|
18
|
+
3. `~/.sinapse/docs/framework/react-bits/`
|
|
19
|
+
|
|
20
|
+
The project location wins so a project can pin or extend its installed research.
|
|
21
|
+
The package location supports package-managed runs, and the global SINAPSE home
|
|
22
|
+
supports `--global-only` installs. Read only the category needed:
|
|
23
|
+
|
|
24
|
+
- `animations.md` for wrappers, cursors, trails, transitions and interactive effects.
|
|
25
|
+
- `text-animations.md` for headline, counter, scramble, marquee and reveal work.
|
|
26
|
+
- `components.md` for cards, navigation, galleries, inputs and compound UI.
|
|
27
|
+
- `backgrounds.md` for Canvas/WebGL/shader/particle backgrounds.
|
|
28
|
+
- `implementation-playbook.md` for integration, composition, accessibility and tuning.
|
|
29
|
+
- `audit-findings.md` when dependency, prop, variant or upstream accuracy matters.
|
|
30
|
+
- `inventory.json` for exact machine-readable props, dependencies and variant state.
|
|
31
|
+
|
|
32
|
+
If none of those locations has all nine files, state which locations were checked,
|
|
33
|
+
ask the user to run the relevant SINAPSE install/update, then verify current facts
|
|
34
|
+
against the official registry and source before implementation.
|
|
35
|
+
|
|
36
|
+
## Workflow
|
|
37
|
+
|
|
38
|
+
1. Inspect the target stack, component architecture, design tokens, performance budget
|
|
39
|
+
and existing animation/scroll ownership.
|
|
40
|
+
2. Translate the visual request into a user-experience function.
|
|
41
|
+
3. Search the relevant catalog and shortlist at most three candidates.
|
|
42
|
+
4. Compare visual fit, dependencies, runtime engine, props, interaction model,
|
|
43
|
+
accessibility, mobile cost and compatibility with existing effects.
|
|
44
|
+
5. Open the official source for the selected variant. Treat source and registry as
|
|
45
|
+
authoritative when docs conflict.
|
|
46
|
+
6. Run `npm view <package>` for every declared dependency before installing it.
|
|
47
|
+
7. Prefer TS-TW or TS-CSS in TypeScript projects. Never use the three empty CurvedInput
|
|
48
|
+
variants recorded in the snapshot; use JS-CSS or verify that upstream fixed them.
|
|
49
|
+
8. Install through shadcn/jsrepo or copy manually. Keep upstream provenance and the
|
|
50
|
+
required license notice for substantial copies.
|
|
51
|
+
9. Wrap the imported component behind a local application component. Map design
|
|
52
|
+
tokens, content, breakpoints and quality presets at that boundary.
|
|
53
|
+
10. Implement reduced-motion behavior, semantic fallback, keyboard/focus behavior,
|
|
54
|
+
cleanup and static/mobile fallback.
|
|
55
|
+
11. Test load, resize, route unmount/remount, long scroll, coarse pointer, reduced
|
|
56
|
+
motion and low-power mobile. Measure frame time and memory.
|
|
57
|
+
12. Report component, variant, source SHA/URL, dependencies, adaptations, fallbacks and
|
|
58
|
+
remaining risks.
|
|
59
|
+
|
|
60
|
+
## Composition rules
|
|
61
|
+
|
|
62
|
+
- Use one dominant motion effect per viewport and no more than two support effects.
|
|
63
|
+
- Prefer the lowest-cost engine that satisfies the visual objective.
|
|
64
|
+
- Allow only one owner for smooth scroll, custom cursor and each render loop.
|
|
65
|
+
- Do not stack multiple heavy WebGL/physics backgrounds without an explicit budget.
|
|
66
|
+
- Pause offscreen/hidden loops and dispose GPU resources on unmount.
|
|
67
|
+
- Preserve content and interaction when animation is disabled or fails.
|
|
68
|
+
|
|
69
|
+
## Accuracy rules
|
|
70
|
+
|
|
71
|
+
- Do not repeat the promotional “140+” as an exact count. The pinned snapshot has 139.
|
|
72
|
+
- Do not infer functional variants from registry names alone; inspect source bytes/code.
|
|
73
|
+
- Use registry JSON for install dependencies and source for actual API/behavior.
|
|
74
|
+
- Note documentation typos and per-variant divergences from `audit-findings.md`.
|
|
75
|
+
- Do not redistribute React Bits components as a standalone library, bundle, or port.
|
|
@@ -34,6 +34,10 @@ function isPathInside(parentPath, candidatePath) {
|
|
|
34
34
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function isSafeSkillId(skillId) {
|
|
38
|
+
return typeof skillId === 'string' && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(skillId);
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
function readCanonicalSource(projectRoot, sourcePath) {
|
|
38
42
|
if (!sourcePath) {
|
|
39
43
|
throw new Error('Codex agent pointer does not declare a canonical source');
|
|
@@ -329,6 +333,14 @@ function readCodexCatalog(projectRoot = PROJECT_ROOT) {
|
|
|
329
333
|
if (typeof catalog.genericAgentSkillId !== 'string' || !catalog.genericAgentSkillId.trim()) {
|
|
330
334
|
throw new Error('Codex catalog does not declare genericAgentSkillId');
|
|
331
335
|
}
|
|
336
|
+
const catalogSkillIds = [
|
|
337
|
+
...catalog.expectedSkillIds,
|
|
338
|
+
...catalog.publicAliasSkillIds,
|
|
339
|
+
catalog.genericAgentSkillId,
|
|
340
|
+
];
|
|
341
|
+
if (catalogSkillIds.some((skillId) => !isSafeSkillId(skillId))) {
|
|
342
|
+
throw new Error('Codex catalog contains an unsafe skill ID');
|
|
343
|
+
}
|
|
332
344
|
return catalog;
|
|
333
345
|
}
|
|
334
346
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
## [1.
|
|
1
|
+
## [1.26.0](https://github.com/caioimori/sinapse-ai/compare/1.25.3...1.26.0) (2026-07-15)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* deliver React Bits across providers [Story react-bits-distribution-parity] ([aa191f1](https://github.com/caioimori/sinapse-ai/commit/aa191f102dc417e3a0182c02661907e474226abb))
|
|
2
6
|
|
|
3
7
|
### Bug Fixes
|
|
4
8
|
|
|
5
|
-
* **
|
|
9
|
+
* **docs:** align provider skill metrics [Story react-bits-distribution-parity] ([729d120](https://github.com/caioimori/sinapse-ai/commit/729d120ee1735473d01971efdb746be59d2d19d8))
|
|
10
|
+
* **react-bits:** harden corpus delivery and orchestration [Story react-bits-distribution-parity] ([6456062](https://github.com/caioimori/sinapse-ai/commit/6456062cd756826e6bc14c698ebde07298d8a5eb))
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
* close installer parity release evidence [Story hotfix-installer-provider-adapter-parity-1.25.3] ([#386](https://github.com/caioimori/sinapse-ai/issues/386)) ([ceafcb8](https://github.com/caioimori/sinapse-ai/commit/ceafcb8ac522835c264115e9ee16cba6e49e173c))
|
|
6
15
|
|
|
7
16
|
# Changelog
|
|
8
17
|
|
package/README.en.md
CHANGED
|
@@ -309,7 +309,7 @@ Both IDEs have access to all 17 squads, 172 agents, workflows, and knowledge bas
|
|
|
309
309
|
| Native agents | 172 | 172 |
|
|
310
310
|
| Direct activation | `@agent-name` | `$sinapse-agent agent-id` |
|
|
311
311
|
| Primary routing | `@sinapse-orqx` | `$snps` |
|
|
312
|
-
| Installed skills |
|
|
312
|
+
| Installed skills | 37 | 37 |
|
|
313
313
|
| Registered hooks | 20 native registrations | 9 lifecycle events through the bridge |
|
|
314
314
|
| Validation source | `npm run validate:providers` | `npm run validate:codex-native` |
|
|
315
315
|
|
package/README.md
CHANGED
|
@@ -331,7 +331,7 @@ Ambas as IDEs têm acesso a todos os 17 squads, 172 agentes, workflows e knowled
|
|
|
331
331
|
| Agentes nativos | 172 | 172 |
|
|
332
332
|
| Ativação direta | `@agent-name` | `$sinapse-agent agent-id` |
|
|
333
333
|
| Roteamento principal | `@sinapse-orqx` | `$snps` |
|
|
334
|
-
| Skills instaladas |
|
|
334
|
+
| Skills instaladas | 37 | 37 |
|
|
335
335
|
| Hooks registrados | 20 registros nativos | 9 eventos de lifecycle via bridge |
|
|
336
336
|
| Fonte de validação | `npm run validate:providers` | `npm run validate:codex-native` |
|
|
337
337
|
|
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.
|
|
@@ -317,7 +321,12 @@ async function cmdInstallGlobal(opts = {}) {
|
|
|
317
321
|
// rich, frontmatter'd stubs are always the final word. Idempotent.
|
|
318
322
|
try {
|
|
319
323
|
const commandsDir = getGlobalCommandStagingDir({ llmChoice, sinapseHome: SINAPSE_HOME, claudeCommandsDir: CLAUDE_COMMANDS_DIR });
|
|
320
|
-
const reconciled = deliverGlobalProviderAdapters({
|
|
324
|
+
const reconciled = deliverGlobalProviderAdapters({
|
|
325
|
+
llmChoice,
|
|
326
|
+
home: HOME,
|
|
327
|
+
commandsDir,
|
|
328
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
329
|
+
});
|
|
321
330
|
const total = reconciled.claude.length + reconciled.codex.length + reconciled.skills.length;
|
|
322
331
|
if (total) logger.always(` ${GREEN}OK${NC} Global provider adapters reconciled (${total} artifacts)`);
|
|
323
332
|
} catch (e) {
|
|
@@ -557,6 +566,9 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
557
566
|
logger.always(` ${GREEN}OK${NC} core development (${coreAgents} agents)`);
|
|
558
567
|
}
|
|
559
568
|
|
|
569
|
+
const reactBits = copyReactBitsCapabilitySync(ROOT, SINAPSE_HOME);
|
|
570
|
+
if (reactBits.files.length) logger.always(` ${GREEN}OK${NC} React Bits corpus (${reactBits.files.length} files)`);
|
|
571
|
+
|
|
560
572
|
// Phase 2: Generate agent commands (shared with `update` via command-generator).
|
|
561
573
|
logger.always(`\n${CYAN}Phase 2:${NC} Generating agent commands`);
|
|
562
574
|
const commandStagingDir = getGlobalCommandStagingDir({ llmChoice, sinapseHome: SINAPSE_HOME, claudeCommandsDir: CLAUDE_COMMANDS_DIR });
|
|
@@ -570,7 +582,12 @@ function installFatalPhases({ squads, squadsDir, isUpsert, llmChoice, existing,
|
|
|
570
582
|
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} total command files`);
|
|
571
583
|
|
|
572
584
|
// Phase 2b: Install global agents based on LLM choice
|
|
573
|
-
const globalAdapters = deliverGlobalProviderAdapters({
|
|
585
|
+
const globalAdapters = deliverGlobalProviderAdapters({
|
|
586
|
+
llmChoice,
|
|
587
|
+
home: HOME,
|
|
588
|
+
commandsDir: commandStagingDir,
|
|
589
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
590
|
+
});
|
|
574
591
|
const activeAgentCount = assertProviderAdapterParity(
|
|
575
592
|
llmChoice,
|
|
576
593
|
globalAdapters,
|
|
@@ -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
|
@@ -36,6 +36,10 @@ 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 {
|
|
40
|
+
copyReactBitsCapabilitySync,
|
|
41
|
+
REACT_BITS_SKILL_RELATIVE_PATH,
|
|
42
|
+
} = require('../../packages/installer/src/installer/react-bits-corpus');
|
|
39
43
|
const { assertProviderAdapterParity } = require('../lib/provider-parity');
|
|
40
44
|
const { execSync } = require('child_process');
|
|
41
45
|
|
|
@@ -164,6 +168,9 @@ async function cmdUpdateGlobal() {
|
|
|
164
168
|
logger.always(` ${GREEN}OK${NC} core development`);
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
const reactBits = copyReactBitsCapabilitySync(ROOT, SINAPSE_HOME);
|
|
172
|
+
if (reactBits.files.length) logger.always(` ${GREEN}OK${NC} React Bits corpus (${reactBits.files.length} files)`);
|
|
173
|
+
|
|
167
174
|
// Phase 2: Regenerate commands — shared with `install` so `update` produces the
|
|
168
175
|
// SAME complete set (every specialist command + the rich master Imperator stubs),
|
|
169
176
|
// not just the `-orqx` subset it used to write.
|
|
@@ -180,7 +187,12 @@ async function cmdUpdateGlobal() {
|
|
|
180
187
|
logger.always(` ${GREEN}OK${NC} ${writtenAgents.size} command files (${totalAgents} agents total)`);
|
|
181
188
|
|
|
182
189
|
// Phase 2b: Install global agents based on LLM choice
|
|
183
|
-
const globalAdapters = deliverGlobalProviderAdapters({
|
|
190
|
+
const globalAdapters = deliverGlobalProviderAdapters({
|
|
191
|
+
llmChoice,
|
|
192
|
+
home: HOME,
|
|
193
|
+
commandsDir: commandStagingDir,
|
|
194
|
+
reactBitsSkillPath: path.join(SINAPSE_HOME, REACT_BITS_SKILL_RELATIVE_PATH),
|
|
195
|
+
});
|
|
184
196
|
const activeAgentCount = assertProviderAdapterParity(
|
|
185
197
|
llmChoice,
|
|
186
198
|
globalAdapters,
|
|
@@ -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;
|
|
@@ -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 };
|
|
@@ -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"
|