@guilhermefsousa/open-spec-kit 0.0.9 → 0.0.11

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 (28) hide show
  1. package/README.md +1 -1
  2. package/bin/open-spec-kit.js +7 -0
  3. package/package.json +1 -1
  4. package/src/commands/doctor.js +107 -197
  5. package/src/commands/init.js +112 -347
  6. package/src/commands/install.js +393 -0
  7. package/src/commands/update.js +117 -165
  8. package/src/schemas/spec.schema.js +3 -3
  9. package/src/utils/global-path.js +73 -0
  10. package/templates/agents/agents/spec-hub.agent.md +13 -13
  11. package/templates/agents/rules/hub_structure.instructions.md +1 -1
  12. package/templates/agents/rules/ownership.instructions.md +39 -39
  13. package/templates/agents/skills/dev-orchestrator/SKILL.md +17 -17
  14. package/templates/agents/skills/discovery/SKILL.md +17 -17
  15. package/templates/agents/skills/setup-project/SKILL.md +15 -15
  16. package/templates/agents/skills/specifying-features/SKILL.md +28 -28
  17. package/templates/github/agents/spec-hub.agent.md +5 -5
  18. package/templates/github/copilot-instructions.md +9 -9
  19. package/templates/github/instructions/hub_structure.instructions.md +1 -1
  20. package/templates/github/instructions/ownership.instructions.md +9 -9
  21. package/templates/github/skills/dev-orchestrator/SKILL.md +619 -5
  22. package/templates/github/skills/discovery/SKILL.md +419 -5
  23. package/templates/github/skills/setup-project/SKILL.md +496 -5
  24. package/templates/github/skills/specifying-features/SKILL.md +417 -5
  25. /package/templates/github/prompts/{dev.prompt.md → osk-build.prompt.md} +0 -0
  26. /package/templates/github/prompts/{discovery.prompt.md → osk-discover.prompt.md} +0 -0
  27. /package/templates/github/prompts/{setup.prompt.md → osk-init.prompt.md} +0 -0
  28. /package/templates/github/prompts/{nova-feature.prompt.md → osk-spec.prompt.md} +0 -0
@@ -1,188 +1,158 @@
1
1
  import chalk from 'chalk';
2
2
  import ora from 'ora';
3
- import { readFile, writeFile, access, mkdir, readdir } from 'fs/promises';
4
- import { join } from 'path';
5
- import yaml from 'yaml';
3
+ import { readFile, writeFile, access, mkdir, readdir, cp } from 'fs/promises';
4
+ import { join, dirname } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { GLOBAL_DIR, getGlobalPath } from '../utils/global-path.js';
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates');
6
10
 
