sinapse-ai 1.25.0 → 1.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) 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 -16
  10. package/bin/cli.js +8 -1
  11. package/bin/commands/help.js +3 -2
  12. package/bin/commands/install.js +25 -14
  13. package/bin/commands/status.js +4 -3
  14. package/bin/commands/uninstall.js +216 -51
  15. package/bin/commands/update.js +8 -4
  16. package/bin/lib/global-provider-adapters.js +73 -10
  17. package/bin/lib/provider-parity.js +22 -0
  18. package/bin/postinstall.js +11 -11
  19. package/docs/framework/codex-parity/codex-integration-process.md +6 -7
  20. package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
  21. package/docs/framework/codex-parity/codex-parity-program.md +4 -4
  22. package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
  23. package/docs/guides/codex-config.md +2 -2
  24. package/docs/guides/development-setup.md +1 -2
  25. package/docs/guides/ide-integration.md +6 -6
  26. package/docs/guides/quality-gates.md +2 -2
  27. package/docs/ide-integration.md +2 -2
  28. package/docs/installation/faq.md +1 -1
  29. package/docs/installation/troubleshooting.md +2 -2
  30. package/docs/installation/v4-quick-start.md +1 -1
  31. package/docs/pt/contributing.md +1 -1
  32. package/docs/pt/guides/development-setup.md +1 -2
  33. package/docs/pt/guides/quality-gates.md +2 -2
  34. package/docs/pt/ide-integration.md +6 -6
  35. package/docs/pt/installation/faq.md +1 -1
  36. package/docs/pt/installation/troubleshooting.md +2 -3
  37. package/docs/pt/installation/v4-quick-start.md +1 -2
  38. package/docs/pt/platforms/claude-code.md +4 -4
  39. package/docs/sinapse-agent-flows/analyst-system.md +1 -1
  40. package/docs/sinapse-agent-flows/architect-system.md +1 -1
  41. package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
  42. package/docs/sinapse-agent-flows/dev-system.md +1 -1
  43. package/docs/sinapse-agent-flows/pm-system.md +1 -1
  44. package/docs/sinapse-agent-flows/qa-system.md +1 -1
  45. package/docs/sinapse-agent-flows/sm-system.md +1 -1
  46. package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
  47. package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
  48. package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
  49. package/package.json +21 -16
  50. package/packages/installer/src/config/ide-configs.js +1 -1
  51. package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
  52. package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
  53. package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
  54. package/packages/installer/src/updater/index.js +24 -7
  55. package/packages/installer/src/wizard/ide-config-generator.js +4 -0
  56. package/packages/installer/src/wizard/index.js +6 -2
  57. package/scripts/validate-codex-command-registry.js +101 -0
  58. package/scripts/validate-provider-adapters.js +117 -1
  59. package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
  60. package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
  61. package/squads/squad-design/agents/ux-designer.md +1 -1
  62. package/.codex/skills/sinapse/SKILL.md +0 -10
  63. package/.codex/skills/sinapse-agent/SKILL.md +0 -22
  64. package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
  65. package/.codex/skills/sinapse-animations/SKILL.md +0 -28
  66. package/.codex/skills/sinapse-architect/SKILL.md +0 -31
  67. package/.codex/skills/sinapse-brand/SKILL.md +0 -28
  68. package/.codex/skills/sinapse-claude/SKILL.md +0 -28
  69. package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
  70. package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
  71. package/.codex/skills/sinapse-content/SKILL.md +0 -28
  72. package/.codex/skills/sinapse-copy/SKILL.md +0 -28
  73. package/.codex/skills/sinapse-council/SKILL.md +0 -28
  74. package/.codex/skills/sinapse-courses/SKILL.md +0 -28
  75. package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
  76. package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
  77. package/.codex/skills/sinapse-design/SKILL.md +0 -28
  78. package/.codex/skills/sinapse-dev/SKILL.md +0 -31
  79. package/.codex/skills/sinapse-devops/SKILL.md +0 -31
  80. package/.codex/skills/sinapse-finance/SKILL.md +0 -28
  81. package/.codex/skills/sinapse-growth/SKILL.md +0 -28
  82. package/.codex/skills/sinapse-loop/SKILL.md +0 -35
  83. package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
  84. package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
  85. package/.codex/skills/sinapse-pm/SKILL.md +0 -31
  86. package/.codex/skills/sinapse-po/SKILL.md +0 -31
  87. package/.codex/skills/sinapse-product/SKILL.md +0 -28
  88. package/.codex/skills/sinapse-qa/SKILL.md +0 -31
  89. package/.codex/skills/sinapse-research/SKILL.md +0 -28
  90. package/.codex/skills/sinapse-sm/SKILL.md +0 -27
  91. package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
  92. package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
  93. package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
  94. package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
  95. package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
  96. package/.codex/skills/snps/SKILL.md +0 -10
  97. package/.codex/skills/snps-orqx/SKILL.md +0 -10
  98. /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
  99. /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
  100. /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
