@guilhermefsousa/open-spec-kit 0.1.0

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 (31) hide show
  1. package/README.md +57 -0
  2. package/bin/open-spec-kit.js +39 -0
  3. package/package.json +51 -0
  4. package/src/commands/doctor.js +324 -0
  5. package/src/commands/init.js +981 -0
  6. package/src/commands/update.js +168 -0
  7. package/src/commands/validate.js +599 -0
  8. package/src/parsers/markdown-sections.js +271 -0
  9. package/src/schemas/projects.schema.js +111 -0
  10. package/src/schemas/spec.schema.js +643 -0
  11. package/templates/agents/agents/spec-hub.agent.md +99 -0
  12. package/templates/agents/rules/hub_structure.instructions.md +49 -0
  13. package/templates/agents/rules/ownership.instructions.md +138 -0
  14. package/templates/agents/scripts/notify-gchat.ps1 +99 -0
  15. package/templates/agents/scripts/notify-gchat.sh +131 -0
  16. package/templates/agents/skills/dev-orchestrator/SKILL.md +573 -0
  17. package/templates/agents/skills/discovery/SKILL.md +406 -0
  18. package/templates/agents/skills/setup-project/SKILL.md +452 -0
  19. package/templates/agents/skills/specifying-features/SKILL.md +378 -0
  20. package/templates/github/agents/spec-hub.agent.md +75 -0
  21. package/templates/github/copilot-instructions.md +102 -0
  22. package/templates/github/instructions/hub_structure.instructions.md +33 -0
  23. package/templates/github/instructions/ownership.instructions.md +45 -0
  24. package/templates/github/prompts/dev.prompt.md +19 -0
  25. package/templates/github/prompts/discovery.prompt.md +20 -0
  26. package/templates/github/prompts/nova-feature.prompt.md +19 -0
  27. package/templates/github/prompts/setup.prompt.md +18 -0
  28. package/templates/github/skills/dev-orchestrator/SKILL.md +9 -0
  29. package/templates/github/skills/discovery/SKILL.md +9 -0
  30. package/templates/github/skills/setup-project/SKILL.md +9 -0
  31. package/templates/github/skills/specifying-features/SKILL.md +9 -0
@@ -0,0 +1,168 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { readFile, writeFile, access, mkdir } from 'fs/promises';
4
+ import { join } from 'path';
5
+ import yaml from 'yaml';
6
+
7
+ export async function updateCommand() {
8
+ console.log(chalk.bold('\n open-spec-kit update\n'));
9
+
10
+ const cwd = process.cwd();
11
+ const spinner = ora('Lendo projects.yml...').start();
12
+
13
+ try {
14
+ const projectsRaw = await readFile(join(cwd, 'projects.yml'), 'utf-8');
15
+ const projects = parseProjectsYaml(projectsRaw);
16
+
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;
20
+ }
21
+
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',
29
+ ];
30
+
31
+ const missingSource = [];
32
+ for (const f of sourceFiles) {
33
+ try {
34
+ await access(join(cwd, f));
35
+ } catch {
36
+ missingSource.push(f);
37
+ }
38
+ }
39
+
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
+ }
46
+
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
+ try {
69
+ const sourceContent = await readFile(join(cwd, source), 'utf-8');
70
+ const targetContent = transform(sourceContent);
71
+
72
+ let existingTarget = '';
73
+ try {
74
+ existingTarget = await readFile(join(cwd, target), 'utf-8');
75
+ } catch { /* target doesn't exist yet */ }
76
+
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);
81
+ synced++;
82
+ console.log(chalk.green(` ✓ ${label} sincronizado`));
83
+ }
84
+ } 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
+
96
+ // Check MCP configs
97
+ if (projects.agents.includes('claude')) {
98
+ try {
99
+ await access(join(cwd, '.mcp.json'));
100
+ console.log(chalk.green(' ✓ .mcp.json (Claude Code) encontrado'));
101
+ } catch {
102
+ console.log(chalk.yellow(' ✗ .mcp.json (Claude Code) ausente — crie manualmente'));
103
+ }
104
+ }
105
+
106
+ if (projects.agents.includes('copilot')) {
107
+ try {
108
+ await access(join(cwd, '.vscode/mcp.json'));
109
+ console.log(chalk.green(' ✓ .vscode/mcp.json (Copilot) encontrado'));
110
+ } catch {
111
+ console.log(chalk.yellow(' ✗ .vscode/mcp.json (Copilot) ausente — execute init'));
112
+ }
113
+ }
114
+
115
+ // Check for spec-hub agent divergence between Claude and Copilot
116
+ if (projects.agents.includes('claude') && projects.agents.includes('copilot')) {
117
+ try {
118
+ const claudeAgentRaw = await readFile(join(cwd, '.agents/agents/spec-hub.agent.md'), 'utf-8');
119
+ const copilotAgentRaw = await readFile(join(cwd, '.github/agents/spec-hub.agent.md'), 'utf-8');
120
+
121
+ // Strip frontmatter (--- ... ---) from both to compare functional content
122
+ const stripFrontmatter = (content) => content.replace(/^---\n[\s\S]*?\n---\n+/, '').trim();
123
+ const claudeBody = stripFrontmatter(claudeAgentRaw);
124
+ const copilotBody = stripFrontmatter(copilotAgentRaw);
125
+
126
+ if (claudeBody === copilotBody) {
127
+ console.log(chalk.green(' ✓ spec-hub agent sincronizado entre Claude e Copilot'));
128
+ } else {
129
+ console.log(chalk.yellow(' ⚠ spec-hub agent diverge entre Claude (.agents/) e Copilot (.github/)'));
130
+ console.log(chalk.dim(' Atualize manualmente: o conteúdo funcional diferente entre plataformas.'));
131
+ console.log(chalk.dim(' Claude: .agents/agents/spec-hub.agent.md'));
132
+ console.log(chalk.dim(' Copilot: .github/agents/spec-hub.agent.md'));
133
+ }
134
+ } catch {
135
+ console.log(chalk.yellow(' ⚠ Não foi possível verificar divergência dos agent files'));
136
+ }
137
+ }
138
+
139
+ console.log('');
140
+
141
+ } catch (err) {
142
+ spinner.fail(`Erro: ${err.message}`);
143
+ process.exit(1);
144
+ }
145
+ }
146
+
147
+ function addCopilotFrontmatter(content, applyTo) {
148
+ // Remove existing frontmatter if present
149
+ const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, '');
150
+ return `---\napplyTo: "${applyTo}"\n---\n\n${withoutFrontmatter.trim()}\n`;
151
+ }
152
+
153
+ /**
154
+ * Parse projects.yml using the yaml package (robust — handles all valid YAML).
155
+ */
156
+ function parseProjectsYaml(content) {
157
+ try {
158
+ const data = yaml.parse(content);
159
+ const agents = data?.project?.agents;
160
+ return {
161
+ agents: Array.isArray(agents) ? agents : [],
162
+ preset: data?.project?.preset || 'standard',
163
+ name: data?.project?.name || '',
164
+ };
165
+ } catch {
166
+ return { agents: [], preset: 'standard', name: '' };
167
+ }
168
+ }