raptor-aios 0.11.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -5,6 +5,37 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.12.1] - 2026-06-13
9
+
10
+ ### Changed
11
+
12
+ - **Documentação em pt-BR.** Toda a documentação em `docs/` passa a ser escrita em
13
+ português brasileiro (identificadores técnicos — comandos `/raptor-*`, gates `gate.*`,
14
+ paths e campos de frontmatter — permanecem em inglês). Traduzidas e corrigidas contra o
15
+ código real: `artifact-chain.md` e `state-model.md` (comandos legados `/rpt.*` →
16
+ `/raptor-*`, criação de feature via `raptor new`, constituição em
17
+ `.raptor/constitution.md`) e `script-contract.md`.
18
+ - **`docs/spec-kit-parity.md` reescrita.** Comparação Raptor × Spec Kit confirmada por
19
+ análise de código: paridade 1:1 dos 9 comandos + a camada de enforcement (constituição
20
+ executável C1–C5/M1–M6, motor de 36+ gates, cadeia de staleness, verificação no edge) e
21
+ de captura (Jira/Figma determinísticos). Dados do Spec Kit atualizados a partir da fonte
22
+ oficial (comandos essenciais vs opcionais, 30+ agentes, tagline) e enriquecida com 5
23
+ diagramas mermaid. Escopo honesto: só `verify perf` roda o app; não há `verify crash`.
24
+
25
+ > Release só-docs: a pasta `docs/` não é empacotada no npm, então o pacote instalável é
26
+ > equivalente ao `0.12.0` (muda apenas o CHANGELOG e a versão).
27
+
28
+ ## [0.12.0] - 2026-06-13
29
+
30
+ ### Added
31
+
32
+ - **`raptor setup` — menu de instalação interativo + onboarding (`packages/cli/src/commands/setup.ts`).** Uma porta de entrada guiada por setas (via `@clack/prompts`) que conduz, fase a fase: ❶ **onboarding** (a essência do Raptor, pulável com `--skip-intro`); ❷ **ponto de partida** (projeto do zero vs. existente, com detecção de stack do `package.json`); ❸ **ambiente em duas perguntas** — **IDE** (VS Code, Cursor, Antigravity, JetBrains, terminal) → **IA** (Claude Code, Copilot, Gemini, Codex), filtradas em cascata e mapeadas para a *agent key* correta; ❹ **preset** (com a sugestão da stack destacada); ❺ **Jira/Figma "configurar depois"** (grava o bloco pronto no `raptor.yml`, sem disparar OAuth no fluxo); ❻ **revisão e aplicação**.
33
+ - **Coleta separada da execução.** O wizard apenas monta um `SetupPlan`; `applySetupPlan` o executa reusando a lógica de `init` — extraída intacta para `performInit` (`shared/init-core.ts`) —, então o `.raptor/` gerado é byte-equivalente ao de `raptor init`.
34
+ - **Caminho não-interativo** (`--non-interactive --ide --ai --preset …`, ou simplesmente a ausência de TTY) para CI/scripts, compartilhando o MESMO plano do wizard — o que mantém a experiência interativa testável sem dirigir um terminal.
35
+ - **Reconfigure não-destrutivo:** `raptor setup --force` agora preserva os blocos do usuário no `raptor.yml` (`git` com `commit_trailer`/`kind_prefixes`, `gates`, conexões `jira`/`design` — inclusive desconectadas-mas-configuradas, que retêm `cloud_id`/`custom_fields`/token — e o nome do projeto), em vez de revertê-los aos defaults do template.
36
+ - Banner ASCII responsivo (degrada em terminais estreitos, respeita `NO_COLOR`/não-TTY) e exit codes corretos (cancelar ou falhar → não-zero; recusar a aplicação → zero).
37
+ - Endurecido por revisão adversarial multi-dimensão (correção, fidelidade de API `@clack`, paridade da extração de `init`, contrato de config, reuso), com etapa cética por achado e o ciclo repetido até convergir.
38
+
8
39
  ## [0.11.0] - 2026-06-12
9
40
 
10
41
  ### Added
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  ```bash
23
23
  npm install -g raptor-aios
24
24
  cd meu-app-react-native
25
- raptor init --ai=claude-code --preset=mobile-opinionated
25
+ raptor setup # 🧭 assistente guiado: IA/IDE, preset, Jira/Figma (ou 'raptor init' direto)
26
26
  raptor new login-biometrico -d "Permitir login com Face ID / digital"
27
27
  # → cria a feature E já troca para a branch feat/001-login-biometrico
28
28
  ```
@@ -95,11 +95,19 @@ raptor doctor # 🩺 checagem de saúde do ambiente e do projeto
95
95
 
96
96
  ### 1️⃣ Inicialize o Raptor
97
97
 
98
+ A forma **guiada** — um assistente interativo (navegação por setas) que apresenta o Raptor e coleta IA/IDE, preset e integrações Jira/Figma:
99
+
100
+ ```bash
101
+ raptor setup
102
+ ```
103
+
104
+ Ou, **direto e não-interativo** (ideal para CI/scripts):
105
+
98
106
  ```bash
99
107
  raptor init --ai=claude-code --preset=mobile-opinionated
100
108
  ```
101
109
 
102
- Isso cria a pasta `.raptor/`, a **constituição** do projeto, e materializa os **slash commands** em `.claude/commands/` (ou `.cursor/`, etc., conforme o agente).
110
+ Qualquer um cria a pasta `.raptor/`, a **constituição** do projeto, e materializa os **slash commands** em `.claude/commands/` (ou `.cursor/`, etc., conforme o agente).
103
111
 
104
112
  | Flag | Para que serve |
105
113
  | ----------------------------- | ------------------------------------------------------------------------------------------------------------ |
@@ -481,7 +489,7 @@ raptor new login --jira APP-1234 # semeia a spec a partir da issue
481
489
  ## 📖 Referência de comandos CLI
482
490
 