@@ -554,7 +554,7 @@ nvm use 16
554
554
 
555
555
  ```bash
556
556
  # Claude Code
557
- ls .claude/commands/SINAPSE/agents/
557
+ ls .claude/agents/
558
558
 
559
559
  # Cursor
560
560
  ls .cursor/rules/
@@ -576,7 +576,7 @@ nvm use 16
576
576
 
577
577
  1. Verifique se a versão da IDE é compatível
578
578
  2. Para Cursor: Certifique-se que os arquivos têm extensão `.mdc`
579
- 3. Para Claude Code: Arquivos devem estar em `.claude/commands/`
579
+ 3. Para Claude Code: Agentes nativos devem estar em `.claude/agents/`; `.claude/commands/` serve apenas para slash commands
580
580
  4. Reinicie a IDE após a instalação
581
581
 
582
582
  ---
@@ -733,4 +733,3 @@ ls -la .sinapse-ai/
733
733
  ## Documentação Relacionada
734
734
 
735
735
  - [FAQ](./faq.md)
736
-
@@ -200,7 +200,7 @@ seu-projeto/
200
200
  │ ├── integrations/ # Adaptadores de PM tools
201
201
  │ └── templates/ # Templates de configuração
202
202
  ├── .claude/ # Configuração Claude Code
203
- │ ├── commands/SINAPSE/agents/ # Skills de agentes
203
+ │ ├── agents/ # Agentes nativos do Claude
204
204
  │ └── rules/ # Regras de agentes
205
205
  ├── docs/ # Documentação
206
206
  │ └── stories/ # Stories de desenvolvimento
@@ -267,4 +267,3 @@ git clone https://github.com/caioimori/sinapse-ai.git
267
267
  ---
268
268
 
269
269
  **Bem-vindo ao SINAPSE! Bom coding!**
270
-
@@ -403,7 +403,7 @@ O Claude Code mostra status em tempo real:
403
403
  flowchart LR
404
404
  Source["SINAPSE Core<br/>.sinapse-ai/development/agents"] --> Parser["Parser de Agente"]
405
405
  Parser --> Transform["Transformador Claude"]
406
- Transform --> Output[".claude/commands/SINAPSE/agents"]
406
+ Transform --> Output[".claude/agents"]
407
407
  ```
408
408
 
409
409
  ### Comandos de Sincronização
@@ -450,7 +450,7 @@ activation: /dev
450
450
 
451
451
  Quando conflitos ocorrem:
452
452
 
453
- 1. Alterações locais são salvas em `.claude/commands/SINAPSE/agents/.backup/`
453
+ 1. Alterações locais são salvas em `.claude/agents/.backup/`
454
454
  2. Sync solicita resolução
455
455
  3. Escolha: manter local, usar remoto ou mesclar
456
456
 
@@ -524,7 +524,7 @@ Error: Unknown command '/xyz'
524
524
  npm run sync:ide
525
525
 
526
526
  # Verificar se o agente existe