7
11
  export async function updateCommand() {
8
12
  console.log(chalk.bold('\n open-spec-kit update\n'));
9
13
 
10
- const cwd = process.cwd();
11
- const spinner = ora('Lendo projects.yml...').start();
14
+ // Check global install exists
15
+ try {
16
+ await access(GLOBAL_DIR);
17
+ } catch {
18
+ console.log(chalk.red(` Instalação global não encontrada (${GLOBAL_DIR})`));
19
+ console.log(chalk.dim(' Execute "open-spec-kit install" primeiro.\n'));
20
+ return;
21
+ }
22
+
23
+ const spinner = ora('Atualizando skills e rules globais...').start();
12
24
 
13
25
  try {
14
- const projectsRaw = await readFile(join(cwd, 'projects.yml'), 'utf-8');
15
- const projects = parseProjectsYaml(projectsRaw);
26
+ // Phase 1: Re-copy templates from npm package → global
27
+ spinner.text = 'Copiando templates atualizados...';
16
28
 
17
- if (projects.agents.length === 0) {
18
- spinner.fail('projects.yml não contém campo "agents". Execute "open-spec-kit init" primeiro.');
19
- return;
29
+ const agentsSource = join(TEMPLATES_DIR, 'agents');
30
+ for (const subdir of ['skills', 'rules', 'agents', 'scripts']) {
31
+ const src = join(agentsSource, subdir);
32
+ const dest = getGlobalPath(subdir);
33
+ try {
34
+ await access(src);
35
+ await cp(src, dest, { recursive: true, force: true });
36
+ } catch { /* subdir may not exist in templates */ }
20
37
  }
21
38
 
22
- spinner.succeed(`Agents configurados: ${projects.agents.join(', ')}`);
23
-
24
- // Check source of truth files exist
25
- const sourceFiles = [
26
- '.agents/agents/spec-hub.agent.md',
27
- '.agents/rules/ownership.instructions.md',
28
- '.agents/rules/hub_structure.instructions.md',
39
+ spinner.succeed('Skills e rules globais atualizados');
40
+
41
+ // Phase 2: Sync Claude Copilot mirrors
42
+ const syncSpinner = ora('Sincronizando mirrors Copilot...').start();
43
+ let synced = 0;
44
+
45
+ // Sync rules with Copilot frontmatter
46
+ const syncPairs = [
47
+ {
48
+ source: getGlobalPath('rules', 'ownership.instructions.md'),
49
+ target: getGlobalPath('github', 'instructions', 'ownership.instructions.md'),
50
+ label: 'ownership rules',
51
+ transform: (content) => addCopilotFrontmatter(content, 'specs/**,docs/**,projects.yml')
52
+ },
53
+ {
54
+ source: getGlobalPath('rules', 'hub_structure.instructions.md'),
55
+ target: getGlobalPath('github', 'instructions', 'hub_structure.instructions.md'),
56
+ label: 'structure rules',
57
+ transform: (content) => addCopilotFrontmatter(content, 'specs/**,docs/**,projects.yml')
58
+ },
29
59
  ];
30
60
 
31
- const missingSource = [];
32
- for (const f of sourceFiles) {
61
+ for (const { source, target, label, transform } of syncPairs) {
33
62
  try {
34
- await access(join(cwd, f));
35
- } catch {
36
- missingSource.push(f);
63
+ const sourceContent = await readFile(source, 'utf-8');
64
+ const targetContent = transform(sourceContent);
65
+
66
+ let existingTarget = '';
67
+ try {
68
+ existingTarget = await readFile(target, 'utf-8');
69
+ } catch { /* target doesn't exist yet */ }
70
+
71
+ if (existingTarget !== targetContent) {
72
+ await mkdir(getGlobalPath('github', 'instructions'), { recursive: true });
73
+ await writeFile(target, targetContent);
74
+ synced++;
75
+ console.log(chalk.green(` ✓ ${label} sincronizado`));
76
+ }
77
+ } catch (err) {
78
+ console.log(chalk.yellow(` ⚠ Não foi possível sincronizar ${label}: ${err.message}`));
37
79
  }
38
80
  }
39
81
 
40
- if (missingSource.length > 0) {
41
- console.log(chalk.yellow('\n Arquivos fonte ausentes (source of truth):'));
42
- missingSource.forEach(f => console.log(chalk.dim(` - ${f}`)));
43
- console.log(chalk.dim(' Execute "open-spec-kit init" para gerar.'));
44
- return;
45
- }
82
+ // Sync skills
83
+ try {
84
+ const skillsDir = getGlobalPath('skills');
85
+ const skillEntries = await readdir(skillsDir, { withFileTypes: true });
86
+ const skillDirs = skillEntries.filter(e => e.isDirectory()).map(e => e.name);
87
+
88
+ for (const skillName of skillDirs) {
89
+ const source = join(skillsDir, skillName, 'SKILL.md');
90
+ const target = getGlobalPath('github', 'skills', skillName, 'SKILL.md');
46
91
 
47
- // Sync Copilot files if configured
48
- if (projects.agents.includes('copilot')) {
49
- const syncSpinner = ora('Sincronizando arquivos Copilot...').start();
50
-
51
- const syncPairs = [
52
- {
53
- source: '.agents/rules/ownership.instructions.md',
54
- target: '.github/instructions/ownership.instructions.md',
55
- label: 'ownership rules',
56
- transform: (content) => addCopilotFrontmatter(content, 'specs/**,docs/**,projects.yml,.agents/**')
57
- },
58
- {
59
- source: '.agents/rules/hub_structure.instructions.md',
60
- target: '.github/instructions/hub_structure.instructions.md',
61
- label: 'structure rules',
62
- transform: (content) => addCopilotFrontmatter(content, 'specs/**,docs/**,projects.yml')
63
- },
64
- ];
65
-
66
- let synced = 0;
67
- for (const { source, target, label, transform } of syncPairs) {
68
92
  try {
69
- const sourceContent = await readFile(join(cwd, source), 'utf-8');
70
- const targetContent = transform(sourceContent);
93
+ const sourceContent = await readFile(source, 'utf-8');
71
94
 
72
95
  let existingTarget = '';
73
96
  try {
74
- existingTarget = await readFile(join(cwd, target), 'utf-8');
97
+ existingTarget = await readFile(target, 'utf-8');
75
98
  } catch { /* target doesn't exist yet */ }
76
99
 
77
- if (existingTarget !== targetContent) {
78
- syncSpinner.text = `Atualizando ${label}...`;
79
- await mkdir(join(cwd, '.github/instructions'), { recursive: true });
80
- await writeFile(join(cwd, target), targetContent);
100
+ if (existingTarget !== sourceContent) {
101
+ await mkdir(getGlobalPath('github', 'skills', skillName), { recursive: true });
102
+ await writeFile(target, sourceContent);
81
103
  synced++;
82
- console.log(chalk.green(` ✓ ${label} sincronizado`));
104
+ console.log(chalk.green(` ✓ skill ${skillName} sincronizado`));
83
105
  }
84
106
  } catch (err) {
85
- console.log(chalk.yellow(` ⚠ Não foi possível sincronizar ${label}: ${err.message}`));
86
- }
87
- }
88
-
89
- if (synced > 0) {
90
- syncSpinner.succeed(`${synced} arquivo(s) Copilot atualizado(s)`);
91
- } else {
92
- syncSpinner.succeed('Arquivos Copilot já estão sincronizados');
93
- }
94
-
95
- // Sync skills: .agents/skills/*/SKILL.md → .github/skills/*/SKILL.md
96
- const skillsSpinner = ora('Sincronizando skills...').start();
97
- let skillsSynced = 0;
98
- try {
99
- const skillsDir = join(cwd, '.agents', 'skills');
100
- const skillEntries = await readdir(skillsDir, { withFileTypes: true });
101
- const skillDirs = skillEntries.filter(e => e.isDirectory()).map(e => e.name);
102
-
103
- for (const skillName of skillDirs) {
104
- const source = join(skillsDir, skillName, 'SKILL.md');
105
- const target = join(cwd, '.github', 'skills', skillName, 'SKILL.md');
106
-
107
- try {
108
- const sourceContent = await readFile(source, 'utf-8');
109
-
110
- let existingTarget = '';
111
- try {
112
- existingTarget = await readFile(target, 'utf-8');
113
- } catch { /* target doesn't exist yet */ }
114
-
115
- if (existingTarget !== sourceContent) {
116
- await mkdir(join(cwd, '.github', 'skills', skillName), { recursive: true });
117
- await writeFile(target, sourceContent);
118
- skillsSynced++;
119
- console.log(chalk.green(` ✓ skill ${skillName} sincronizado`));
120
- }
121
- } catch (err) {
122
- if (err.code !== 'ENOENT') {
123
- console.log(chalk.yellow(` ⚠ Não foi possível sincronizar skill ${skillName}: ${err.message}`));
124
- }
107
+ if (err.code !== 'ENOENT') {
108
+ console.log(chalk.yellow(` ⚠ Não foi possível sincronizar skill ${skillName}: ${err.message}`));
125
109
  }
126
110
  }
127
-
128
- if (skillsSynced > 0) {
129
- skillsSpinner.succeed(`${skillsSynced} skill(s) Copilot atualizado(s)`);
130
- } else {
131
- skillsSpinner.succeed('Skills Copilot já estão sincronizados');
132
- }
133
- } catch (err) {
134
- if (err.code === 'ENOENT') {
135
- skillsSpinner.warn('Diretório .agents/skills/ não encontrado — skills não sincronizados');
136
- } else {
137
- skillsSpinner.warn(`Erro ao sincronizar skills: ${err.message}`);
138
- }
139
111
  }
140
- }
141
-
142
- // Check MCP configs
143
- if (projects.agents.includes('claude')) {
144
- try {
145
- await access(join(cwd, '.mcp.json'));
146
- console.log(chalk.green(' ✓ .mcp.json (Claude Code) encontrado'));
147
- } catch {
148
- console.log(chalk.yellow(' ✗ .mcp.json (Claude Code) ausente — crie manualmente'));
112
+ } catch (err) {
113
+ if (err.code === 'ENOENT') {
114
+ console.log(chalk.yellow(' ⚠ Diretório skills/ global não encontrado'));
149
115
  }
150
116
  }
151
117
 
152
- if (projects.agents.includes('copilot')) {
153
- try {
154
- await access(join(cwd, '.vscode/mcp.json'));
155
- console.log(chalk.green(' .vscode/mcp.json (Copilot) encontrado'));
156
- } catch {
157
- console.log(chalk.yellow(' ✗ .vscode/mcp.json (Copilot) ausente — execute init'));
158
- }
159
- }
118
+ // Sync agent hub
119
+ try {
120
+ const claudeAgent = await readFile(getGlobalPath('agents', 'spec-hub.agent.md'), 'utf-8');
121
+ const copilotAgentPath = getGlobalPath('github', 'agents', 'spec-hub.agent.md');
160
122
 
161
- // Check for spec-hub agent divergence between Claude and Copilot
162
- if (projects.agents.includes('claude') && projects.agents.includes('copilot')) {
123
+ // Copy Copilot version from templates (has extra frontmatter)
124
+ const copilotTemplate = join(TEMPLATES_DIR, 'github', 'agents', 'spec-hub.agent.md');
163
125
  try {
164
- const claudeAgentRaw = await readFile(join(cwd, '.agents/agents/spec-hub.agent.md'), 'utf-8');
165
- const copilotAgentRaw = await readFile(join(cwd, '.github/agents/spec-hub.agent.md'), 'utf-8');
166
-
167
- // Strip frontmatter (--- ... ---) from both to compare functional content
168
- const stripFrontmatter = (content) => content.replace(/^---\n[\s\S]*?\n---\n+/, '').trim();
169
- const claudeBody = stripFrontmatter(claudeAgentRaw);
170
- const copilotBody = stripFrontmatter(copilotAgentRaw);
171
-
172
- if (claudeBody === copilotBody) {
173
- console.log(chalk.green(' ✓ spec-hub agent sincronizado entre Claude e Copilot'));
174
- } else {
175
- console.log(chalk.yellow(' ⚠ spec-hub agent diverge entre Claude (.agents/) e Copilot (.github/)'));
176
- console.log(chalk.dim(' Atualize manualmente: o conteúdo funcional diferente entre plataformas.'));
177
- console.log(chalk.dim(' Claude: .agents/agents/spec-hub.agent.md'));
178
- console.log(chalk.dim(' Copilot: .github/agents/spec-hub.agent.md'));
126
+ await access(copilotTemplate);
127
+ const copilotContent = await readFile(copilotTemplate, 'utf-8');
128
+ let existing = '';
129
+ try { existing = await readFile(copilotAgentPath, 'utf-8'); } catch { /* */ }
130
+ if (existing !== copilotContent) {
131
+ await mkdir(getGlobalPath('github', 'agents'), { recursive: true });
132
+ await writeFile(copilotAgentPath, copilotContent);
133
+ synced++;
134
+ console.log(chalk.green(' ✓ spec-hub agent (Copilot) sincronizado'));
179
135
  }
180
- } catch {
181
- console.log(chalk.yellow(' ⚠ Não foi possível verificar divergência dos agent files'));
182
- }
136
+ } catch { /* template not found */ }
137
+ } catch { /* ignore */ }
138
+
139
+ if (synced > 0) {
140
+ syncSpinner.succeed(`${synced} arquivo(s) Copilot atualizado(s)`);
141
+ } else {
142
+ syncSpinner.succeed('Tudo já sincronizado');
183
143
  }
184
144
 
185
- console.log('');
145
+ // Update version.json
146
+ try {
147
+ const pkgPath = join(__dirname, '..', '..', 'package.json');
148
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
149
+ await writeFile(getGlobalPath('version.json'), JSON.stringify({
150
+ version: pkg.version,
151
+ installedAt: new Date().toISOString(),
152
+ }, null, 2) + '\n');
153
+ } catch { /* ignore version write failure */ }
154
+
155
+ console.log(chalk.dim(`\n Diretório global: ${GLOBAL_DIR}\n`));
186
156
 
187
157
  } catch (err) {
188
158
  spinner.fail(`Erro: ${err.message}`);
@@ -191,24 +161,6 @@ export async function updateCommand() {
191
161
  }
192
162
 
193
163
  function addCopilotFrontmatter(content, applyTo) {
194
- // Remove existing frontmatter if present
195
164
  const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, '');
196
165
  return `---\napplyTo: "${applyTo}"\n---\n\n${withoutFrontmatter.trim()}\n`;
197
166
  }
198
-
199
- /**
200
- * Parse projects.yml using the yaml package (robust — handles all valid YAML).
201
- */
202
- function parseProjectsYaml(content) {
203
- try {
204
- const data = yaml.parse(content);
205
- const agents = data?.project?.agents;
206
- return {
207
- agents: Array.isArray(agents) ? agents : [],
208
- preset: data?.project?.preset || 'standard',
209
- name: data?.project?.name || '',
210
- };
211
- } catch {
212
- return { agents: [], preset: 'standard', name: '' };
213
- }
214
- }
@@ -457,7 +457,7 @@ export function rule21_taskReposMatchProjects(tasks, repoNames) {
457
457
  */
458
458
  export function rule22_auditReportClean(auditContent) {
459
459
  if (auditContent === null) {
460
- return result(22, true, 'WARNING', `audit-report.md not found (generated by /spec)`);
460
+ return result(22, true, 'WARNING', `audit-report.md not found (generated by /osk-spec)`);
461
461
  }
462
462
  const hasFail = auditContent.includes('❌');
463
463
  return result(22, !hasFail, 'ERROR',
@@ -517,7 +517,7 @@ export function rule28_noUnresolvedMarkers(contents) {
517
517
  return result(28, unique.length === 0, 'ERROR',
518
518
  unique.length === 0
519
519
  ? `No unresolved markers found`
520
- : `Unresolved markers found: ${unique.join(', ')} — resolve before /dev`,
520
+ : `Unresolved markers found: ${unique.join(', ')} — resolve before /osk-build`,
521
521
  unique.length > 0 ? unique : undefined,
522
522
  );
523
523
  }
@@ -624,7 +624,7 @@ export function rule33_openDecisions(architectureContent) {
624
624
  const blocking = [];
625
625
 
626
626
  for (const line of lines) {
627
- if (/\|\s*aberta\s*\|/i.test(line) && /\/spec|\/dev/i.test(line)) {
627
+ if (/\|\s*aberta\s*\|/i.test(line) && /\/osk-spec|\/osk-build/i.test(line)) {
628
628
  const cells = line.split('|').map(c => c.trim()).filter(Boolean);
629
629
  blocking.push(cells[1] || line.trim());
630
630
  }
@@ -0,0 +1,73 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { readFile } from 'fs/promises';
4
+
5
+ export const GLOBAL_DIR_NAME = '.open-spec-kit';
6
+ export const GLOBAL_DIR = join(homedir(), GLOBAL_DIR_NAME);
7
+
8
+ export function getGlobalPath(...segments) {
9
+ return join(GLOBAL_DIR, ...segments);
10
+ }
11
+
12
+ export function getGlobalPathPosix(...segments) {
13
+ return getGlobalPath(...segments).replace(/\\/g, '/');
14
+ }
15
+
16
+ export function getGlobalEnvPath() {
17
+ return getGlobalPath('.env');
18
+ }
19
+
20
+ /**
21
+ * Parse a .env file into a key-value object.
22
+ * Handles comments, empty lines, and quoted values.
23
+ */
24
+ export function parseEnvContent(content) {
25
+ const env = {};
26
+ for (const line of content.split('\n')) {
27
+ const trimmed = line.trim();
28
+ if (!trimmed || trimmed.startsWith('#')) continue;
29
+ const eqIdx = trimmed.indexOf('=');
30
+ if (eqIdx === -1) continue;
31
+ const key = trimmed.slice(0, eqIdx).trim();
32
+ let value = trimmed.slice(eqIdx + 1).trim();
33
+ // Strip surrounding quotes
34
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
35
+ value = value.slice(1, -1);
36
+ }
37
+ env[key] = value;
38
+ }
39
+ return env;
40
+ }
41
+
42
+ /**
43
+ * Read and parse the global .env file.
44
+ */
45
+ export async function readGlobalEnv() {
46
+ const content = await readFile(getGlobalEnvPath(), 'utf-8');
47
+ return parseEnvContent(content);
48
+ }
49
+
50
+ /**
51
+ * Parse JSON that may contain comments (JSONC, used by VS Code settings).
52
+ * Respects quoted strings so URLs like "https://..." are not corrupted.
53
+ */
54
+ export function parseJsonc(text) {
55
+ let result = '';
56
+ let i = 0;
57
+ while (i < text.length) {
58
+ if (text[i] === '"') {
59
+ const start = i++;
60
+ while (i < text.length && text[i] !== '"') { if (text[i] === '\\') i++; i++; }
61
+ result += text.slice(start, ++i);
62
+ } else if (text[i] === '/' && text[i + 1] === '/') {
63
+ while (i < text.length && text[i] !== '\n') i++;
64
+ } else if (text[i] === '/' && text[i + 1] === '*') {
65
+ i += 2;
66
+ while (i < text.length && !(text[i] === '*' && text[i + 1] === '/')) i++;
67
+ i += 2;
68
+ } else {
69
+ result += text[i++];
70
+ }
71
+ }
72
+ return JSON.parse(result.replace(/,\s*([}\]])/g, '$1'));
73
+ }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec-agent
3
- description: "Orquestra o ciclo de especificação: lê contexto do projeto, invoca as skills (/setup, /discovery, /spec, /dev) e coordena agents do Labs quando necessário."
3
+ description: "Orquestra o ciclo de especificação: lê contexto do projeto, invoca as skills (/osk-init, /osk-discover, /osk-spec, /osk-build) e coordena agents do Labs quando necessário."
4
4
  user-invocable: true
5
5
  disable-model-invocation: false
6
6
  ---
@@ -25,10 +25,10 @@ Nunca gere specs sem ter lido o acima.
25
25
 
26
26
  | Skill | Comando | O que faz |
27
27
  |-------|---------|-----------|
28
- | Setup | `/setup @space_key` ou `/setup @page_id` | Bootstrap do projeto a partir do Confluence |
29
- | Discovery | `/discovery` | Análise de demanda, Q&A com PO, gera PRD |
30
- | Spec | `/spec` | Quebra PRD em specs técnicas (brief, cenários, contratos, tasks) |
31
- | Dev | `/dev NNN` | Orquestra implementação (TDD, MR, security, docs vivas) |
28
+ | Setup | `/osk-init @space_key` ou `/osk-init @page_id` | Bootstrap do projeto a partir do Confluence |
29
+ | Discovery | `/osk-discover` | Análise de demanda, Q&A com PO, gera PRD |
30
+ | Spec | `/osk-spec` | Quebra PRD em specs técnicas (brief, cenários, contratos, tasks) |
31
+ | Dev | `/osk-build NNN` | Orquestra implementação (TDD, MR, security, docs vivas) |
32
32
 
33
33
  As instruções completas de cada skill estão em `.agents/skills/*/SKILL.md`.
34
34
 
@@ -36,7 +36,7 @@ As instruções completas de cada skill estão em `.agents/skills/*/SKILL.md`.
36
36
 
37
37
  | Responsabilidade | Quem faz |
38
38
  |-----------------|----------|
39
- | Specs (brief, scenarios, contracts, tasks, links) | **spec-agent** via `/spec` |
39
+ | Specs (brief, scenarios, contracts, tasks, links) | **spec-agent** via `/osk-spec` |
40
40
  | ADRs (`docs/decisions/`) | **spec-agent** |
41
41
  | Design Document (DD) no Confluence | **`design-doc`** (Labs agent) — fallback: spec-agent via MCP direto |
42
42
  | Implementação de código | **`dotnet-engineer` / `nodejs-engineer` / `java-engineer`** (Labs) |
@@ -60,12 +60,12 @@ As instruções completas de cada skill estão em `.agents/skills/*/SKILL.md`.
60
60
 
61
61
  ```
62
62
  Projeto (Space ou página raiz)
63
- ├── 🎯 Visão do Produto ← /setup gera
64
- ├── 📖 Glossário ← /setup gera | /discovery e /dev atualizam
65
- ├── 📐 DD (Design Document) ← design-doc agent gera | /dev atualiza
63
+ ├── 🎯 Visão do Produto ← /osk-init gera
64
+ ├── 📖 Glossário ← /osk-init gera | /osk-discover e /osk-build atualizam
65
+ ├── 📐 DD (Design Document) ← design-doc agent gera | /osk-build atualiza
66
66
  ├── Demandas/ ← PO joga docs aqui
67
- ├── 🚀 Features/ ← /setup cria | /discovery escreve dúvidas + PRD
68
- ├── 🏛️ Domínio/ ← /setup gera | /dev atualiza
67
+ ├── 🚀 Features/ ← /osk-init cria | /osk-discover escreve dúvidas + PRD
68
+ ├── 🏛️ Domínio/ ← /osk-init gera | /osk-build atualiza
69
69
  │ ├── 📏 Regras
70
70
  │ ├── 🔀 Fluxos
71
71
  │ ├── 📊 Tabelas de Referência
@@ -82,8 +82,8 @@ specs/NNN-nome/
82
82
  scenarios.md ← Given/When/Then (viram testes)
83
83
  tasks.md ← tarefas por repo
84
84
  links.md ← PRs + link da feature page
85
- audit-report.md ← resultado do self-review do /spec
86
- conformance-report.json ← validação pós-implementação do /dev
85
+ audit-report.md ← resultado do self-review do /osk-spec
86
+ conformance-report.json ← validação pós-implementação do /osk-build
87
87
  conformance-history.log ← histórico de validações (append por run)
88
88
  openapi.yaml ← stub OpenAPI 3.0 (opcional, se REST endpoints)
89
89
  ```
@@ -24,7 +24,7 @@ applyTo: '**/*.md'
24
24
 
25
25
  1. Created: spec directory with brief + scenarios + contracts + tasks
26
26
  2. In progress: tasks are checked off, links.md tracks PRs
27
- 3. Post-merge sync: if implementation diverged intentionally from spec, /dev updates contracts.md and scenarios.md to match reality (Phase D.1.5)
27
+ 3. Post-merge sync: if implementation diverged intentionally from spec, /osk-build updates contracts.md and scenarios.md to match reality (Phase D.1.5)
28
28
  4. Done: all tasks checked, spec directory stays (it IS the history)
29
29
 
30
30
  Specs are NOT archived or moved. The spec directory with checked tasks IS the record.