sinapse-ai 1.25.0 → 1.25.2

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.
Files changed (118) hide show
  1. package/.claude/settings.json +170 -0
  2. package/.codex/catalog.json +1 -1
  3. package/.codex/scripts/sinapse-codex.js +0 -5
  4. package/.codex/scripts/sync-codex-native.js +4 -37
  5. package/.codex/scripts/validate-codex-native.js +13 -23
  6. package/.codex/tasks/onboard-sinapse-codex.md +1 -1
  7. package/.sinapse-ai/data/entity-registry.yaml +44 -18
  8. package/.sinapse-ai/install-manifest.yaml +3 -3
  9. package/CHANGELOG.md +8 -14
  10. package/README.en.md +38 -30
  11. package/README.md +33 -25
  12. package/bin/cli.js +8 -1
  13. package/bin/commands/help.js +3 -2
  14. package/bin/commands/install.js +25 -14
  15. package/bin/commands/status.js +4 -3
  16. package/bin/commands/uninstall.js +216 -51
  17. package/bin/commands/update.js +8 -4
  18. package/bin/lib/global-provider-adapters.js +73 -10
  19. package/bin/lib/provider-parity.js +22 -0
  20. package/bin/postinstall.js +11 -11
  21. package/docs/framework/codex-parity/codex-integration-process.md +6 -7
  22. package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
  23. package/docs/framework/codex-parity/codex-parity-program.md +4 -4
  24. package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
  25. package/docs/getting-started.md +13 -5
  26. package/docs/guides/codex-config.md +35 -51
  27. package/docs/guides/development-setup.md +1 -2
  28. package/docs/guides/ide-integration.md +28 -29
  29. package/docs/guides/project-status-feature.md +35 -55
  30. package/docs/guides/quality-gates.md +2 -2
  31. package/docs/guides/user-guide.md +21 -10
  32. package/docs/ide-integration.md +2 -2
  33. package/docs/installation/README.md +4 -4
  34. package/docs/installation/faq.md +84 -70
  35. package/docs/installation/linux.md +11 -14
  36. package/docs/installation/macos.md +10 -4
  37. package/docs/installation/troubleshooting.md +11 -5
  38. package/docs/installation/v4-quick-start.md +14 -14
  39. package/docs/installation/windows.md +11 -15
  40. package/docs/pt/contributing.md +1 -1
  41. package/docs/pt/guides/development-setup.md +1 -2
  42. package/docs/pt/guides/project-status-feature.md +35 -55
  43. package/docs/pt/guides/quality-gates.md +2 -2
  44. package/docs/pt/guides/user-guide.md +21 -11
  45. package/docs/pt/ide-integration.md +6 -6
  46. package/docs/pt/installation/README.md +2 -4
  47. package/docs/pt/installation/faq.md +54 -89
  48. package/docs/pt/installation/linux.md +9 -27
  49. package/docs/pt/installation/macos.md +8 -12
  50. package/docs/pt/installation/troubleshooting.md +11 -10
  51. package/docs/pt/installation/v4-quick-start.md +17 -18
  52. package/docs/pt/installation/windows.md +8 -21
  53. package/docs/pt/platforms/claude-code.md +4 -4
  54. package/docs/sinapse-agent-flows/analyst-system.md +1 -1
  55. package/docs/sinapse-agent-flows/architect-system.md +1 -1
  56. package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
  57. package/docs/sinapse-agent-flows/dev-system.md +1 -1
  58. package/docs/sinapse-agent-flows/pm-system.md +1 -1
  59. package/docs/sinapse-agent-flows/qa-system.md +1 -1
  60. package/docs/sinapse-agent-flows/sm-system.md +1 -1
  61. package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
  62. package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
  63. package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
  64. package/docs/troubleshooting.md +9 -7
  65. package/package.json +21 -16
  66. package/packages/installer/src/config/ide-configs.js +1 -1
  67. package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
  68. package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
  69. package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
  70. package/packages/installer/src/updater/index.js +24 -7
  71. package/packages/installer/src/wizard/ide-config-generator.js +4 -0
  72. package/packages/installer/src/wizard/index.js +6 -2
  73. package/scripts/validate-article-vii.js +305 -20
  74. package/scripts/validate-codex-command-registry.js +101 -0
  75. package/scripts/validate-install-docs.js +87 -3
  76. package/scripts/validate-provider-adapters.js +117 -1
  77. package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
  78. package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
  79. package/squads/squad-design/agents/ux-designer.md +1 -1
  80. package/.codex/skills/sinapse/SKILL.md +0 -10
  81. package/.codex/skills/sinapse-agent/SKILL.md +0 -22
  82. package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
  83. package/.codex/skills/sinapse-animations/SKILL.md +0 -28
  84. package/.codex/skills/sinapse-architect/SKILL.md +0 -31
  85. package/.codex/skills/sinapse-brand/SKILL.md +0 -28
  86. package/.codex/skills/sinapse-claude/SKILL.md +0 -28
  87. package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
  88. package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
  89. package/.codex/skills/sinapse-content/SKILL.md +0 -28
  90. package/.codex/skills/sinapse-copy/SKILL.md +0 -28
  91. package/.codex/skills/sinapse-council/SKILL.md +0 -28
  92. package/.codex/skills/sinapse-courses/SKILL.md +0 -28
  93. package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
  94. package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
  95. package/.codex/skills/sinapse-design/SKILL.md +0 -28
  96. package/.codex/skills/sinapse-dev/SKILL.md +0 -31
  97. package/.codex/skills/sinapse-devops/SKILL.md +0 -31
  98. package/.codex/skills/sinapse-finance/SKILL.md +0 -28
  99. package/.codex/skills/sinapse-growth/SKILL.md +0 -28
  100. package/.codex/skills/sinapse-loop/SKILL.md +0 -35
  101. package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
  102. package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
  103. package/.codex/skills/sinapse-pm/SKILL.md +0 -31
  104. package/.codex/skills/sinapse-po/SKILL.md +0 -31
  105. package/.codex/skills/sinapse-product/SKILL.md +0 -28
  106. package/.codex/skills/sinapse-qa/SKILL.md +0 -31
  107. package/.codex/skills/sinapse-research/SKILL.md +0 -28
  108. package/.codex/skills/sinapse-sm/SKILL.md +0 -27
  109. package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
  110. package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
  111. package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
  112. package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
  113. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
  114. package/.codex/skills/snps/SKILL.md +0 -10
  115. package/.codex/skills/snps-orqx/SKILL.md +0 -10
  116. /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
  117. /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
  118. /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