527
- ls .claude/commands/SINAPSE/agents/
527
+ ls .claude/agents/
528
528
  ```
529
529
 
530
530
  #### Permissão de Ferramenta Negada
@@ -588,7 +588,7 @@ R: Simplesmente use o comando slash para o agente desejado: `/dev`, `/qa`, etc.
588
588
  R: Sim, através do Task tool que pode criar subagentes.
589
589
 
590
590
  **P: Onde as definições de agentes são armazenadas?**
591
- R: Em `.claude/commands/SINAPSE/agents/` após a sincronização.
591
+ R: Em `.claude/agents/` após a sincronização.
592
592
 
593
593
  **P: Com que frequência devo sincronizar agentes?**
594
594
  R: Após atualizar o SINAPSE ou quando agentes são modificados no core.
@@ -56,7 +56,7 @@ O agente Analyst e projetado para:
56
56
  | Arquivo | Propósito |
57
57
  |---------|-----------|
58
58
  | `.sinapse-ai/development/agents/analyst.md` | Definição core do agente Analyst |
59
- | `.claude/commands/SINAPSE/agents/analyst.md` | Comando Claude Code para ativar @analyst |
59
+ | `.claude/agents/analyst.md` | Comando Claude Code para ativar @analyst |
60
60
 
61
61
  ### Tasks do Analyst
62
62
 
@@ -56,7 +56,7 @@ O agente **@architect** (Aria) e o **Holistic System Architect & Full-Stack Tech
56
56
  | Arquivo | Propósito |
57
57
  |---------|-----------|
58
58
  | `.sinapse-ai/development/agents/architect.md` | Definição core do agente Architect |
59
- | `.claude/commands/SINAPSE/agents/architect.md` | Comando Claude Code para ativar @architect |
59
+ | `.claude/agents/architect.md` | Comando Claude Code para ativar @architect |
60
60
 
61
61
  ### Arquivos de Templates do @architect
62
62
 
@@ -89,7 +89,7 @@ O agente **@data-engineer (Dara)** e o Database Architect & Operations Engineer
89
89
  | Arquivo | Propósito |
90
90
  |---------|-----------|
91
91
  | `.sinapse-ai/development/agents/data-engineer.md` | Definição core do agente @data-engineer (persona, comandos, workflows) |
92
- | `.claude/commands/SINAPSE/agents/data-engineer.md` | Comando Claude Code para ativar @data-engineer |
92
+ | `.claude/agents/data-engineer.md` | Comando Claude Code para ativar @data-engineer |
93
93
 
94
94
  ### Arquivos de Templates SQL
95
95
 
@@ -55,7 +55,7 @@ O agente **@developer (Dex)** e o Full Stack Developer do SINAPSE, responsavel p
55
55
  | Arquivo | Propósito |
56
56
  |---------|-----------|
57
57
  | `.sinapse-ai/development/agents/developer.md` | Definição core do agente @developer (persona, comandos, workflows) |
58
- | `.claude/commands/SINAPSE/agents/developer.md` | Comando Claude Code para ativar @developer |
58
+ | `.claude/agents/developer.md` | Comando Claude Code para ativar @developer |
59
59
 
60
60
  ### Arquivos de Checklists Usados pelo @dev
61
61
 
@@ -41,7 +41,7 @@ O agente PM e projetado para:
41
41
  | Arquivo | Propósito |
42
42
  |---------|-----------|
43
43
  | `.sinapse-ai/development/agents/project-lead.md` | Definição core do agente PM |
44
- | `.claude/commands/SINAPSE/agents/project-lead.md` | Comando Claude Code para ativar @project-lead |
44
+ | `.claude/agents/project-lead.md` | Comando Claude Code para ativar @project-lead |
45
45
 
46
46
  ### Tasks do @pm
47
47
 
@@ -64,7 +64,7 @@ O agente **@quality-gate (Quinn)** e o Test Architect & Quality Advisor do SINAP
64
64
  | Arquivo | Propósito |
65
65
  |---------|-----------|
66
66
  | `.sinapse-ai/development/agents/quality-gate.md` | Definição completa do agente QA |
67
- | `.claude/commands/SINAPSE/agents/quality-gate.md` | Comando Claude Code para ativar @quality-gate |
67
+ | `.claude/agents/quality-gate.md` | Comando Claude Code para ativar @quality-gate |
68
68
 
69
69
  ### Arquivos de Workflow
70
70
 
@@ -43,7 +43,7 @@ O agente **@sprint-lead (River)** e o Scrum Master tecnico do SINAPSE, especiali
43
43
  | Arquivo | Propósito |
44
44
  |---------|-----------|
45
45
  | `.sinapse-ai/development/agents/sprint-lead.md` | Definição core do agente SM |
46
- | `.claude/commands/SINAPSE/agents/sprint-lead.md` | Comando Claude Code para ativar @sprint-lead |
46
+ | `.claude/agents/sprint-lead.md` | Comando Claude Code para ativar @sprint-lead |
47
47
  | `.cursor/rules/sm.md` | Regras para Cursor IDE |
48
48
  | `.cursor/rules/sm.mdc` | Regras compiladas para Cursor |
49
49
 
@@ -60,7 +60,7 @@ O **@sinapse-orqx** (Orion - The Orchestrator) e o meta-agente principal do fram
60
60
  | Arquivo | Propósito |
61
61
  |---------|-----------|
62
62
  | `.sinapse-ai/development/agents/sinapse-orqx.md` | Definição completa do agente (persona, comandos, dependencias) |
63
- | `.claude/commands/SINAPSE/agents/sinapse-orqx.md` | Comando Claude Code para ativar @sinapse-orqx |
63
+ | `.claude/agents/sinapse-orqx.md` | Comando Claude Code para ativar @sinapse-orqx |
64
64
 
65
65
  ### Arquivos de Templates do @sinapse-orqx
66
66
 
@@ -39,7 +39,7 @@ Este sistema implementa a **arquitetura task-first** do SINAPSE, onde tasks sao
39
39
  | Arquivo | Propósito |
40
40
  |---------|-----------|
41
41
  | `.sinapse-ai/development/agents/squad-creator.md` | Definição core do agente Squad Creator |
42
- | `.claude/commands/SINAPSE/agents/squad-creator.md` | Comando Claude Code para ativar @squad-creator |
42
+ | `.claude/agents/squad-creator.md` | Comando Claude Code para ativar @squad-creator |
43
43
 
44
44
  ### Arquivos de Tasks do @squad-creator
45
45
 
@@ -668,7 +668,7 @@ flowchart LR
668
668
  - [Script: squad-validator.js](../../.sinapse-ai/development/scripts/squad/squad-validator.js)
669
669
  - [Schema: squad-schema.json](../../.sinapse-ai/schemas/squad-schema.json)
670
670
  - [Agent: squad-creator.md](../../.sinapse-ai/development/agents/squad-creator.md)
671
- - [Command: squad-creator.md](../../.claude/commands/SINAPSE/agents/squad-creator.md)
671
+ - [Command: squad-creator.md](../../.claude/agents/squad-creator.md)
672
672
 
673
673
  ---
674
674
 
@@ -88,7 +88,7 @@ flowchart TB
88
88
  | Arquivo | Propósito |
89
89
  |---------|-----------|
90
90
  | `.sinapse-ai/development/agents/ux-design-expert.md` | Definição completa do agente (persona, comandos, workflows) |
91
- | `.claude/commands/SINAPSE/agents/ux-design-expert.md` | Comando Claude Code para ativar @ux-design-expert |
91
+ | `.claude/agents/ux-design-expert.md` | Comando Claude Code para ativar @ux-design-expert |
92
92
 
93
93
  ### Tasks por Fase
94
94
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinapse-ai",
3
- "version": "1.25.0",
3
+ "version": "1.25.1",
4
4
  "description": "SINAPSE AI: Framework de orquestracao de IA — 17 squads, 172 agentes especializados",
5
5
  "bin": {
6
6
  "sinapse": "bin/sinapse.js",
@@ -31,6 +31,7 @@
31
31
  "!.sinapse-ai/node_modules",
32
32
  "!**/node_modules/**",
33
33
  ".claude/CLAUDE.md",
34
+ ".claude/settings.json",
34
35
  ".claude/rules/",
35
36
  ".claude/hooks/",
36
37
  ".claude/agents/",
@@ -50,7 +51,6 @@
50
51
  ".codex/hooks/",
51
52
  ".codex/agents/",
52
53
  ".codex/scripts/",
53
- ".codex/skills/",
54
54
  ".codex/tasks/",
55
55
  ".agents/skills/",
56
56
  "AGENTS.md",
@@ -113,29 +113,29 @@
113
113
  "validate:manifest": "node scripts/validate-manifest.js",
114
114
  "validate:manifest:parity": "node .sinapse-ai/infrastructure/scripts/validate-manifest-parity.js",
115
115
  "validate:agents": "node .sinapse-ai/infrastructure/scripts/validate-agents.js",
116
- "sync:ide": "node .sinapse-ai/infrastructure/scripts/ide-sync/index.js sync",
117
- "sync:ide:validate": "node .sinapse-ai/infrastructure/scripts/ide-sync/index.js validate",
118
- "sync:ide:check": "node .sinapse-ai/infrastructure/scripts/ide-sync/index.js validate --strict",
119
- "sync:ide:claude": "node .sinapse-ai/infrastructure/scripts/ide-sync/index.js sync --ide claude-code",
116
+ "sync:ide": "node scripts/sync-provider-adapters.js",
117
+ "sync:ide:validate": "node scripts/validate-provider-adapters.js",
118
+ "sync:ide:check": "node scripts/validate-provider-adapters.js",
119
+ "sync:ide:claude": "node scripts/sync-provider-adapters.js",
120
120
  "sync:ide:codex": "node .sinapse-ai/infrastructure/scripts/sync-codex-local-first.js && node .codex/scripts/sync-codex-native.js",
121
121
  "sync:providers": "node scripts/sync-provider-adapters.js",
122
- "validate:claude-sync": "node .sinapse-ai/infrastructure/scripts/ide-sync/index.js validate --ide claude-code --strict",
123
- "validate:claude-integration": "node .sinapse-ai/infrastructure/scripts/validate-claude-integration.js",
122
+ "validate:claude-sync": "node scripts/validate-provider-adapters.js",
123
+ "validate:claude-integration": "node scripts/validate-provider-adapters.js",
124
124
  "validate:providers": "node scripts/validate-provider-adapters.js",
125
125
  "validate:engineering-applicability": "jest tests/unit/engineering-applicability.test.js --runInBand",
126
126
  "validate:squad-schema": "node scripts/validate-squad-yaml.js",
127
127
  "validate:squad-schema:strict": "node scripts/validate-squad-yaml.js --strict",
128
- "validate:codex-sync": "node .sinapse-ai/infrastructure/scripts/validate-codex-sync.js && node .codex/scripts/validate-codex-native.js",
128
+ "validate:codex-sync": "node .codex/scripts/validate-codex-native.js && node scripts/validate-codex-command-registry.js && node .sinapse-ai/infrastructure/scripts/validate-codex-integration.js && node .sinapse-ai/infrastructure/scripts/validate-codex-delegation.js",
129
129
  "validate:codex-native": "node .codex/scripts/validate-codex-native.js",
130
130
  "validate:codex-integration": "node .sinapse-ai/infrastructure/scripts/validate-codex-integration.js",
131
- "validate:codex-commands": "node .sinapse-ai/infrastructure/scripts/validate-codex-command-registry.js",
131
+ "validate:codex-commands": "node scripts/validate-codex-command-registry.js",
132
132
  "validate:codex-delegation": "node .sinapse-ai/infrastructure/scripts/validate-codex-delegation.js",
133
- "sync:skills:codex": "node .sinapse-ai/infrastructure/scripts/codex-skills-sync/index.js",
134
- "sync:skills:codex:global": "node .sinapse-ai/infrastructure/scripts/codex-skills-sync/index.js --global --global-only",
135
- "validate:codex-skills": "node .sinapse-ai/infrastructure/scripts/codex-skills-sync/validate.js --strict",
133
+ "sync:skills:codex": "node .codex/scripts/sync-codex-native.js",
134
+ "sync:skills:codex:global": "node bin/cli.js install --llm=codex --global-only",
135
+ "validate:codex-skills": "node .codex/scripts/validate-codex-native.js",
136
136
  "validate:paths": "node .sinapse-ai/infrastructure/scripts/validate-paths.js",
137
- "validate:parity": "node .sinapse-ai/infrastructure/scripts/validate-parity.js",
138
- "validate:parity:fast": "node .sinapse-ai/infrastructure/scripts/validate-parity.js --fast --quiet",
137
+ "validate:parity": "node scripts/validate-provider-adapters.js && node .codex/scripts/validate-codex-native.js && node scripts/validate-codex-command-registry.js && node .sinapse-ai/infrastructure/scripts/validate-codex-integration.js && node .sinapse-ai/infrastructure/scripts/validate-codex-delegation.js && node .sinapse-ai/infrastructure/scripts/validate-paths.js",
138
+ "validate:parity:fast": "node scripts/validate-provider-adapters.js && node scripts/validate-codex-command-registry.js",
139
139
  "collab:adopt": "node bin/utils/collab-start.js --adopt-current",
140
140
  "collab:check": "node bin/utils/collab-start.js --check",
141
141
  "collab:start": "node bin/utils/collab-start.js",
@@ -264,5 +264,10 @@
264
264
  "ip-address": "^10.1.1",
265
265
  "@babel/core": "^7.29.6",
266
266
  "js-yaml": "^4.2.0"
267
- }
267
+ },
268
+ "directories": {
269
+ "doc": "docs",
270
+ "test": "tests"
271
+ },
272
+ "type": "commonjs"
268
273
  }
@@ -38,7 +38,7 @@ const IDE_CONFIGS = {
38
38
  requiresDirectory: true,
39
39
  format: 'text',
40
40
  recommended: true,
41
- agentFolder: path.join('.claude', 'commands', 'SINAPSE', 'agents'),
41
+ agentFolder: path.join('.claude', 'agents'),
42
42
  },
43
43
  codex: {
44
44
  name: 'Codex CLI',
@@ -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,