sinapse-ai 1.25.0 → 1.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.json +170 -0
- package/.codex/catalog.json +1 -1
- package/.codex/scripts/sinapse-codex.js +0 -5
- package/.codex/scripts/sync-codex-native.js +4 -37
- package/.codex/scripts/validate-codex-native.js +13 -23
- package/.codex/tasks/onboard-sinapse-codex.md +1 -1
- package/.sinapse-ai/data/entity-registry.yaml +44 -18
- package/.sinapse-ai/install-manifest.yaml +3 -3
- package/CHANGELOG.md +8 -16
- package/bin/cli.js +8 -1
- package/bin/commands/help.js +3 -2
- package/bin/commands/install.js +25 -14
- package/bin/commands/status.js +4 -3
- package/bin/commands/uninstall.js +216 -51
- package/bin/commands/update.js +8 -4
- package/bin/lib/global-provider-adapters.js +73 -10
- package/bin/lib/provider-parity.js +22 -0
- package/bin/postinstall.js +11 -11
- package/docs/framework/codex-parity/codex-integration-process.md +6 -7
- package/docs/framework/codex-parity/codex-native-runtime-v2.md +8 -8
- package/docs/framework/codex-parity/codex-parity-program.md +4 -4
- package/docs/framework/codex-parity/codex-total-parity-orchestration-plan.md +1 -1
- package/docs/guides/codex-config.md +2 -2
- package/docs/guides/development-setup.md +1 -2
- package/docs/guides/ide-integration.md +6 -6
- package/docs/guides/quality-gates.md +2 -2
- package/docs/ide-integration.md +2 -2
- package/docs/installation/faq.md +1 -1
- package/docs/installation/troubleshooting.md +2 -2
- package/docs/installation/v4-quick-start.md +1 -1
- package/docs/pt/contributing.md +1 -1
- package/docs/pt/guides/development-setup.md +1 -2
- package/docs/pt/guides/quality-gates.md +2 -2
- package/docs/pt/ide-integration.md +6 -6
- package/docs/pt/installation/faq.md +1 -1
- package/docs/pt/installation/troubleshooting.md +2 -3
- package/docs/pt/installation/v4-quick-start.md +1 -2
- package/docs/pt/platforms/claude-code.md +4 -4
- package/docs/sinapse-agent-flows/analyst-system.md +1 -1
- package/docs/sinapse-agent-flows/architect-system.md +1 -1
- package/docs/sinapse-agent-flows/data-engineer-system.md +1 -1
- package/docs/sinapse-agent-flows/dev-system.md +1 -1
- package/docs/sinapse-agent-flows/pm-system.md +1 -1
- package/docs/sinapse-agent-flows/qa-system.md +1 -1
- package/docs/sinapse-agent-flows/sm-system.md +1 -1
- package/docs/sinapse-agent-flows/snps-orqx-system.md +1 -1
- package/docs/sinapse-agent-flows/squad-creator-system.md +2 -2
- package/docs/sinapse-agent-flows/ux-design-expert-system.md +1 -1
- package/package.json +21 -16
- package/packages/installer/src/config/ide-configs.js +1 -1
- package/packages/installer/src/installer/sinapse-ai-installer.js +282 -10
- package/packages/installer/src/migrations/legacy-claude-agent-command-hashes.json +36 -0
- package/packages/installer/src/migrations/legacy-codex-native-skill-hashes.json +42 -0
- package/packages/installer/src/updater/index.js +24 -7
- package/packages/installer/src/wizard/ide-config-generator.js +4 -0
- package/packages/installer/src/wizard/index.js +6 -2
- package/scripts/validate-codex-command-registry.js +101 -0
- package/scripts/validate-provider-adapters.js +117 -1
- package/squads/claude-code-mastery/agents/skill-craftsman.md +2 -2
- package/squads/claude-code-mastery/knowledge-base/skill-creation-patterns.md +1 -1
- package/squads/squad-design/agents/ux-designer.md +1 -1
- package/.codex/skills/sinapse/SKILL.md +0 -10
- package/.codex/skills/sinapse-agent/SKILL.md +0 -22
- package/.codex/skills/sinapse-analyst/SKILL.md +0 -29
- package/.codex/skills/sinapse-animations/SKILL.md +0 -28
- package/.codex/skills/sinapse-architect/SKILL.md +0 -31
- package/.codex/skills/sinapse-brand/SKILL.md +0 -28
- package/.codex/skills/sinapse-claude/SKILL.md +0 -28
- package/.codex/skills/sinapse-cloning/SKILL.md +0 -28
- package/.codex/skills/sinapse-commercial/SKILL.md +0 -28
- package/.codex/skills/sinapse-content/SKILL.md +0 -28
- package/.codex/skills/sinapse-copy/SKILL.md +0 -28
- package/.codex/skills/sinapse-council/SKILL.md +0 -28
- package/.codex/skills/sinapse-courses/SKILL.md +0 -28
- package/.codex/skills/sinapse-cyber/SKILL.md +0 -28
- package/.codex/skills/sinapse-data-engineer/SKILL.md +0 -31
- package/.codex/skills/sinapse-design/SKILL.md +0 -28
- package/.codex/skills/sinapse-dev/SKILL.md +0 -31
- package/.codex/skills/sinapse-devops/SKILL.md +0 -31
- package/.codex/skills/sinapse-finance/SKILL.md +0 -28
- package/.codex/skills/sinapse-growth/SKILL.md +0 -28
- package/.codex/skills/sinapse-loop/SKILL.md +0 -35
- package/.codex/skills/sinapse-orqx/SKILL.md +0 -44
- package/.codex/skills/sinapse-paidmedia/SKILL.md +0 -28
- package/.codex/skills/sinapse-pm/SKILL.md +0 -31
- package/.codex/skills/sinapse-po/SKILL.md +0 -31
- package/.codex/skills/sinapse-product/SKILL.md +0 -28
- package/.codex/skills/sinapse-qa/SKILL.md +0 -31
- package/.codex/skills/sinapse-research/SKILL.md +0 -28
- package/.codex/skills/sinapse-sm/SKILL.md +0 -27
- package/.codex/skills/sinapse-spec-driven/SKILL.md +0 -35
- package/.codex/skills/sinapse-squad-creator/SKILL.md +0 -31
- package/.codex/skills/sinapse-storytelling/SKILL.md +0 -28
- package/.codex/skills/sinapse-swarm/SKILL.md +0 -28
- package/.codex/skills/sinapse-ux-design-expert/SKILL.md +0 -24
- package/.codex/skills/snps/SKILL.md +0 -10
- package/.codex/skills/snps-orqx/SKILL.md +0 -10
- /package/{.codex → .agents}/skills/react-bits-frontend/SKILL.md +0 -0
- /package/{.codex → .agents}/skills/react-bits-frontend/agents/openai.yaml +0 -0
- /package/{.codex → .agents}/skills/react-bits-frontend/references/corpus-map.md +0 -0
|
@@ -554,7 +554,7 @@ nvm use 16
|
|
|
554
554
|
|
|
555
555
|
```bash
|
|
556
556
|
# Claude Code
|
|
557
|
-
ls .claude/
|
|
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:
|
|
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
|
-
│ ├──
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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.
|
|
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
|
|
117
|
-
"sync:ide:validate": "node
|
|
118
|
-
"sync:ide:check": "node
|
|
119
|
-
"sync:ide:claude": "node
|
|
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
|
|
123
|
-
"validate:claude-integration": "node
|
|
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-
|
|
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
|
|
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 .
|
|
134
|
-
"sync:skills:codex:global": "node
|
|
135
|
-
"validate:codex-skills": "node .
|
|
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-
|
|
138
|
-
"validate:parity:fast": "node
|
|
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
|
}
|
|
@@ -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
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
?
|
|
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,
|