483
491
  ```text
484
- 🔄 Ciclo init · new · clarify · plan · tasks · analyze · checklist · implement · approve · taskstoissues
492
+ 🔄 Ciclo setup · init · new · clarify · plan · tasks · analyze · checklist · implement · approve · taskstoissues
485
493
  🌿 Branch/commit commit · scan
486
494
  🔬 Verificação verify · verify a11y · verify perf · verify stores · verify os-matrix
487
495
  verify architecture · verify audit · verify constitution
@@ -557,6 +565,7 @@ Comece pelo **[📖 Glossário Canônico](docs/glossary.md)** — a referência
557
565
  | Documento | Sobre |
558
566
  | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
559
567
  | **[📖 docs/glossary.md](docs/glossary.md)** | Glossário canônico: conceitos, hierarquias, catálogo de 42 gates e de comandos CLI |
568
+ | [🧭 docs/setup-wizard.md](docs/setup-wizard.md) | Assistente de instalação `raptor setup`: onboarding, IA/IDE, preset, Jira/Figma |
560
569
  | [🛡️ docs/readiness-gates.md](docs/readiness-gates.md) | Gates de prontidão (`spec/plan/tasks.ready`) em detalhe |
561
570
  | [🎨 docs/design-gate.md](docs/design-gate.md) | Design gate, integração Figma e `assets-manifest.json` |
562
571
  | [🔗 docs/artifact-chain.md](docs/artifact-chain.md) | Cadeia de artefatos e rastreabilidade spec→plan→tasks→código |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raptor/core",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js"
6
6
  }
@@ -1,10 +1,8 @@
1
1
  import { Flags } from "@oclif/core";
2
2
  import { BaseCommand } from "../base-command.js";
3
- import { chmodSync, existsSync, mkdirSync, writeFileSync, } from "node:fs";
4
- import { basename, join } from "node:path";
5
- import { appendAuditEvent, buildContextBlock, coreBlock, CORE_VERSION, createManifest, detectInstalledAgents, generateAgentsYaml, getAgentByKind, getPreset, hashString, knownPresetIds, resolvePresetId, materializeSlashCommands, renderBundled, renderPresetArticles, renderPresetsArticles, SELECTABLE_AGENT_KEYS, upsertContextFile, VERSION, } from "../_core/dist/index.js";
6
- import { currentActor } from "../shared/project.js";
7
- import { collectKnowledgeRefs, installBundledExtensions, installShellScripts, installTemplates, } from "../shared/scaffold.js";
3
+ import { basename } from "node:path";
4
+ import { CORE_VERSION, SELECTABLE_AGENT_KEYS } from "../_core/dist/index.js";
5
+ import { describeCi, describeHooks, InitExistsError, performInit, } from "../shared/init-core.js";
8
6
  export default class Init extends BaseCommand {
9
7
  static description = "Initialize a Raptor project in the current directory";
10
8
  static examples = [
@@ -56,184 +54,31 @@ export default class Init extends BaseCommand {
56
54
  };
57
55
  async run() {
58
56
  const { flags } = await this.parse(Init);
59
- const cwd = process.cwd();
60
- const raptorDir = join(cwd, ".raptor");
61
- if (existsSync(raptorDir) && !flags.force) {
62
- this.error("A .raptor/ directory already exists here. Use --force to overwrite.");
57
+ let result;
58
+ try {
59
+ result = performInit({
60
+ cwd: process.cwd(),
61
+ projectName: flags["project-name"],
62
+ preset: flags.preset,
63
+ ai: flags.ai,
64
+ script: flags.script,
65
+ branchNumbering: flags["branch-numbering"],
66
+ noGit: flags["no-git"],
67
+ withCi: flags["with-ci"],
68
+ withHooks: flags["with-hooks"],
69
+ force: flags.force,
70
+ }, this);
63
71
  }
64
- const projectName = flags["project-name"] ?? basename(cwd);
65
- const createdAt = new Date().toISOString();
66
- const subdirs = [
67
- "specs",
68
- "memory",
69
- "templates",
70
- "templates/overrides",
71
- "extensions",
72
- "workflows",
73
- "presets",
74
- "agents",
75
- "skills",
76
- "scripts",
77
- "scripts/bash",
78
- "scripts/powershell",
79
- "cache",
80
- ];
81
- mkdirSync(raptorDir, { recursive: true });
82
- for (const sub of subdirs)
83
- mkdirSync(join(raptorDir, sub), { recursive: true });
84
- const rawPresetIds = flags.preset
85
- .split(",")
86
- .map((s) => s.trim())
87
- .filter((s) => s.length > 0);
88
- const resolved = rawPresetIds.map((raw) => ({
89
- raw,
90
- id: resolvePresetId(raw) ?? raw,
91
- preset: getPreset(raw),
92
- }));
93
- for (const r of resolved) {
94
- if (!r.preset) {
95
- this.warn(`unknown preset "${r.raw}" — known presets: ${knownPresetIds().join(", ")}. It will have no constitution articles.`);
72
+ catch (err) {
73
+ if (err instanceof InitExistsError) {
74
+ this.error(err.message);
96
75
  }
76
+ throw err;
97
77
  }
98
- const presets = resolved.map((r) => r.preset).filter((p) => Boolean(p));
99
- const presetIds = resolved.map((r) => r.id);
100
- const preset = presets[0];
101
- const presetId = presetIds[0];
102
- const presetArticles = presets.length > 1
103
- ? renderPresetsArticles(presets)
104
- : preset
105
- ? renderPresetArticles(preset)
106
- : "";
107
- const presetLabel = presetIds.join(", ");
108
- const allArticleIds = presets.length
109
- ? presets.flatMap((p) => p.articles.map((a) => a.id))
110
- : undefined;
111
- const presetsBlock = presetIds.length > 1
112
- ? `presets:\n${presetIds.map((id) => ` - ${id}`).join("\n")}`
113
- : `preset: ${presetId}`;
114
- const raptorYml = renderBundled("raptorYml", {
115
- raptorVersion: VERSION,
116
- projectName,
117
- createdAt,
118
- presetsBlock,
119
- });
120
- writeFileSync(join(raptorDir, "raptor.yml"), raptorYml);
121
- const manifest = createManifest({
122
- raptor_version: VERSION,
123
- project_name: projectName,
124
- integration: flags.ai,
125
- script_type: flags.script,
126
- branch_numbering: flags["branch-numbering"],
127
- preset: presetId,
128
- scripts_installed: [],
129
- templates_installed: [],
130
- extensions_registered: [],
131
- });
132
- writeFileSync(join(raptorDir, "raptor.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
133
- const initOptions = {
134
- ai: flags.ai,
135
- script_type: flags.script,
136
- branch_numbering: flags["branch-numbering"],
137
- preset: presetId,
138
- no_git: flags["no-git"],
139
- created_at: createdAt,
140
- };
141
- writeFileSync(join(raptorDir, "init-options.json"), JSON.stringify(initOptions, null, 2) + "\n");
142
- const constitution = renderBundled("constitution", {
143
- projectName,
144
- preset: presetLabel,
145
- coreBlock: coreBlock(),
146
- presetArticles,
147
- });
148
- const constitutionPath = join(raptorDir, "constitution.md");
149
- writeFileSync(constitutionPath, constitution);
150
- writeFileSync(join(raptorDir, "memory", "constitution.md"), constitution);
151
- writeFileSync(join(raptorDir, "memory", "amendments.log"), "");
152
- writeFileSync(join(raptorDir, "memory", "decisions.md"), "# Architecture Decision Records\n\n");
153
- writeFileSync(join(raptorDir, "memory", "glossary.md"), "# Project Glossary\n\n");
154
- writeFileSync(join(raptorDir, ".gitignore"), "cache/\n*.log\n");
155
- installShellScripts(this, raptorDir, manifest);
156
- installTemplates(raptorDir);
157
- installBundledExtensions(this, raptorDir, manifest, presetIds);
158
- const knowledgeRefs = collectKnowledgeRefs(raptorDir);
159
- const installedAgents = detectInstalledAgents();
160
- const primaryAgent = getAgentByKind(flags.ai);
161
- if (primaryAgent && !installedAgents.some((a) => a.id === primaryAgent.id)) {
162
- this.warn(`Primary agent "${flags.ai}" CLI was not detected on PATH — materializing its commands anyway.`);
163
- }
164
- const targetAgents = primaryAgent
165
- ? [
166
- primaryAgent,
167
- ...installedAgents.filter((a) => a.id !== primaryAgent.id),
168
- ]
169
- : installedAgents;
170
- const agentsYamlContent = generateAgentsYaml(targetAgents.map((a) => ({
171
- id: a.id,
172
- kind: a.id,
173
- capabilities: [...a.capabilities],
174
- })));
175
- const agentsYamlPath = join(raptorDir, "agents.yml");
176
- writeFileSync(agentsYamlPath, agentsYamlContent);
177
- const contextBlock = buildContextBlock({
178
- projectName,
179
- preset: presetLabel,
180
- constitutionArticles: allArticleIds,
181
- knowledge: knowledgeRefs,
182
- });
183
- for (const agent of targetAgents) {
184
- if (agent.contextFile) {
185
- const filePath = join(cwd, agent.contextFile);
186
- upsertContextFile(filePath, contextBlock);
187
- }
188
- }
189
- const allMaterialized = [];
190
- for (const agent of targetAgents) {
191
- if (agent.id === "human")
192
- continue;
193
- try {
194
- const result = materializeSlashCommands({
195
- projectRoot: cwd,
196
- projectName,
197
- adapter: agent,
198
- constitutionArticles: allArticleIds,
199
- presetId: presetId,
200
- });
201
- allMaterialized.push({
202
- agentId: agent.id,
203
- count: result.commands.length,
204
- paths: result.commands.map((c) => c.path),
205
- });
206
- }
207
- catch (err) {
208
- this.warn(`Failed to materialize slash commands for ${agent.displayName}: ${err instanceof Error ? err.message : String(err)}`);
209
- }
210
- }
211
- const amendmentsLog = join(raptorDir, "memory", "amendments.log");
212
- const actor = currentActor("human");
213
- appendAuditEvent(amendmentsLog, {
214
- event: "constitution.initialized",
215
- command: "raptor init",
216
- actor: { user: actor.user, via: actor.via },
217
- outputs: [
218
- { path: ".raptor/constitution.md", hash: hashString(constitution) },
219
- { path: ".raptor/agents.yml", hash: hashString(agentsYamlContent) },
220
- ],
221
- meta: {
222
- core_version: CORE_VERSION,
223
- preset: presetLabel,
224
- preset_version: preset?.version,
225
- preset_articles: allArticleIds,
226
- project: projectName,
227
- detected_agents: installedAgents.map((a) => a.id),
228
- configured_agents: targetAgents.map((a) => a.id),
229
- },
230
- });
231
- const ciResult = flags["with-ci"]
232
- ? this.installCiWorkflow(cwd)
233
- : { status: "disabled" };
234
- const hooksResult = flags["with-hooks"]
235
- ? this.installGitHooks(cwd)
236
- : { status: "disabled" };
78
+ this.renderSummary(result, flags);
79
+ }
80
+ renderSummary(result, flags) {
81
+ const { raptorDir, projectName, presets, presetLabel, allArticleIds, targetAgents, materialized, primaryAgentId, agentsYamlPath, manifestPath, ciResult, hooksResult, } = result;
237
82
  this.log(`✓ Initialized Raptor project in ${raptorDir}`);
238
83
  this.log(` Project: ${projectName}`);
239
84
  if (presets.length > 1) {
@@ -242,6 +87,7 @@ export default class Init extends BaseCommand {
242
87
  this.log(` Presets: ${presetLabel} (${presets.length} stacked, ${totalArticles} articles, ${totalGates} gates)`);
243
88
  }
244
89
  else {
90
+ const preset = presets[0];
245
91
  this.log(` Preset: ${flags.preset}${preset ? ` (v${preset.version}, ${preset.articles.length} articles, ${preset.gates.length} gates)` : " (unknown)"}`);
246
92
  }
247
93
  this.log(` Core: v${CORE_VERSION}`);
@@ -249,7 +95,7 @@ export default class Init extends BaseCommand {
249
95
  this.log(` Articles: ${allArticleIds.join(", ")}`);
250
96
  }
251
97
  this.log(` Agents: ${targetAgents.map((a) => `${a.displayName} (${a.id})`).join(", ")}`);
252
- for (const m of allMaterialized) {
98
+ for (const m of materialized) {
253
99
  this.log(` Slash: ${m.count} commands materialized for ${m.agentId}`);
254
100
  }
255
101
  this.log(` Config: ${agentsYamlPath}`);
@@ -257,11 +103,11 @@ export default class Init extends BaseCommand {
257
103
  this.log(` Hooks: ${describeHooks(hooksResult)}`);
258
104
  this.log("");
259
105
  this.log(` AI: ${flags.ai} (script: ${flags.script}, branching: ${flags["branch-numbering"]})`);
260
- this.log(` Manifest: ${join(raptorDir, "raptor.manifest.json")}`);
106
+ this.log(` Manifest: ${manifestPath}`);
261
107
  this.log("Next steps:");
262
108
  this.log(` 1. Review ${raptorDir}/constitution.md (RAPTOR-CORE block is immutable)`);
263
109
  this.log(` 2. Review ${raptorDir}/agents.yml (agent policy + fallback chain)`);
264
- const primaryMat = allMaterialized.find((m) => primaryAgent && m.agentId === primaryAgent.id) ?? allMaterialized[0];
110
+ const primaryMat = materialized.find((m) => primaryAgentId && m.agentId === primaryAgentId) ?? materialized[0];
265
111
  const slashList = primaryMat
266
112
  ? primaryMat.paths
267
113
  .map((p) => `/${basename(p).replace(/\.[^.]+$/, "")}`)
@@ -270,61 +116,4 @@ export default class Init extends BaseCommand {
270
116
  this.log(` 3. Use slash commands: ${slashList}`);
271
117
  this.log(` 4. Or run 'raptor new <slug>' to create your first feature directly`);
272
118
  }
273
- installCiWorkflow(cwd) {
274
- const dir = join(cwd, ".github", "workflows");
275
- const path = join(dir, "raptor-verify.yml");
276
- if (existsSync(path)) {
277
- return { status: "skipped-existing", path };
278
- }
279
- mkdirSync(dir, { recursive: true });
280
- const content = renderBundled("ghaWorkflow", { raptorVersion: VERSION });
281
- writeFileSync(path, content);
282
- return { status: "installed", path };
283
- }
284
- installGitHooks(cwd) {
285
- const gitDir = join(cwd, ".git");
286
- if (!existsSync(gitDir)) {
287
- return { status: "skipped-no-git" };
288
- }
289
- const hooksDir = join(gitDir, "hooks");
290
- mkdirSync(hooksDir, { recursive: true });
291
- const hooks = [
292
- { name: "pre-commit", key: "preCommitHook" },
293
- { name: "pre-push", key: "prePushHook" },
294
- { name: "commit-msg", key: "commitMsgHook" },
295
- ];
296
- const installed = [];
297
- const skipped = [];
298
- for (const h of hooks) {
299
- const path = join(hooksDir, h.name);
300
- if (existsSync(path)) {
301
- skipped.push(h.name);
302
- continue;
303
- }
304
- const content = renderBundled(h.key, {});
305
- writeFileSync(path, content);
306
- chmodSync(path, 0o755);
307
- installed.push(h.name);
308
- }
309
- return { status: "ok", installed, skipped };
310
- }
311
- }
312
- function describeCi(r) {
313
- if (r.status === "disabled")
314
- return "disabled (--no-with-ci)";
315
- if (r.status === "skipped-existing")
316
- return `skipped (${r.path} already exists)`;
317
- return `installed at ${r.path}`;
318
- }
319
- function describeHooks(r) {
320
- if (r.status === "disabled")
321
- return "disabled (--no-with-hooks)";
322
- if (r.status === "skipped-no-git")
323
- return "skipped (.git/ not found — run 'git init' first)";
324
- const parts = [];
325
- if (r.installed.length > 0)
326
- parts.push(`installed: ${r.installed.join(", ")}`);
327
- if (r.skipped.length > 0)
328
- parts.push(`skipped (already exist): ${r.skipped.join(", ")}`);
329
- return parts.length > 0 ? parts.join("; ") : "nothing to do";
330
119
  }
@@ -0,0 +1,149 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../base-command.js";
3
+ import { applySetupPlan, buildSetupPlan, } from "../shared/setup/plan.js";
4
+ import { describeCi, describeHooks, InitExistsError, } from "../shared/init-core.js";
5
+ import { aisForIde, IDES, resolveAgentKey, } from "../shared/setup/agent-resolver.js";
6
+ import { detectProjectStack } from "../shared/setup/detect-stack.js";
7
+ import { resolveInteractive } from "../shared/git-prompt.js";
8
+ import { runSetupWizard } from "../shared/setup/wizard.js";
9
+ const IDE_IDS = IDES.map((i) => i.id);
10
+ export default class Setup extends BaseCommand {
11
+ static description = "Guided interactive setup — onboarding + project initialization (arrow-key wizard)";
12
+ static examples = [
13
+ "<%= config.bin %> setup",
14
+ "<%= config.bin %> setup --ide=vscode --ai=claude-code",
15
+ "<%= config.bin %> setup --non-interactive --ide=terminal --ai=claude-code --preset=mobile-opinionated",
16
+ ];
17
+ static flags = {
18
+ ide: Flags.string({
19
+ description: `Target IDE/environment (${IDE_IDS.join(", ")})`,
20
+ options: IDE_IDS,
21
+ }),
22
+ ai: Flags.string({
23
+ description: "AI assistant id within the chosen IDE (valid values depend on --ide)",
24
+ }),
25
+ preset: Flags.string({
26
+ description: "Constitution preset, or comma-separated stack",
27
+ }),
28
+ "project-name": Flags.string({
29
+ description: "Project display name (default: current directory name)",
30
+ }),
31
+ "jira-later": Flags.boolean({
32
+ description: "Write a disabled jira: block so 'raptor jira connect' is one step away",
33
+ default: false,
34
+ }),
35
+ "figma-later": Flags.boolean({
36
+ description: "Write a disabled design: block so 'raptor design connect' is one step away",
37
+ default: false,
38
+ }),
39
+ "skip-intro": Flags.boolean({
40
+ description: "Skip the onboarding intro in the wizard",
41
+ default: false,
42
+ }),
43
+ "non-interactive": Flags.boolean({
44
+ description: "Run without prompts (requires --ide and --ai)",
45
+ default: false,
46
+ }),
47
+ force: Flags.boolean({
48
+ description: "Overwrite an existing .raptor/ directory",
49
+ default: false,
50
+ }),
51
+ };
52
+ async run() {
53
+ const { flags } = await this.parse(Setup);
54
+ const cwd = process.cwd();
55
+ if (resolveInteractive(flags["non-interactive"])) {
56
+ const outcome = await runSetupWizard({
57
+ cwd,
58
+ force: flags.force,
59
+ skipIntro: flags["skip-intro"],
60
+ defaults: {
61
+ ide: flags.ide,
62
+ ai: flags.ai,
63
+ preset: flags.preset,
64
+ projectName: flags["project-name"],
65
+ jiraLater: flags["jira-later"],
66
+ figmaLater: flags["figma-later"],
67
+ },
68
+ });
69
+ if (outcome === "cancelled" || outcome === "failed")
70
+ this.exit(1);
71
+ return;
72
+ }
73
+ const answers = this.answersFromFlags(flags, cwd);
74
+ const plan = this.buildPlanOrFail(answers, cwd, flags.force);
75
+ const result = this.applyOrFail(plan);
76
+ this.renderSummary(result, plan);
77
+ }
78
+ answersFromFlags(flags, cwd) {
79
+ const ide = flags.ide;
80
+ const ai = flags.ai;
81
+ if (!ide || !ai) {
82
+ this.error("Non-interactive setup requires --ide and --ai.\n" +
83
+ "Example: raptor setup --non-interactive --ide=terminal --ai=claude-code");
84
+ }
85
+ if (!resolveAgentKey(ide, ai)) {
86
+ const valid = aisForIde(ide).map((a) => a.id).join(", ") || "(none)";
87
+ this.error(`AI "${ai}" is not valid for IDE "${ide}". Valid: ${valid}`);
88
+ }
89
+ const stack = detectProjectStack(cwd);
90
+ const preset = flags.preset ?? stack.suggestedPreset ?? "mobile-opinionated";
91
+ return {
92
+ startingPoint: stack.isBrownfield ? "existing" : "scratch",
93
+ projectName: flags["project-name"],
94
+ ide,
95
+ ai,
96
+ preset,
97
+ jiraLater: flags["jira-later"],
98
+ figmaLater: flags["figma-later"],
99
+ };
100
+ }
101
+ buildPlanOrFail(answers, cwd, force) {
102
+ try {
103
+ return buildSetupPlan(answers, { cwd, force });
104
+ }
105
+ catch (err) {
106
+ this.error(err instanceof Error ? err.message : String(err));
107
+ }
108
+ }
109
+ applyOrFail(plan) {
110
+ try {
111
+ return applySetupPlan(plan, this);
112
+ }
113
+ catch (err) {
114
+ if (err instanceof InitExistsError) {
115
+ this.error(`${err.message} Use 'raptor setup --force' to reconfigure.`);
116
+ }
117
+ throw err;
118
+ }
119
+ }
120
+ renderSummary(result, plan) {
121
+ const { init } = result;
122
+ this.log(`✓ Raptor setup complete in ${init.raptorDir}`);
123
+ this.log(` Project: ${init.projectName}`);
124
+ this.log(` Preset: ${init.presetLabel}`);
125
+ this.log(` Agent: ${plan.primaryAgentKey}`);
126
+ for (const m of init.materialized) {
127
+ this.log(` Slash: ${m.count} commands materialized for ${m.agentId}`);
128
+ }
129
+ this.log(` CI: ${describeCi(init.ciResult)}`);
130
+ this.log(` Hooks: ${describeHooks(init.hooksResult)}`);
131
+ if (result.jiraPreserved) {
132
+ this.log(" Jira: existing config preserved");
133
+ }
134
+ else if (result.jiraConfigured) {
135
+ this.log(" Jira: configured later (run 'raptor jira connect')");
136
+ }
137
+ if (result.figmaPreserved) {
138
+ this.log(" Figma: existing config preserved");
139
+ }
140
+ else if (result.figmaConfigured) {
141
+ this.log(" Figma: configured later (run 'raptor design connect')");
142
+ }
143
+ this.log("Next steps:");
144
+ this.log(plan.startingPoint === "existing"
145
+ ? " 1. raptor new <slug> — specify a feature over your existing code"
146
+ : " 1. raptor new <slug> — create your first feature");
147
+ this.log(" 2. raptor status — see the project at a glance");
148
+ }
149
+ }
@@ -0,0 +1,260 @@
1
+ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
2
+ import { basename, join } from "node:path";
3
+ import { appendAuditEvent, buildContextBlock, coreBlock, CORE_VERSION, createManifest, detectInstalledAgents, generateAgentsYaml, getAgentByKind, getPreset, hashString, knownPresetIds, resolvePresetId, materializeSlashCommands, renderBundled, renderPresetArticles, renderPresetsArticles, upsertContextFile, VERSION, } from "../_core/dist/index.js";
4
+ import { currentActor } from "./project.js";
5
+ import { collectKnowledgeRefs, installBundledExtensions, installShellScripts, installTemplates, } from "./scaffold.js";
6
+ export class InitExistsError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = "InitExistsError";
10
+ }
11
+ }
12
+ export function performInit(opts, logger) {
13
+ const cwd = opts.cwd;
14
+ const raptorDir = join(cwd, ".raptor");
15
+ if (existsSync(raptorDir) && !opts.force) {
16
+ throw new InitExistsError("A .raptor/ directory already exists here. Use --force to overwrite.");
17
+ }
18
+ const projectName = opts.projectName ?? basename(cwd);
19
+ const createdAt = new Date().toISOString();
20
+ const subdirs = [
21
+ "specs",
22
+ "memory",
23
+ "templates",
24
+ "templates/overrides",
25
+ "extensions",
26
+ "workflows",
27
+ "presets",
28
+ "agents",
29
+ "skills",
30
+ "scripts",
31
+ "scripts/bash",
32
+ "scripts/powershell",
33
+ "cache",
34
+ ];
35
+ mkdirSync(raptorDir, { recursive: true });
36
+ for (const sub of subdirs)
37
+ mkdirSync(join(raptorDir, sub), { recursive: true });
38
+ const rawPresetIds = opts.preset
39
+ .split(",")
40
+ .map((s) => s.trim())
41
+ .filter((s) => s.length > 0);
42
+ const resolved = rawPresetIds.map((raw) => ({
43
+ raw,
44
+ id: resolvePresetId(raw) ?? raw,
45
+ preset: getPreset(raw),
46
+ }));
47
+ for (const r of resolved) {
48
+ if (!r.preset) {
49
+ logger.warn(`unknown preset "${r.raw}" — known presets: ${knownPresetIds().join(", ")}. It will have no constitution articles.`);
50
+ }
51
+ }
52
+ const presets = resolved
53
+ .map((r) => r.preset)
54
+ .filter((p) => Boolean(p));
55
+ const presetIds = resolved.map((r) => r.id);
56
+ const preset = presets[0];
57
+ const presetId = presetIds[0];
58
+ const presetArticles = presets.length > 1
59
+ ? renderPresetsArticles(presets)
60
+ : preset
61
+ ? renderPresetArticles(preset)
62
+ : "";
63
+ const presetLabel = presetIds.join(", ");
64
+ const allArticleIds = presets.length
65
+ ? presets.flatMap((p) => p.articles.map((a) => a.id))
66
+ : undefined;
67
+ const presetsBlock = presetIds.length > 1
68
+ ? `presets:\n${presetIds.map((id) => ` - ${id}`).join("\n")}`
69
+ : `preset: ${presetId}`;
70
+ const raptorYml = renderBundled("raptorYml", {
71
+ raptorVersion: VERSION,
72
+ projectName,
73
+ createdAt,
74
+ presetsBlock,
75
+ });
76
+ writeFileSync(join(raptorDir, "raptor.yml"), raptorYml);
77
+ const manifest = createManifest({
78
+ raptor_version: VERSION,
79
+ project_name: projectName,
80
+ integration: opts.ai,
81
+ script_type: opts.script,
82
+ branch_numbering: opts.branchNumbering,
83
+ preset: presetId,
84
+ scripts_installed: [],
85
+ templates_installed: [],
86
+ extensions_registered: [],
87
+ });
88
+ const manifestPath = join(raptorDir, "raptor.manifest.json");
89
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
90
+ const initOptions = {
91
+ ai: opts.ai,
92
+ script_type: opts.script,
93
+ branch_numbering: opts.branchNumbering,
94
+ preset: presetId,
95
+ no_git: opts.noGit,
96
+ created_at: createdAt,
97
+ };
98
+ writeFileSync(join(raptorDir, "init-options.json"), JSON.stringify(initOptions, null, 2) + "\n");
99
+ const constitution = renderBundled("constitution", {
100
+ projectName,
101
+ preset: presetLabel,
102
+ coreBlock: coreBlock(),
103
+ presetArticles,
104
+ });
105
+ writeFileSync(join(raptorDir, "constitution.md"), constitution);
106
+ writeFileSync(join(raptorDir, "memory", "constitution.md"), constitution);
107
+ writeFileSync(join(raptorDir, "memory", "amendments.log"), "");
108
+ writeFileSync(join(raptorDir, "memory", "decisions.md"), "# Architecture Decision Records\n\n");
109
+ writeFileSync(join(raptorDir, "memory", "glossary.md"), "# Project Glossary\n\n");
110
+ writeFileSync(join(raptorDir, ".gitignore"), "cache/\n*.log\n");
111
+ installShellScripts(logger, raptorDir, manifest);
112
+ installTemplates(raptorDir);
113
+ installBundledExtensions(logger, raptorDir, manifest, presetIds);
114
+ const knowledgeRefs = collectKnowledgeRefs(raptorDir);
115
+ const installedAgents = detectInstalledAgents();
116
+ const primaryAgent = getAgentByKind(opts.ai);
117
+ if (primaryAgent && !installedAgents.some((a) => a.id === primaryAgent.id)) {
118
+ logger.warn(`Primary agent "${opts.ai}" CLI was not detected on PATH — materializing its commands anyway.`);
119
+ }
120
+ const targetAgents = primaryAgent
121
+ ? [primaryAgent, ...installedAgents.filter((a) => a.id !== primaryAgent.id)]
122
+ : installedAgents;
123
+ const agentsYamlContent = generateAgentsYaml(targetAgents.map((a) => ({
124
+ id: a.id,
125
+ kind: a.id,
126
+ capabilities: [...a.capabilities],
127
+ })));
128
+ const agentsYamlPath = join(raptorDir, "agents.yml");
129
+ writeFileSync(agentsYamlPath, agentsYamlContent);
130
+ const contextBlock = buildContextBlock({
131
+ projectName,
132
+ preset: presetLabel,
133
+ constitutionArticles: allArticleIds,
134
+ knowledge: knowledgeRefs,
135
+ });
136
+ for (const agent of targetAgents) {
137
+ if (agent.contextFile) {
138
+ upsertContextFile(join(cwd, agent.contextFile), contextBlock);
139
+ }
140
+ }
141
+ const materialized = [];
142
+ for (const agent of targetAgents) {
143
+ if (agent.id === "human")
144
+ continue;
145
+ try {
146
+ const result = materializeSlashCommands({
147
+ projectRoot: cwd,
148
+ projectName,
149
+ adapter: agent,
150
+ constitutionArticles: allArticleIds,
151
+ presetId: presetId,
152
+ });
153
+ materialized.push({
154
+ agentId: agent.id,
155
+ count: result.commands.length,
156
+ paths: result.commands.map((c) => c.path),
157
+ });
158
+ }
159
+ catch (err) {
160
+ logger.warn(`Failed to materialize slash commands for ${agent.displayName}: ${err instanceof Error ? err.message : String(err)}`);
161
+ }
162
+ }
163
+ const amendmentsLog = join(raptorDir, "memory", "amendments.log");
164
+ const actor = currentActor("human");
165
+ appendAuditEvent(amendmentsLog, {
166
+ event: "constitution.initialized",
167
+ command: opts.auditCommand ?? "raptor init",
168
+ actor: { user: actor.user, via: actor.via },
169
+ outputs: [
170
+ { path: ".raptor/constitution.md", hash: hashString(constitution) },
171
+ { path: ".raptor/agents.yml", hash: hashString(agentsYamlContent) },
172
+ ],
173
+ meta: {
174
+ core_version: CORE_VERSION,
175
+ preset: presetLabel,
176
+ preset_version: preset?.version,
177
+ preset_articles: allArticleIds,
178
+ project: projectName,
179
+ detected_agents: installedAgents.map((a) => a.id),
180
+ configured_agents: targetAgents.map((a) => a.id),
181
+ },
182
+ });
183
+ const ciResult = opts.withCi
184
+ ? installCiWorkflow(cwd)
185
+ : { status: "disabled" };
186
+ const hooksResult = opts.withHooks
187
+ ? installGitHooks(cwd)
188
+ : { status: "disabled" };
189
+ return {
190
+ raptorDir,
191
+ projectName,
192
+ presets,
193
+ presetLabel,
194
+ allArticleIds,
195
+ primaryAgentId: primaryAgent?.id,
196
+ targetAgents,
197
+ materialized,
198
+ agentsYamlPath,
199
+ manifestPath,
200
+ ciResult,
201
+ hooksResult,
202
+ };
203
+ }
204
+ function installCiWorkflow(cwd) {
205
+ const dir = join(cwd, ".github", "workflows");
206
+ const path = join(dir, "raptor-verify.yml");
207
+ if (existsSync(path)) {
208
+ return { status: "skipped-existing", path };
209
+ }
210
+ mkdirSync(dir, { recursive: true });
211
+ const content = renderBundled("ghaWorkflow", { raptorVersion: VERSION });
212
+ writeFileSync(path, content);
213
+ return { status: "installed", path };
214
+ }
215
+ function installGitHooks(cwd) {
216
+ const gitDir = join(cwd, ".git");
217
+ if (!existsSync(gitDir)) {
218
+ return { status: "skipped-no-git" };
219
+ }
220
+ const hooksDir = join(gitDir, "hooks");
221
+ mkdirSync(hooksDir, { recursive: true });
222
+ const hooks = [
223
+ { name: "pre-commit", key: "preCommitHook" },
224
+ { name: "pre-push", key: "prePushHook" },
225
+ { name: "commit-msg", key: "commitMsgHook" },
226
+ ];
227
+ const installed = [];
228
+ const skipped = [];
229
+ for (const h of hooks) {
230
+ const path = join(hooksDir, h.name);
231
+ if (existsSync(path)) {
232
+ skipped.push(h.name);
233
+ continue;
234
+ }
235
+ const content = renderBundled(h.key, {});
236
+ writeFileSync(path, content);
237
+ chmodSync(path, 0o755);
238
+ installed.push(h.name);
239
+ }
240
+ return { status: "ok", installed, skipped };
241
+ }
242
+ export function describeCi(r) {
243
+ if (r.status === "disabled")
244
+ return "disabled (--no-with-ci)";
245
+ if (r.status === "skipped-existing")
246
+ return `skipped (${r.path} already exists)`;
247
+ return `installed at ${r.path}`;
248
+ }
249
+ export function describeHooks(r) {
250
+ if (r.status === "disabled")
251
+ return "disabled (--no-with-hooks)";
252
+ if (r.status === "skipped-no-git")
253
+ return "skipped (.git/ not found — run 'git init' first)";
254
+ const parts = [];
255
+ if (r.installed.length > 0)
256
+ parts.push(`installed: ${r.installed.join(", ")}`);
257
+ if (r.skipped.length > 0)
258
+ parts.push(`skipped (already exist): ${r.skipped.join(", ")}`);
259
+ return parts.length > 0 ? parts.join("; ") : "nothing to do";
260
+ }
@@ -0,0 +1,54 @@
1
+ export const IDES = [
2
+ { id: "vscode", label: "VS Code" },
3
+ { id: "cursor", label: "Cursor", hint: "IDE multi-modelo" },
4
+ { id: "antigravity", label: "Antigravity", hint: "IDE do Google · Gemini" },
5
+ { id: "jetbrains", label: "JetBrains" },
6
+ { id: "terminal", label: "Terminal / CLI" },
7
+ { id: "other", label: "Outro / manual" },
8
+ ];
9
+ const AIS_BY_IDE = {
10
+ vscode: [
11
+ { id: "claude-code", label: "Claude Code", agentKey: "claude-code", hint: "→ CLAUDE.md" },
12
+ { id: "copilot", label: "GitHub Copilot", agentKey: "copilot", hint: "→ .github/copilot-instructions.md" },
13
+ { id: "gemini", label: "Gemini", agentKey: "gemini", hint: "→ GEMINI.md" },
14
+ { id: "codex", label: "OpenAI Codex", agentKey: "codex", hint: "→ AGENTS.md" },
15
+ ],
16
+ cursor: [
17
+ { id: "cursor", label: "Cursor", agentKey: "cursor", hint: "modelo gerenciado pelo Cursor → .cursor/rules/" },
18
+ ],
19
+ antigravity: [
20
+ { id: "gemini", label: "Gemini", agentKey: "antigravity", hint: "→ AGENTS.md" },
21
+ ],
22
+ jetbrains: [
23
+ { id: "claude-code", label: "Claude Code", agentKey: "claude-code", hint: "→ CLAUDE.md" },
24
+ { id: "copilot", label: "GitHub Copilot", agentKey: "copilot", hint: "→ .github/copilot-instructions.md" },
25
+ ],
26
+ terminal: [
27
+ { id: "claude-code", label: "Claude Code", agentKey: "claude-code", hint: "→ CLAUDE.md" },
28
+ { id: "gemini", label: "Gemini CLI", agentKey: "gemini", hint: "→ GEMINI.md" },
29
+ { id: "codex", label: "OpenAI Codex CLI", agentKey: "codex", hint: "→ AGENTS.md" },
30
+ ],
31
+ other: [
32
+ { id: "claude-code", label: "Claude Code", agentKey: "claude-code", hint: "→ CLAUDE.md" },
33
+ { id: "cursor", label: "Cursor", agentKey: "cursor", hint: "→ .cursor/rules/" },
34
+ { id: "copilot", label: "GitHub Copilot", agentKey: "copilot", hint: "→ .github/copilot-instructions.md" },
35
+ { id: "gemini", label: "Gemini", agentKey: "gemini", hint: "→ GEMINI.md" },
36
+ { id: "codex", label: "OpenAI Codex", agentKey: "codex", hint: "→ AGENTS.md" },
37
+ ],
38
+ };
39
+ export function isKnownIde(ide) {
40
+ return IDES.some((i) => i.id === ide);
41
+ }
42
+ export function aisForIde(ide) {
43
+ return AIS_BY_IDE[ide] ?? [];
44
+ }
45
+ export function resolveAgentKey(ide, ai) {
46
+ return aisForIde(ide).find((a) => a.id === ai)?.agentKey;
47
+ }
48
+ export function referencedAgentKeys() {
49
+ const keys = new Set();
50
+ for (const list of Object.values(AIS_BY_IDE))
51
+ for (const ai of list)
52
+ keys.add(ai.agentKey);
53
+ return [...keys];
54
+ }
@@ -0,0 +1,19 @@
1
+ import { bold, cyan, dim } from "../ui.js";
2
+ const ASCII = [
3
+ "██████╗ █████╗ ██████╗ ████████╗ ██████╗ ██████╗",
4
+ "██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗",
5
+ "██████╔╝███████║██████╔╝ ██║ ██║ ██║██████╔╝",
6
+ "██╔══██╗██╔══██║██╔═══╝ ██║ ██║ ██║██╔══██╗",
7
+ "██║ ██║██║ ██║██║ ██║ ╚██████╔╝██║ ██║",
8
+ "╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝",
9
+ ];
10
+ const ASCII_WIDTH = Math.max(...ASCII.map((l) => l.length));
11
+ export function renderBanner(version) {
12
+ const cols = process.stdout.columns ?? 80;
13
+ if (cols < ASCII_WIDTH + 2) {
14
+ return `${cyan(bold("▲ RAPTOR"))} ${dim(`· AIOS · SDD · v${version}`)}`;
15
+ }
16
+ const art = ASCII.map((line) => cyan(line)).join("\n");
17
+ const tag = `${bold("AIOS")} ${dim(`· Spec-Driven Development · v${version}`)}`;
18
+ return `${art}\n${tag}`;
19
+ }
@@ -0,0 +1,31 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export function detectProjectStack(root) {
4
+ const pkgPath = join(root, "package.json");
5
+ const hasPackageJson = existsSync(pkgPath);
6
+ const hasGit = existsSync(join(root, ".git"));
7
+ let deps = {};
8
+ if (hasPackageJson) {
9
+ try {
10
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
11
+ deps = { ...pkg.dependencies, ...pkg.devDependencies };
12
+ }
13
+ catch {
14
+ }
15
+ }
16
+ const isReactNative = Boolean(deps["react-native"]);
17
+ const isWeb = Boolean(deps["react"]) && !isReactNative;
18
+ const language = existsSync(join(root, "tsconfig.json")) || Boolean(deps["typescript"])
19
+ ? "typescript"
20
+ : "javascript";
21
+ const suggestedPreset = isReactNative ? "mobile-opinionated" : undefined;
22
+ return {
23
+ hasPackageJson,
24
+ hasGit,
25
+ isReactNative,
26
+ isWeb,
27
+ language,
28
+ suggestedPreset,
29
+ isBrownfield: hasPackageJson,
30
+ };
31
+ }
@@ -0,0 +1,18 @@
1
+ export const ONBOARD = [
2
+ "O RAPTOR transforma uma ideia em linguagem natural numa especificação",
3
+ "rica e sem pontas soltas — e conduz, fase a fase, até o código",
4
+ "verificado, sempre com um humano no comando das aprovações.",
5
+ "",
6
+ "Ele não escreve a spec nem o código sozinho: orquestra o ciclo, aplica",
7
+ "gates que bloqueiam transições incompletas, mantém uma trilha de",
8
+ "auditoria e materializa slash commands para o seu agente de IA.",
9
+ "",
10
+ "Diferencial — a ponta de verificação: o RAPTOR mede a realidade do app",
11
+ "(acessibilidade, performance, permissões de loja, versões de SO) e",
12
+ "compara com o que a spec prometeu.",
13
+ "",
14
+ "Ciclo: new → specify → clarify → plan → tasks → implement → verify",
15
+ ].join("\n");
16
+ export function renderOnboard() {
17
+ return ONBOARD;
18
+ }
@@ -0,0 +1,93 @@
1
+ import { performInit, } from "../init-core.js";
2
+ import { readConfig, writeConfig } from "../project.js";
3
+ import { resolveAgentKey } from "./agent-resolver.js";
4
+ export function buildSetupPlan(answers, ctx) {
5
+ const primaryAgentKey = resolveAgentKey(answers.ide, answers.ai);
6
+ if (!primaryAgentKey) {
7
+ throw new Error(`Nenhuma integração de agente mapeia IDE "${answers.ide}" + IA "${answers.ai}".`);
8
+ }
9
+ return {
10
+ cwd: ctx.cwd,
11
+ projectName: answers.projectName,
12
+ preset: answers.preset,
13
+ primaryAgentKey,
14
+ startingPoint: answers.startingPoint,
15
+ noGit: false,
16
+ jiraLater: answers.jiraLater,
17
+ figmaLater: answers.figmaLater,
18
+ force: ctx.force,
19
+ };
20
+ }
21
+ function worthPreserving(block) {
22
+ return (!!block &&
23
+ typeof block === "object" &&
24
+ !Array.isArray(block) &&
25
+ (block.enabled === true || Object.keys(block).some((k) => k !== "enabled")));
26
+ }
27
+ export function applySetupPlan(plan, logger) {
28
+ const prior = readConfig(plan.cwd);
29
+ const priorJira = worthPreserving(prior.jira) ? prior.jira : undefined;
30
+ const priorDesign = worthPreserving(prior.design) ? prior.design : undefined;
31
+ const priorGit = prior.git;
32
+ const priorGates = prior.gates;
33
+ const init = performInit({
34
+ cwd: plan.cwd,
35
+ projectName: plan.projectName ?? prior.project?.name,
36
+ preset: plan.preset,
37
+ ai: plan.primaryAgentKey,
38
+ script: "sh",
39
+ branchNumbering: "sequential",
40
+ noGit: plan.noGit,
41
+ withCi: true,
42
+ withHooks: true,
43
+ force: plan.force,
44
+ auditCommand: "raptor setup",
45
+ }, logger);
46
+ const cfg = readConfig(plan.cwd);
47
+ let changed = false;
48
+ if (priorGit) {
49
+ cfg.git = { ...cfg.git, ...priorGit };
50
+ changed = true;
51
+ }
52
+ if (priorGates) {
53
+ cfg.gates = { ...cfg.gates, ...priorGates };
54
+ changed = true;
55
+ }
56
+ if (priorJira) {
57
+ cfg.jira = priorJira;
58
+ changed = true;
59
+ }
60
+ if (priorDesign) {
61
+ cfg.design = priorDesign;
62
+ changed = true;
63
+ }
64
+ let jiraConfigured = false;
65
+ let figmaConfigured = false;
66
+ if (plan.jiraLater && !cfg.jira?.enabled) {
67
+ cfg.jira = {
68
+ ...cfg.jira,
69
+ enabled: false,
70
+ provider: cfg.jira?.provider ?? "atlassian-oauth",
71
+ };
72
+ jiraConfigured = true;
73
+ changed = true;
74
+ }
75
+ if (plan.figmaLater && !cfg.design?.enabled) {
76
+ cfg.design = {
77
+ ...cfg.design,
78
+ enabled: false,
79
+ provider: cfg.design?.provider ?? "figma-rest",
80
+ };
81
+ figmaConfigured = true;
82
+ changed = true;
83
+ }
84
+ if (changed)
85
+ writeConfig(plan.cwd, cfg);
86
+ return {
87
+ init,
88
+ jiraConfigured,
89
+ figmaConfigured,
90
+ jiraPreserved: Boolean(priorJira),
91
+ figmaPreserved: Boolean(priorDesign),
92
+ };
93
+ }
@@ -0,0 +1,164 @@
1
+ import * as p from "@clack/prompts";
2
+ import { basename } from "node:path";
3
+ import { knownPresetIds, VERSION } from "../../_core/dist/index.js";
4
+ import { applySetupPlan, buildSetupPlan, } from "./plan.js";
5
+ import { aisForIde, IDES } from "./agent-resolver.js";
6
+ import { detectProjectStack } from "./detect-stack.js";
7
+ import { renderBanner } from "./banner.js";
8
+ import { renderOnboard } from "./onboard.js";
9
+ import { describeCi, describeHooks, } from "../init-core.js";
10
+ export async function runSetupWizard(ctx) {
11
+ console.log(`\n${renderBanner(VERSION)}\n`);
12
+ p.intro("Configuração guiada do projeto");
13
+ if (!ctx.skipIntro) {
14
+ const see = await p.confirm({
15
+ message: "Ver uma introdução rápida (30s) ao RAPTOR?",
16
+ initialValue: true,
17
+ });
18
+ if (p.isCancel(see))
19
+ return cancelled();
20
+ if (see)
21
+ p.note(renderOnboard(), "O que é o RAPTOR AIOS");
22
+ }
23
+ const stack = detectProjectStack(ctx.cwd);
24
+ const startingPoint = await p.select({
25
+ message: "Este projeto é...",
26
+ options: [
27
+ {
28
+ value: "existing",
29
+ label: "Já existente",
30
+ hint: stack.isBrownfield ? "código detectado aqui" : undefined,
31
+ },
32
+ { value: "scratch", label: "Do zero" },
33
+ ],
34
+ initialValue: stack.isBrownfield ? "existing" : "scratch",
35
+ });
36
+ if (p.isCancel(startingPoint))
37
+ return cancelled();
38
+ const ide = await p.select({
39
+ message: "Qual seu ambiente?",
40
+ options: IDES.map((i) => ({ value: i.id, label: i.label, hint: i.hint })),
41
+ initialValue: ctx.defaults?.ide,
42
+ });
43
+ if (p.isCancel(ide))
44
+ return cancelled();
45
+ const ais = aisForIde(ide);
46
+ let ai;
47
+ if (ais.length === 1) {
48
+ ai = ais[0].id;
49
+ p.log.info(`Assistente: ${ais[0].label}`);
50
+ }
51
+ else {
52
+ const picked = await p.select({
53
+ message: "Qual assistente de IA?",
54
+ options: ais.map((a) => ({ value: a.id, label: a.label, hint: a.hint })),
55
+ initialValue: ctx.defaults?.ai,
56
+ });
57
+ if (p.isCancel(picked))
58
+ return cancelled();
59
+ ai = picked;
60
+ }
61
+ const presetIds = knownPresetIds();
62
+ const suggested = stack.suggestedPreset ?? "mobile-opinionated";
63
+ const preset = await p.select({
64
+ message: "Preset da constituição",
65
+ options: presetIds.map((id) => ({
66
+ value: id,
67
+ label: id,
68
+ hint: id === suggested ? "sugerido para sua stack" : undefined,
69
+ })),
70
+ initialValue: ctx.defaults?.preset ?? suggested,
71
+ });
72
+ if (p.isCancel(preset))
73
+ return cancelled();
74
+ const jiraLater = await p.confirm({
75
+ message: "Deixar o Jira pronto para conectar depois?",
76
+ initialValue: ctx.defaults?.jiraLater ?? false,
77
+ });
78
+ if (p.isCancel(jiraLater))
79
+ return cancelled();
80
+ const figmaLater = await p.confirm({
81
+ message: "Deixar o Figma pronto para conectar depois?",
82
+ initialValue: ctx.defaults?.figmaLater ?? false,
83
+ });
84
+ if (p.isCancel(figmaLater))
85
+ return cancelled();
86
+ const answers = {
87
+ startingPoint,
88
+ projectName: ctx.defaults?.projectName,
89
+ ide,
90
+ ai,
91
+ preset,
92
+ jiraLater,
93
+ figmaLater,
94
+ };
95
+ let plan;
96
+ try {
97
+ plan = buildSetupPlan(answers, { cwd: ctx.cwd, force: ctx.force });
98
+ }
99
+ catch (err) {
100
+ p.cancel(err instanceof Error ? err.message : String(err));
101
+ return "failed";
102
+ }
103
+ p.note([
104
+ `Projeto: ${plan.projectName ?? basename(ctx.cwd)}`,
105
+ `Preset: ${plan.preset}`,
106
+ `Agente: ${plan.primaryAgentKey}`,
107
+ `Jira: ${plan.jiraLater ? "configurar depois" : "pular"}`,
108
+ `Figma: ${plan.figmaLater ? "configurar depois" : "pular"}`,
109
+ ].join("\n"), "Revisão");
110
+ const go = await p.confirm({ message: "Aplicar agora?" });
111
+ if (p.isCancel(go))
112
+ return cancelled();
113
+ if (!go) {
114
+ p.outro("Nada foi alterado.");
115
+ return "declined";
116
+ }
117
+ const s = p.spinner();
118
+ s.start("Inicializando o projeto Raptor…");
119
+ const warnings = [];
120
+ const logger = {
121
+ log: () => { },
122
+ warn: (m) => warnings.push(typeof m === "string" ? m : String(m)),
123
+ };
124
+ let result;
125
+ try {
126
+ result = applySetupPlan(plan, logger);
127
+ }
128
+ catch (err) {
129
+ s.stop("Falhou.");
130
+ p.cancel(err instanceof Error ? err.message : String(err));
131
+ return "failed";
132
+ }
133
+ s.stop("Projeto inicializado.");
134
+ for (const w of warnings)
135
+ p.log.warn(w);
136
+ const matLine = result.init.materialized
137
+ .map((m) => `${m.agentId} (${m.count})`)
138
+ .join(", ") || "(nenhum)";
139
+ p.note([
140
+ `Comandos: ${matLine}`,
141
+ `CI: ${describeCi(result.init.ciResult)}`,
142
+ `Hooks: ${describeHooks(result.init.hooksResult)}`,
143
+ result.jiraPreserved
144
+ ? "Jira: config existente preservada"
145
+ : result.jiraConfigured
146
+ ? "Jira: pronto (raptor jira connect)"
147
+ : "",
148
+ result.figmaPreserved
149
+ ? "Figma: config existente preservada"
150
+ : result.figmaConfigured
151
+ ? "Figma: pronto (raptor design connect)"
152
+ : "",
153
+ ]
154
+ .filter(Boolean)
155
+ .join("\n"), "Pronto");
156
+ p.outro(plan.startingPoint === "existing"
157
+ ? "Próximo: 'raptor new <slug>' para especificar uma feature do código existente."
158
+ : "Próximo: 'raptor new <slug>' para criar sua primeira feature.");
159
+ return "completed";
160
+ }
161
+ function cancelled() {
162
+ p.cancel("Setup cancelado.");
163
+ return "cancelled";
164
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raptor-aios",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Raptor — Spec-Driven Development (SDD) CLI for modern mobile apps. Constitutional gates, audit trail, real verification (a11y/perf/stores/OS matrix), and AI-agent slash commands.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,6 +53,7 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
+ "@clack/prompts": "^1.5.1",
56
57
  "@modelcontextprotocol/sdk": "^1.29.0",
57
58
  "@oclif/core": "^4",
58
59
  "ajv": "^8.18.0",
@@ -29,7 +29,7 @@ const CLI = join(ROOT, "packages", "cli");
29
29
  const CORE = join(ROOT, "packages", "core");
30
30
  const OUT = join(ROOT, "build", "npm");
31
31
 
32
- const VERSION = "0.11.0";
32
+ const VERSION = "0.12.1";
33
33
 
34
34
  function log(msg) {
35
35
  process.stdout.write(` ${msg}\n`);
@@ -177,6 +177,7 @@ const pkg = {
177
177
  bugs: { url: "https://github.com/lucaspedronet/raptor-aios/issues" },
178
178
  publishConfig: { access: "public" },
179
179
  dependencies: {
180
+ "@clack/prompts": "^1.5.1",
180
181
  "@modelcontextprotocol/sdk": "^1.29.0",
181
182
  "@oclif/core": "^4",
182
183
  ajv: "^8.18.0",