@@ -9,10 +9,29 @@
9
9
  */
10
10
 
11
11
  const fs = require('fs-extra');
12
+ const nativeFs = require('fs');
12
13
  const path = require('path');
14
+ const crypto = require('crypto');
13
15
  const ora = require('ora');
14
16
  const { hashFile } = require('./file-hasher');
15
17
 
18
+ const NOFOLLOW_READ_FLAGS = nativeFs.constants.O_RDONLY | (nativeFs.constants.O_NOFOLLOW || 0);
19
+
20
+ async function readRegularFileNoFollow(filePath) {
21
+ let handle;
22
+ try {
23
+ handle = await nativeFs.promises.open(filePath, NOFOLLOW_READ_FLAGS);
24
+ const stat = await handle.stat();
25
+ if (!stat.isFile()) return null;
26
+ return await handle.readFile();
27
+ } catch (error) {
28
+ if (['ENOENT', 'ELOOP', 'EISDIR'].includes(error.code)) return null;
29
+ throw error;
30
+ } finally {
31
+ if (handle) await handle.close();
32
+ }
33
+ }
34
+
16
35
  /**
17
36
  * Get the path to the source .sinapse-ai directory in the package
18
37
  * @returns {string} Absolute path to .sinapse-ai source
@@ -62,6 +81,82 @@ const ROOT_FILES_TO_COPY = [
62
81
  const CODEX_TOP_LEVEL_DIRS = ['.agents', '.codex'];
63
82
  const CLAUDE_SUPPORT_DIRS = ['.claude/hooks', '.claude/rules'];
64
83
 
84
+ function getClaudeHookName(command) {
85
+ const match = String(command || '').replace(/\\/g, '/').match(/\.claude\/hooks\/([^"'\s]+)/);
86
+ return match ? match[1] : null;
87
+ }
88
+
89
+ function getClaudeHookRegistrationKey(event, matcher, hookName) {
90
+ return JSON.stringify([event, matcher ?? null, hookName]);
91
+ }
92
+
93
+ /** Merge shipped SINAPSE hook registrations without replacing user settings. */
94
+ async function reconcileClaudeHookSettings(packageRoot, targetDir) {
95
+ const sourcePath = path.join(packageRoot, '.claude', 'settings.json');
96
+ if (!await fs.pathExists(sourcePath)) return null;
97
+
98
+ const targetPath = path.join(targetDir, '.claude', 'settings.local.json');
99
+ const source = await fs.readJson(sourcePath);
100
+ let target = {};
101
+ if (await fs.pathExists(targetPath)) {
102
+ try {
103
+ target = await fs.readJson(targetPath);
104
+ } catch (_error) {
105
+ // Preserve malformed user settings verbatim. The installer must not turn
106
+ // a recoverable configuration problem into data loss or a failed install.
107
+ return null;
108
+ }
109
+ }
110
+ if (!target || typeof target !== 'object' || Array.isArray(target)) return null;
111
+ if (!target.hooks || typeof target.hooks !== 'object' || Array.isArray(target.hooks)) {
112
+ target.hooks = {};
113
+ }
114
+
115
+ const existingSettings = [target];
116
+ const projectSettingsPath = path.join(targetDir, '.claude', 'settings.json');
117
+ if (await fs.pathExists(projectSettingsPath)) {
118
+ try {
119
+ const projectSettings = await fs.readJson(projectSettingsPath);
120
+ if (projectSettings && typeof projectSettings === 'object' && !Array.isArray(projectSettings)) {
121
+ existingSettings.push(projectSettings);
122
+ }
123
+ } catch (_error) {
124
+ // An invalid sibling settings file cannot provide reliable registrations;
125
+ // ignore it while retaining its contents and merge into settings.local.
126
+ }
127
+ }
128
+ const registered = new Set();
129
+ for (const settings of existingSettings) {
130
+ for (const [event, groups] of Object.entries(settings.hooks || {})) {
131
+ for (const group of Array.isArray(groups) ? groups : []) {
132
+ for (const hook of Array.isArray(group.hooks) ? group.hooks : []) {
133
+ const hookName = getClaudeHookName(hook.command);
134
+ if (hookName) registered.add(getClaudeHookRegistrationKey(event, group.matcher, hookName));
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ for (const [event, groups] of Object.entries(source.hooks || {})) {
141
+ if (!Array.isArray(target.hooks[event])) target.hooks[event] = [];
142
+ for (const group of groups || []) {
143
+ for (const hook of group.hooks || []) {
144
+ const hookName = getClaudeHookName(hook.command);
145
+ const registrationKey = getClaudeHookRegistrationKey(event, group.matcher, hookName);
146
+ if (!hookName || registered.has(registrationKey)) continue;
147
+ const registration = { hooks: [hook] };
148
+ if (group.matcher) registration.matcher = group.matcher;
149
+ target.hooks[event].push(registration);
150
+ registered.add(registrationKey);
151
+ }
152
+ }
153
+ }
154
+
155
+ await fs.ensureDir(path.dirname(targetPath));
156
+ await fs.writeJson(targetPath, target, { spaces: 2 });
157
+ return path.join('.claude', 'settings.local.json');
158
+ }
159
+
65
160
  /**
66
161
  * Replace {root} placeholder in file content
67
162
  * @param {string} content - File content
@@ -121,9 +216,22 @@ async function generateVersionJson(options) {
121
216
  installedFiles,
122
217
  mode = 'project-development',
123
218
  providers = [],
219
+ targetDir = path.dirname(targetSinapseCore),
220
+ providerFiles = [],
221
+ providerSourceRoot = targetDir,
124
222
  } = options;
125
223
 
126
224
  const fileHashes = await generateFileHashes(targetSinapseCore, installedFiles);
225
+ const providerFileHashes = {};
226
+ for (const relativePath of providerFiles) {
227
+ const sourcePath = path.join(providerSourceRoot, relativePath);
228
+ const sourceContent = await readRegularFileNoFollow(sourcePath);
229
+ if (sourceContent !== null) {
230
+ const installedContent = replaceRootPlaceholder(sourceContent.toString('utf8'));
231
+ const digest = crypto.createHash('sha256').update(installedContent).digest('hex');
232
+ providerFileHashes[relativePath.replace(/\\/g, '/')] = `sha256:${digest}`;
233
+ }
234
+ }
127
235
 
128
236
  const versionJson = {
129
237
  version,
@@ -131,6 +239,7 @@ async function generateVersionJson(options) {
131
239
  mode,
132
240
  providers: [...new Set(providers)].sort(),
133
241
  fileHashes,
242
+ providerFileHashes,
134
243
  customized: [],
135
244
  };
136
245
 
@@ -162,17 +271,158 @@ function isUserOwnedL3(destPath) {
162
271
  return false;
163
272
  }
164
273
 
165
- function isUserOwnedCodexConfig(destPath) {
166
- const norm = String(destPath).replace(/\\/g, '/');
167
- if (norm.endsWith('/.codex/config.toml') || norm.endsWith('/.codex/hooks.json')) return true;
168
- if (/\/\.agents\/skills\/[^/]+\/SKILL\.md$/.test(norm)) {
169
- const compatibilityPath = destPath.replace(
170
- `${path.sep}.agents${path.sep}skills${path.sep}`,
171
- `${path.sep}.codex${path.sep}skills${path.sep}`,
274
+ function getManagedCodexSkillPaths(packageRoot) {
275
+ const catalog = fs.readJsonSync(path.join(packageRoot, '.codex', 'catalog.json'));
276
+ return [...new Set([
277
+ ...(catalog.expectedSkillIds || []),
278
+ ...(catalog.publicAliasSkillIds || []),
279
+ catalog.genericAgentSkillId,
280
+ ].filter(Boolean))]
281
+ .sort()
282
+ .map((skillId) => path.posix.join('.agents', 'skills', skillId, 'SKILL.md'));
283
+ }
284
+
285
+ function createCodexPreservePredicate(targetDir, packageRoot, previousProviderHashes = {}) {
286
+ const legacyRegistry = fs.readJsonSync(path.join(
287
+ packageRoot,
288
+ 'packages',
289
+ 'installer',
290
+ 'src',
291
+ 'migrations',
292
+ 'legacy-codex-native-skill-hashes.json',
293
+ ));
294
+ return (destPath) => {
295
+ const normalized = String(destPath).replace(/\\/g, '/');
296
+ if (normalized.endsWith('/.codex/config.toml') || normalized.endsWith('/.codex/hooks.json')) return true;
297
+ if (!/\/(?:\.codex|\.agents)\/skills\/[^/]+\/SKILL\.md$/.test(normalized)) return false;
298
+ if (!fs.existsSync(destPath)) return false;
299
+ const relativePath = path.relative(targetDir, destPath).replace(/\\/g, '/');
300
+ const digest = hashFile(destPath);
301
+ const previous = String(previousProviderHashes[relativePath] || '').replace(/^sha256:/, '');
302
+ const legacyHashes = legacyRegistry.files[relativePath] || [];
303
+ return digest !== previous && !legacyHashes.includes(digest);
304
+ };
305
+ }
306
+
307
+ async function reconcileLegacyCodexSkills(targetDir, packageRoot) {
308
+ const catalogPath = path.join(packageRoot, '.codex', 'catalog.json');
309
+ if (!await fs.pathExists(catalogPath)) return { removed: 0, migrated: 0, quarantined: 0, ambiguous: [] };
310
+
311
+ const catalog = await fs.readJson(catalogPath);
312
+ const managedIds = [...new Set([
313
+ ...(catalog.expectedSkillIds || []),
314
+ ...(catalog.publicAliasSkillIds || []),
315
+ catalog.genericAgentSkillId,
316
+ ].filter((id) => /^[a-z0-9][a-z0-9-]*$/.test(String(id))))].sort();
317
+ const result = { removed: 0, migrated: 0, quarantined: 0, ambiguous: [] };
318
+
319
+ for (const skillId of managedIds) {
320
+ const legacyPath = path.join(targetDir, '.codex', 'skills', skillId, 'SKILL.md');
321
+ if (!await fs.pathExists(legacyPath)) continue;
322
+ const legacyContent = await readRegularFileNoFollow(legacyPath);
323
+ if (legacyContent === null) {
324
+ result.ambiguous.push(path.relative(targetDir, legacyPath));
325
+ continue;
326
+ }
327
+
328
+ const nativePath = path.join(targetDir, '.agents', 'skills', skillId, 'SKILL.md');
329
+ if (!await fs.pathExists(nativePath)) {
330
+ await fs.ensureDir(path.dirname(nativePath));
331
+ try {
332
+ await fs.writeFile(nativePath, legacyContent, { flag: 'wx' });
333
+ } catch (error) {
334
+ if (error.code === 'EEXIST') {
335
+ result.ambiguous.push(path.relative(targetDir, legacyPath));
336
+ continue;
337
+ }
338
+ throw error;
339
+ }
340
+ await fs.remove(legacyPath);
341
+ if ((await fs.readdir(path.dirname(legacyPath))).length === 0) {
342
+ await fs.remove(path.dirname(legacyPath));
343
+ }
344
+ result.migrated += 1;
345
+ continue;
346
+ }
347
+
348
+ const nativeContent = await readRegularFileNoFollow(nativePath);
349
+ if (nativeContent === null) {
350
+ result.ambiguous.push(path.relative(targetDir, legacyPath));
351
+ continue;
352
+ }
353
+ if (legacyContent.equals(nativeContent)) {
354
+ await fs.remove(legacyPath);
355
+ if ((await fs.readdir(path.dirname(legacyPath))).length === 0) {
356
+ await fs.remove(path.dirname(legacyPath));
357
+ }
358
+ result.removed += 1;
359
+ continue;
360
+ }
361
+
362
+ const digest = crypto.createHash('sha256').update(legacyContent).digest('hex').slice(0, 12);
363
+ const quarantinePath = path.join(
364
+ targetDir,
365
+ '.sinapse-ai',
366
+ 'migrations',
367
+ 'codex-skills',
368
+ `${skillId}.${digest}.legacy.md`,
172
369
  );
173
- if (!fs.existsSync(destPath) && fs.existsSync(compatibilityPath)) return true;
370
+ await fs.ensureDir(path.dirname(quarantinePath));
371
+ try {
372
+ await fs.writeFile(quarantinePath, legacyContent, { flag: 'wx' });
373
+ } catch (error) {
374
+ if (error.code !== 'EEXIST') throw error;
375
+ const quarantinedContent = await readRegularFileNoFollow(quarantinePath);
376
+ if (quarantinedContent === null || !quarantinedContent.equals(legacyContent)) {
377
+ result.ambiguous.push(path.relative(targetDir, legacyPath));
378
+ continue;
379
+ }
380
+ }
381
+ await fs.remove(legacyPath);
382
+ if ((await fs.readdir(path.dirname(legacyPath))).length === 0) {
383
+ await fs.remove(path.dirname(legacyPath));
384
+ }
385
+ result.quarantined += 1;
386
+ }
387
+
388
+ return result;
389
+ }
390
+
391
+ async function reconcileLegacyClaudeAgentCommands(targetDir, packageRoot = path.join(__dirname, '..', '..', '..', '..')) {
392
+ const legacyDir = path.join(targetDir, '.claude', 'commands', 'SINAPSE', 'agents');
393
+ const result = { removed: 0, preserved: 0, ambiguous: [] };
394
+ if (!await fs.pathExists(legacyDir)) return result;
395
+
396
+ const registryPath = path.join(
397
+ packageRoot,
398
+ 'packages',
399
+ 'installer',
400
+ 'src',
401
+ 'migrations',
402
+ 'legacy-claude-agent-command-hashes.json',
403
+ );
404
+ const registry = await fs.readJson(registryPath);
405
+ for (const entry of await fs.readdir(legacyDir, { withFileTypes: true })) {
406
+ const filePath = path.join(legacyDir, entry.name);
407
+ if (!entry.isFile()) {
408
+ result.preserved += 1;
409
+ result.ambiguous.push(path.relative(targetDir, filePath));
410
+ continue;
411
+ }
412
+ const content = await fs.readFile(filePath);
413
+ const digest = crypto.createHash('sha256').update(content).digest('hex');
414
+ const knownHashes = registry.files[entry.name] || [];
415
+ const isMarked = content.includes(Buffer.from('SINAPSE-MANAGED:claude-command'));
416
+ if (knownHashes.includes(digest) || isMarked) {
417
+ await fs.remove(filePath);
418
+ result.removed += 1;
419
+ } else {
420
+ result.preserved += 1;
421
+ result.ambiguous.push(path.relative(targetDir, filePath));
422
+ }
174
423
  }
175
- return /\/(?:\.codex|\.agents)\/skills\/[^/]+\/SKILL\.md$/.test(norm);
424
+ if ((await fs.readdir(legacyDir)).length === 0) await fs.remove(legacyDir);
425
+ return result;
176
426
  }
177
427
 
178
428
  async function copyFileWithRootReplacement(sourcePath, destPath, replaceRoot = true, preserveExisting = null) {
@@ -350,6 +600,15 @@ async function installSinapseCore(options = {}) {
350
600
  try {
351
601
  const sourceDir = getSinapseCoreSourcePath();
352
602
  const targetSinapseCore = path.join(targetDir, '.sinapse-ai');
603
+ const previousVersionPath = path.join(targetSinapseCore, 'version.json');
604
+ let previousProviderHashes = {};
605
+ if (await fs.pathExists(previousVersionPath)) {
606
+ try {
607
+ previousProviderHashes = (await fs.readJson(previousVersionPath)).providerFileHashes || {};
608
+ } catch (_error) {
609
+ previousProviderHashes = {};
610
+ }
611
+ }
353
612
 
354
613
  // Check if source exists
355
614
  if (!await fs.pathExists(sourceDir)) {
@@ -415,7 +674,7 @@ async function installSinapseCore(options = {}) {
415
674
  if (await fs.pathExists(src)) {
416
675
  spinner.text = `Copying ${dirName}...`;
417
676
  const preserveExisting = dirName === '.codex' || dirName === '.agents'
418
- ? isUserOwnedCodexConfig
677
+ ? createCodexPreservePredicate(targetDir, pkgRoot, previousProviderHashes)
419
678
  : null;
420
679
  const copied = await copyDirectoryWithRootReplacement(
421
680
  src,
@@ -431,6 +690,7 @@ async function installSinapseCore(options = {}) {
431
690
  }
432
691
 
433
692
  if (includeClaude) {
693
+ result.claudeLegacyAgentCommandReconciliation = await reconcileLegacyClaudeAgentCommands(targetDir, pkgRoot);
434
694
  const claude = await deliverClaudeNativeAdapters(pkgRoot, targetDir, { overwriteManaged: overwriteManagedAdapters });
435
695
  result.claudeNativeAgentFiles = claude.agents.length;
436
696
  result.claudeNativeSkillFiles = claude.skills.length;
@@ -441,6 +701,8 @@ async function installSinapseCore(options = {}) {
441
701
  if (await fs.pathExists(claudeContextSource) && await copyFileWithRootReplacement(claudeContextSource, claudeContextDest, true)) {
442
702
  result.installedFiles.push(path.join('.claude', 'CLAUDE.md'));
443
703
  }
704
+ const claudeSettings = await reconcileClaudeHookSettings(pkgRoot, targetDir);
705
+ if (claudeSettings) result.installedFiles.push(claudeSettings);
444
706
  }
445
707
 
446
708
  // AGENTS.md is the Codex CLI's default context file (Imperator greeting +
@@ -455,6 +717,7 @@ async function installSinapseCore(options = {}) {
455
717
  result.installedFiles.push('AGENTS.md');
456
718
  result.agentsMdInstalled = true;
457
719
  }
720
+ result.codexLegacySkillReconciliation = await reconcileLegacyCodexSkills(targetDir, pkgRoot);
458
721
  }
459
722
 
460
723
  // Create install manifest
@@ -481,6 +744,9 @@ async function installSinapseCore(options = {}) {
481
744
  installedFiles: result.installedFiles,
482
745
  mode: 'project-development',
483
746
  providers: [includeClaude && 'claude-code', includeCodex && 'codex'].filter(Boolean),
747
+ targetDir,
748
+ providerFiles: includeCodex ? getManagedCodexSkillPaths(pkgRoot) : [],
749
+ providerSourceRoot: pkgRoot,
484
750
  });
485
751
  result.versionInfo = versionInfo;
486
752
 
@@ -602,10 +868,16 @@ module.exports = {
602
868
  copyFileWithRootReplacement,
603
869
  copyDirectoryWithRootReplacement,
604
870
  isUserOwnedL3,
871
+ createCodexPreservePredicate,
872
+ getManagedCodexSkillPaths,
873
+ reconcileLegacyCodexSkills,
874
+ reconcileLegacyClaudeAgentCommands,
605
875
  generateVersionJson,
606
876
  generateFileHashes,
607
877
  deliverCodexRootFiles,
608
878
  deliverClaudeNativeAdapters,
879
+ getClaudeHookName,
880
+ reconcileClaudeHookSettings,
609
881
  FOLDERS_TO_COPY,
610
882
  ROOT_FILES_TO_COPY,
611
883
  CODEX_ROOT_FILES,
@@ -0,0 +1,36 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "algorithm": "sha256",
4
+ "files": {
5
+ "analyst.md": ["795b9209e7bc5bd31a736bb67ee5c0eeb1a95590afd47fb630c81de0dd87c126"],
6
+ "animations-orqx.md": ["f3cce5c6d75a6a872171593dba8bafeae657488950fad63c89f17e7e193e92e0"],
7
+ "architect.md": ["d2e8a9b7b4c7582870f08561f482fa6c51a0b347fdcc066bd88bda5575a2052f"],
8
+ "brand-orqx.md": ["a4afdbd9dc8fd118939be2e07377967a86748310b89c7f59b14818d84fbb10ad"],
9
+ "cloning-orqx.md": ["134d10224635b9898469d7bb3992f636ecdba375272e3ad1c24eb0215c1a85c0"],
10
+ "commercial-orqx.md": ["170288942d7c85c28fece5eef0256101eee0699a76a8f28b3e7ec5878d2f0b79"],
11
+ "content-orqx.md": ["f93bf397c8da352d10b884600ae339979a0c6c5190fb3b21daa1de6d3e7a2e45"],
12
+ "copy-orqx.md": ["86f70db676af7475d13face6468da78ee05e5c257b506fe484b62c0d1ca9439c"],
13
+ "council-orqx.md": ["d2ab7fcd71e71677d6fd15ae1565c35fb241e4b9558e79da0e030fc70a81efe4"],
14
+ "courses-orqx.md": ["236e69533c0155a42dfc762f563455244b71f127ae6459b70ffabad8ba081654"],
15
+ "cyber-orqx.md": ["8efff68f6e1441df8719b3d743dc27f9e6d0ce6d9f6e7128284a144d5c74ac63"],
16
+ "data-engineer.md": ["9c9faba56ad0bb8816e76117e0392e0727d8f4aba46413fa053c97d5aaf086bf"],
17
+ "design-orqx.md": ["e3b10fb943a64916826a11d13e0e57f61cc5e27e8966ea15924cae4e055aad64"],
18
+ "developer.md": ["b996b501f752b4f049367182d0c2c5a99019fddeab343c840a7da2f8ca1d70a4"],
19
+ "devops.md": ["18354e8a0e8b39786d48a5d9f8a7e532698e6fe49a65f3243be6a96eb6c73c42"],
20
+ "finance-orqx.md": ["f4ebeb70b7a0890c491608a00feedd31b9b4a9b872ed02d489b39a99fa01c537"],
21
+ "growth-orqx.md": ["b0f74d98f40fe384c81a22ab705007e4e40ab9f82bc6c23d2282a05aed17cbdf"],
22
+ "paidmedia-orqx.md": ["b68b91bc647fa6290c9109d621d98f6567b28967acb9e474cbf399442711c530"],
23
+ "product-lead.md": ["a62d2f6079b7deabe22d24cd66943375e82fbe9ea389e23bf9a7a9ea2fa55e08"],
24
+ "product-orqx.md": ["5df3d89096efdc1cabdcfaaf82b14571689d258a495981520ae0a0ea8fa04a92"],
25
+ "project-lead.md": ["0dd16d04f28c7614c025dadb557aa4b3eeb56c96fda8874afec40b8044db6e2b"],
26
+ "quality-gate.md": ["ed735180b28c51df2803933009f9b9bc6f5f497ac2b528919a4eed83e0ba1682"],
27
+ "research-orqx.md": ["9cf3c189b670ee4565329abec1a2c7d208a3013a9dba695208cbe617b4b0984b"],
28
+ "sinapse-orqx.md": ["36e704406c594a72cbf72eac52bb9ce22a636965a8c86a45c6560e4cc4fb67d6"],
29
+ "snps-orqx.md": ["0452d5291fd27239c0e6f79064d214c9755234e444daabbcea7cdd5e0f5a544d"],
30
+ "sprint-lead.md": ["3abfcccefb0e15765549cc54fabb8e398de135532c76f9ef8b16a57220dcac69"],
31
+ "squad-creator.md": ["1a92aeabebee66782dd906a75844451d3cb0fc0af59f7df266ab4106026ef657"],
32
+ "storytelling-orqx.md": ["45889421752caaecaff3ccc92d22e333235d675f65710f435b3b7224bbc0e022"],
33
+ "swarm-orqx.md": ["9eb519069dc7b9a10fd2ca677f4dfbed9ad3f966d0d65b2a73daf67b000b81c0"],
34
+ "ux-design-expert.md": ["f26f6935b95bd3c5d051a0b0a70394571f47fb1992e26ad17b25033b04f81048"]
35
+ }
36
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "algorithm": "sha256",
4
+ "files": {
5
+ ".agents/skills/sinapse-agent/SKILL.md": ["122ddfefe2fc23579c54513953f2f8cb7e872708c3c2078aac1a72a5e8a55959"],
6
+ ".agents/skills/sinapse-analyst/SKILL.md": ["3ff8b42481e23e847ebac3c255ff4437b984c715046e16fb61d0605fa1a5b677"],
7
+ ".agents/skills/sinapse-animations/SKILL.md": ["78666bb951ea083f1df13d9960ed7cecaef304024cea4392b2fc3022251fdab5"],
8
+ ".agents/skills/sinapse-architect/SKILL.md": ["05bbbaf7d493918cf4856feddd611b8f2e1f88924e7bba0f6ab9ce83ab7b6718"],
9
+ ".agents/skills/sinapse-brand/SKILL.md": ["bfbdebaa0e3c9e2668025af01df88841646ea450e4b1e6cb5008ca02258206db"],
10
+ ".agents/skills/sinapse-claude/SKILL.md": ["85f25d07838fa259cf48338ab526eeb6391c47ddbf10dba44aa6cc35421a90bf"],
11
+ ".agents/skills/sinapse-cloning/SKILL.md": ["b9e6d9e6c87cb79f261c46b6f6e83fc1d10506bcb920ece4012b07020334dc2a"],
12
+ ".agents/skills/sinapse-commercial/SKILL.md": ["b26cb18661293ffee078bd555677b7fa201718f1cdedf859dd358ae33b901e1e"],
13
+ ".agents/skills/sinapse-content/SKILL.md": ["61f4cbb9b9a5de4dde5fbf7b3027898be20fb127a287b19d5955e34a832b3ba3"],
14
+ ".agents/skills/sinapse-copy/SKILL.md": ["dddf3da31fe8807eb60450ad0dcdd73288ba32470b142d4d1c994277282dd1a2"],
15
+ ".agents/skills/sinapse-council/SKILL.md": ["6692021c2578524513a5462b7271aa7eda7335961cea5d9d015f7b75f3dfd2a1"],
16
+ ".agents/skills/sinapse-courses/SKILL.md": ["653a21c66dd78904b53b8924601778f1682db8b879ab5d50320d06452236687d"],
17
+ ".agents/skills/sinapse-cyber/SKILL.md": ["55c12f9a6f35970b3bf0d6d6ab8208b6348a9390040df1d8d01df108a369b429"],
18
+ ".agents/skills/sinapse-data-engineer/SKILL.md": ["5c8e31f9cba46f89fffbb5c83fe831725d951f75275b085b51ea48beab0291fa"],
19
+ ".agents/skills/sinapse-design/SKILL.md": ["3c35efa3e905d636352f5f046d494665ebf9e33f4c896fddcfa112103877a00d"],
20
+ ".agents/skills/sinapse-dev/SKILL.md": ["a2ff86c33e61efb1085e9388c8f1f6a58389b9dc8b74ecbe83c096b920f114db"],
21
+ ".agents/skills/sinapse-devops/SKILL.md": ["9cf5ef12999568e292f4e5d302bd61831872a783d6ed9caaca7a1ac247c63c01"],
22
+ ".agents/skills/sinapse-finance/SKILL.md": ["4b596578bbb950e8513eb96ea62200880c9184989bdfc91c7ecd1370f47b07ab"],
23
+ ".agents/skills/sinapse-growth/SKILL.md": ["53d32adb439098528925425d4ff86458e8fb2efb3c8fd6b62e8c42de4f06873c"],
24
+ ".agents/skills/sinapse-loop/SKILL.md": ["e5a33f9235c66e554e67d7668ac5e3255e1b6f9ed026f7bf3be666c169bf396a"],
25
+ ".agents/skills/sinapse-orqx/SKILL.md": ["b522b867768f4f6cf9151978734611b1482ec6f35cbe8552e9e324c4bb16de50"],
26
+ ".agents/skills/sinapse-paidmedia/SKILL.md": ["81ae19dae135dcd001703446131989ac51da1a299d8fab59ddc21596b1d462e2"],
27
+ ".agents/skills/sinapse-pm/SKILL.md": ["ecf406f3a94c48defff97c9ef55410cdf82cfa445b3d1244df0c6dd66543b910"],
28
+ ".agents/skills/sinapse-po/SKILL.md": ["56693d11a13cf51eb600883d26f232adfb6c0275015a1dfa0fd467387700667f"],
29
+ ".agents/skills/sinapse-product/SKILL.md": ["50bae9a40dca95287b864a873fe578953b5f361239a3e6c2b70ae2cd38197eb7"],
30
+ ".agents/skills/sinapse-qa/SKILL.md": ["54ac75619ae2cd28fa820e224abc5fc82163198652ce200d0706aa0734e98509"],
31
+ ".agents/skills/sinapse-research/SKILL.md": ["ac98bc465c1c975d95d93ffc6a665c41ab679e1885882ab7a91c86501a34d181"],
32
+ ".agents/skills/sinapse-sm/SKILL.md": ["408556e7cff08d5b84c724e55a3d35e7624e155984edfe697c7a9fe61e60a72f"],
33
+ ".agents/skills/sinapse-spec-driven/SKILL.md": ["5216b2f651f88263323a07cabec78259036bb4ec23689d312c770cef216b26a1"],
34
+ ".agents/skills/sinapse-squad-creator/SKILL.md": ["779a93754fb121d8f405667f2d3852faeb8945302129aec74c0d1f6b51f62ed3"],
35
+ ".agents/skills/sinapse-storytelling/SKILL.md": ["65e2d8719eee6d0221bcfe758144c24575cf00c0aa988b785f98c69fc4982025"],
36
+ ".agents/skills/sinapse-swarm/SKILL.md": ["8c98e9b1da2cb279ef7fa10ca4e81cb0a230ecdbfda4f890b0b94f1604626f00"],
37
+ ".agents/skills/sinapse-ux-design-expert/SKILL.md": ["5a1c747c030113b50d98a431bfffc4418e38355cae72f7e5a088ee5bee8bc530"],
38
+ ".agents/skills/sinapse/SKILL.md": ["fe017c091d1308eb937762ffe25f83518f778a266303cc3f028d0f468df0747f"],
39
+ ".agents/skills/snps-orqx/SKILL.md": ["24f66470a5721fc086a52eca9907958d091d68c2de0a0b56a42e07e83fe00342"],
40
+ ".agents/skills/snps/SKILL.md": ["9f9a0972a184a07234948f4e5dd8f7032835be3283608a00c8cbf8bbf68064a3"]
41
+ }
42
+ }
@@ -26,6 +26,24 @@ const { PostInstallValidator, formatReport: formatValidationReport } = require('
26
26
  const { _testing: { installGlobalAgents } } = require('../wizard/index');
27
27
  const { installSinapseCore } = require('../installer/sinapse-ai-installer');
28
28
 
29
+ function detectInstalledProviders(projectRoot, recordedProviders = []) {
30
+ if (recordedProviders.length > 0) {
31
+ return {
32
+ includeClaude: recordedProviders.includes('claude-code'),
33
+ includeCodex: recordedProviders.includes('codex'),
34
+ };
35
+ }
36
+ const includeClaude = fs.existsSync(path.join(projectRoot, '.claude', 'skill-manifest.json'));
37
+ const hasCodexCatalog = fs.existsSync(path.join(projectRoot, '.codex', 'catalog.json'));
38
+ const hasNativeActivator = fs.existsSync(
39
+ path.join(projectRoot, '.agents', 'skills', 'sinapse-agent', 'SKILL.md'),
40
+ );
41
+ const hasLegacyActivator = fs.existsSync(
42
+ path.join(projectRoot, '.codex', 'skills', 'sinapse-agent', 'SKILL.md'),
43
+ );
44
+ return { includeClaude, includeCodex: hasCodexCatalog && (hasNativeActivator || hasLegacyActivator) };
45
+ }
46
+
29
47
  /**
30
48
  * Update status types
31
49
  * @enum {string}
@@ -614,13 +632,10 @@ class SINAPSEUpdater {
614
632
  const recordedProviders = Array.isArray(this.versionInfo?.providers)
615
633
  ? this.versionInfo.providers
616
634
  : [];
617
- const includeClaude = recordedProviders.length > 0
618
- ? recordedProviders.includes('claude-code')
619
- : fs.existsSync(path.join(this.projectRoot, '.claude', 'skill-manifest.json'));
620
- const includeCodex = recordedProviders.length > 0
621
- ? recordedProviders.includes('codex')
622
- : fs.existsSync(path.join(this.projectRoot, '.codex', 'catalog.json'))
623
- && fs.existsSync(path.join(this.projectRoot, '.agents', 'skills', 'sinapse-agent', 'SKILL.md'));
635
+ const { includeClaude, includeCodex } = detectInstalledProviders(
636
+ this.projectRoot,
637
+ recordedProviders,
638
+ );
624
639
  const delivery = await installSinapseCore({
625
640
  targetDir: this.projectRoot,
626
641
  includeClaude,
@@ -631,6 +646,7 @@ class SINAPSEUpdater {
631
646
 
632
647
  result.success = true;
633
648
  result.filesUpdated = 1 + delivery.installedFiles.length;
649
+ result.codexLegacySkillReconciliation = delivery.codexLegacySkillReconciliation || null;
634
650
 
635
651
  return result;
636
652
  } catch (error) {
@@ -942,4 +958,5 @@ module.exports = {
942
958
  formatCheckResult,
943
959
  formatUpdateResult,
944
960
  sanitizeLogText,
961
+ detectInstalledProviders,
945
962
  };
@@ -18,6 +18,7 @@ const crossSpawn = require('cross-spawn');
18
18
  const { getIDEConfig } = require('../config/ide-configs');
19
19
  const { validateProjectName } = require('./validators');
20
20
  const { getMergeStrategy, hasMergeStrategy } = require('../merger/index.js');
21
+ const { reconcileLegacyClaudeAgentCommands } = require('../installer/sinapse-ai-installer');
21
22
 
22
23
  /**
23
24
  * Render template with variables
@@ -373,6 +374,9 @@ async function generateIDEConfigs(selectedIDEs, wizardState, options = {}) {
373
374
 
374
375
  // Copy agent files to IDE-specific agent folder
375
376
  if (ide.agentFolder) {
377
+ if (ideKey === 'claude-code') {
378
+ await reconcileLegacyClaudeAgentCommands(projectRoot);
379
+ }
376
380
  spinner.start(`Copying agents to ${ide.agentFolder}...`);
377
381
  const agentFiles = await copyAgentFiles(projectRoot, ide.agentFolder, ide);
378
382
  createdFiles.push(...agentFiles);
@@ -536,6 +536,10 @@ async function runWizard(options = {}) {
536
536
  const installed = pillars.filter(p => sinapseCoreResult.installedFolders.includes(p));
537
537
  const aux = sinapseCoreResult.installedFolders.filter(f => !pillars.includes(f));
538
538
  console.log(`✅ SINAPSE core: ${sinapseCoreResult.installedFiles.length} files (${installed.length} pillars, ${aux.length} modules)`);
539
+ const cleanup = sinapseCoreResult.codexLegacySkillReconciliation;
540
+ if (cleanup && (cleanup.removed || cleanup.migrated || cleanup.quarantined || cleanup.ambiguous.length)) {
541
+ console.log(`SINAPSE Codex skill cleanup: ${cleanup.removed} removed, ${cleanup.migrated} migrated, ${cleanup.quarantined} quarantined, ${cleanup.ambiguous.length} ambiguous`);
542
+ }
539
543
  }
540
544
  answers.sinapseCoreInstalled = true;
541
545
  answers.sinapseCoreResult = sinapseCoreResult;
@@ -806,7 +810,7 @@ async function runWizard(options = {}) {
806
810
  }
807
811
 
808
812
  // Codex local-first sync: the ide-sync above drives claude-code only now,
809
- // so .codex/agents + .codex/skills are regenerated here from the canonical
813
+ // so .codex/agents + .agents/skills are regenerated here from the canonical
810
814
  // source (the static .codex payload — scripts/tasks/JSONs — was already
811
815
  // copied by installSinapseCore when Codex was selected). Codex installs only.
812
816
  if (shouldConfigureCodex(selectedLLM)) {
@@ -822,7 +826,7 @@ async function runWizard(options = {}) {
822
826
  : { total: 0 };
823
827
  answers.codexSyncStatus = codexResult.ok ? 'synced' : 'failed';
824
828
  const m = codexResult.metrics || {};
825
- console.log(`✅ Codex sync: ${codexResult.ok ? 'synced' : 'issues'} (${m.codexAgents || 0} adapters, ${nativeResult.total || 0} native agents, ${m.codexSkills || 0} compatibility skills)`);
829
+ console.log(`✅ Codex sync: ${codexResult.ok ? 'synced' : 'issues'} (${m.codexAgents || 0} adapters, ${nativeResult.total || 0} native agents, ${m.codexSkills || 0} native skills)`);
826
830
  } catch (codexErr) {
827
831
  console.warn(`⚠️ Codex sync failed: ${codexErr.message} — run 'npm run sync:ide:codex' post-install`);
828
832
  answers.codexSyncStatus = 'failed';