@dynamicworks/br-openspec 1.3.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +210 -210
  3. package/README.pt-BR.md +212 -212
  4. package/bin/openspec.js +2 -2
  5. package/dist/commands/feedback.js +4 -4
  6. package/dist/commands/schema.js +60 -60
  7. package/dist/core/artifact-graph/instruction-loader.js +4 -4
  8. package/dist/core/artifact-graph/schema.js +5 -4
  9. package/dist/core/command-generation/adapters/amazon-q.js +5 -5
  10. package/dist/core/command-generation/adapters/antigravity.js +5 -5
  11. package/dist/core/command-generation/adapters/auggie.js +6 -6
  12. package/dist/core/command-generation/adapters/bob.js +6 -6
  13. package/dist/core/command-generation/adapters/claude.js +8 -8
  14. package/dist/core/command-generation/adapters/cline.js +5 -5
  15. package/dist/core/command-generation/adapters/codebuddy.js +7 -7
  16. package/dist/core/command-generation/adapters/codex.js +6 -6
  17. package/dist/core/command-generation/adapters/continue.js +7 -7
  18. package/dist/core/command-generation/adapters/costrict.js +6 -6
  19. package/dist/core/command-generation/adapters/crush.js +8 -8
  20. package/dist/core/command-generation/adapters/cursor.js +8 -8
  21. package/dist/core/command-generation/adapters/factory.js +6 -6
  22. package/dist/core/command-generation/adapters/gemini.js +5 -5
  23. package/dist/core/command-generation/adapters/github-copilot.js +5 -5
  24. package/dist/core/command-generation/adapters/iflow.js +8 -8
  25. package/dist/core/command-generation/adapters/junie.js +5 -5
  26. package/dist/core/command-generation/adapters/kilocode.js +1 -1
  27. package/dist/core/command-generation/adapters/kiro.js +5 -5
  28. package/dist/core/command-generation/adapters/lingma.js +8 -8
  29. package/dist/core/command-generation/adapters/opencode.js +5 -5
  30. package/dist/core/command-generation/adapters/pi.js +5 -5
  31. package/dist/core/command-generation/adapters/qoder.js +8 -8
  32. package/dist/core/command-generation/adapters/qwen.js +5 -5
  33. package/dist/core/command-generation/adapters/roocode.js +5 -5
  34. package/dist/core/command-generation/adapters/windsurf.js +8 -8
  35. package/dist/core/completions/factory.js +3 -2
  36. package/dist/core/completions/generators/bash-generator.js +41 -41
  37. package/dist/core/completions/generators/fish-generator.js +7 -7
  38. package/dist/core/completions/generators/powershell-generator.js +29 -29
  39. package/dist/core/completions/generators/zsh-generator.js +33 -33
  40. package/dist/core/completions/installers/fish-installer.js +13 -12
  41. package/dist/core/completions/installers/powershell-installer.js +16 -17
  42. package/dist/core/completions/installers/zsh-installer.js +1 -1
  43. package/dist/core/completions/templates/bash-templates.js +18 -18
  44. package/dist/core/completions/templates/fish-templates.js +32 -32
  45. package/dist/core/completions/templates/powershell-templates.js +19 -19
  46. package/dist/core/completions/templates/zsh-templates.js +30 -30
  47. package/dist/core/parsers/change-parser.js +7 -6
  48. package/dist/core/project-config.js +12 -13
  49. package/dist/core/shared/skill-generation.js +12 -12
  50. package/dist/core/specs-apply.js +37 -38
  51. package/dist/core/templates/workflows/apply-change.js +288 -288
  52. package/dist/core/templates/workflows/archive-change.js +251 -251
  53. package/dist/core/templates/workflows/bulk-archive-change.js +472 -472
  54. package/dist/core/templates/workflows/continue-change.js +212 -212
  55. package/dist/core/templates/workflows/explore.js +443 -443
  56. package/dist/core/templates/workflows/feedback.js +97 -97
  57. package/dist/core/templates/workflows/ff-change.js +178 -178
  58. package/dist/core/templates/workflows/propose.js +196 -196
  59. package/dist/core/templates/workflows/sync-specs.js +252 -252
  60. package/dist/core/templates/workflows/upstream-sync.js +93 -93
  61. package/dist/core/tools-manager.js +2 -2
  62. package/dist/messages/index.d.ts +111 -0
  63. package/dist/messages/index.js +1115 -977
  64. package/dist/utils/change-metadata.js +8 -7
  65. package/dist/utils/change-utils.js +12 -11
  66. package/package.json +82 -84
  67. package/schemas/spec-driven/schema.yaml +153 -153
  68. package/schemas/spec-driven/templates/design.md +19 -19
  69. package/schemas/spec-driven/templates/proposal.md +23 -23
  70. package/schemas/spec-driven/templates/spec.md +8 -8
  71. package/schemas/spec-driven/templates/tasks.md +9 -9
  72. package/scripts/postinstall.js +83 -83
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { parse as parseYaml } from 'yaml';
3
3
  import { SchemaYamlSchema } from './types.js';
4
+ import { ARTIFACT_GRAPH_MESSAGES } from '../../messages/index.js';
4
5
  export class SchemaValidationError extends Error {
5
6
  constructor(message) {
6
7
  super(message);
@@ -23,7 +24,7 @@ export function parseSchema(yamlContent) {
23
24
  const result = SchemaYamlSchema.safeParse(parsed);
24
25
  if (!result.success) {
25
26
  const errors = result.error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ');
26
- throw new SchemaValidationError(`Invalid schema: ${errors}`);
27
+ throw new SchemaValidationError(ARTIFACT_GRAPH_MESSAGES.invalidSchema(errors));
27
28
  }
28
29
  const schema = result.data;
29
30
  // Check for duplicate artifact IDs
@@ -41,7 +42,7 @@ function validateNoDuplicateIds(artifacts) {
41
42
  const seen = new Set();
42
43
  for (const artifact of artifacts) {
43
44
  if (seen.has(artifact.id)) {
44
- throw new SchemaValidationError(`Duplicate artifact ID: ${artifact.id}`);
45
+ throw new SchemaValidationError(ARTIFACT_GRAPH_MESSAGES.duplicateArtifactId(artifact.id));
45
46
  }
46
47
  seen.add(artifact.id);
47
48
  }
@@ -54,7 +55,7 @@ function validateRequiresReferences(artifacts) {
54
55
  for (const artifact of artifacts) {
55
56
  for (const req of artifact.requires) {
56
57
  if (!validIds.has(req)) {
57
- throw new SchemaValidationError(`Invalid dependency reference in artifact '${artifact.id}': '${req}' does not exist`);
58
+ throw new SchemaValidationError(ARTIFACT_GRAPH_MESSAGES.invalidDependencyReference(artifact.id, req));
58
59
  }
59
60
  }
60
61
  }
@@ -100,7 +101,7 @@ function validateNoCycles(artifacts) {
100
101
  if (!visited.has(artifact.id)) {
101
102
  const cycle = dfs(artifact.id);
102
103
  if (cycle) {
103
- throw new SchemaValidationError(`Cyclic dependency detected: ${cycle}`);
104
+ throw new SchemaValidationError(ARTIFACT_GRAPH_MESSAGES.cyclicDependency(cycle));
104
105
  }
105
106
  }
106
107
  }
@@ -15,11 +15,11 @@ export const amazonQAdapter = {
15
15
  return path.join('.amazonq', 'prompts', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- ---
21
-
22
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ ---
21
+
22
+ ${content.body}
23
23
  `;
24
24
  },
25
25
  };
@@ -15,11 +15,11 @@ export const antigravityAdapter = {
15
15
  return path.join('.agent', 'workflows', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- ---
21
-
22
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ ---
21
+
22
+ ${content.body}
23
23
  `;
24
24
  },
25
25
  };
@@ -15,12 +15,12 @@ export const auggieAdapter = {
15
15
  return path.join('.augment', 'commands', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- argument-hint: command arguments
21
- ---
22
-
23
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ argument-hint: command arguments
21
+ ---
22
+
23
+ ${content.body}
24
24
  `;
25
25
  },
26
26
  };
@@ -33,12 +33,12 @@ export const bobAdapter = {
33
33
  formatFile(content) {
34
34
  // Transform command references from colon to hyphen format for Bob
35
35
  const transformedBody = transformToHyphenCommands(content.body);
36
- return `---
37
- description: ${escapeYamlValue(content.description)}
38
- argument-hint: command arguments
39
- ---
40
-
41
- ${transformedBody}
36
+ return `---
37
+ description: ${escapeYamlValue(content.description)}
38
+ argument-hint: command arguments
39
+ ---
40
+
41
+ ${transformedBody}
42
42
  `;
43
43
  },
44
44
  };
@@ -36,14 +36,14 @@ export const claudeAdapter = {
36
36
  return path.join('.claude', 'commands', 'opsx', `${commandId}.md`);
37
37
  },
38
38
  formatFile(content) {
39
- return `---
40
- name: ${escapeYamlValue(content.name)}
41
- description: ${escapeYamlValue(content.description)}
42
- category: ${escapeYamlValue(content.category)}
43
- tags: ${formatTagsArray(content.tags)}
44
- ---
45
-
46
- ${content.body}
39
+ return `---
40
+ name: ${escapeYamlValue(content.name)}
41
+ description: ${escapeYamlValue(content.description)}
42
+ category: ${escapeYamlValue(content.category)}
43
+ tags: ${formatTagsArray(content.tags)}
44
+ ---
45
+
46
+ ${content.body}
47
47
  `;
48
48
  },
49
49
  };
@@ -16,11 +16,11 @@ export const clineAdapter = {
16
16
  return path.join('.clinerules', 'workflows', `opsx-${commandId}.md`);
17
17
  },
18
18
  formatFile(content) {
19
- return `# ${content.name}
20
-
21
- ${content.description}
22
-
23
- ${content.body}
19
+ return `# ${content.name}
20
+
21
+ ${content.description}
22
+
23
+ ${content.body}
24
24
  `;
25
25
  },
26
26
  };
@@ -15,13 +15,13 @@ export const codebuddyAdapter = {
15
15
  return path.join('.codebuddy', 'commands', 'opsx', `${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- name: ${content.name}
20
- description: "${content.description}"
21
- argument-hint: "[command arguments]"
22
- ---
23
-
24
- ${content.body}
18
+ return `---
19
+ name: ${content.name}
20
+ description: "${content.description}"
21
+ argument-hint: "[command arguments]"
22
+ ---
23
+
24
+ ${content.body}
25
25
  `;
26
26
  },
27
27
  };
@@ -27,12 +27,12 @@ export const codexAdapter = {
27
27
  return path.join(getCodexHome(), 'prompts', `opsx-${commandId}.md`);
28
28
  },
29
29
  formatFile(content) {
30
- return `---
31
- description: ${content.description}
32
- argument-hint: command arguments
33
- ---
34
-
35
- ${content.body}
30
+ return `---
31
+ description: ${content.description}
32
+ argument-hint: command arguments
33
+ ---
34
+
35
+ ${content.body}
36
36
  `;
37
37
  },
38
38
  };
@@ -15,13 +15,13 @@ export const continueAdapter = {
15
15
  return path.join('.continue', 'prompts', `opsx-${commandId}.prompt`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- name: opsx-${content.id}
20
- description: ${content.description}
21
- invokable: true
22
- ---
23
-
24
- ${content.body}
18
+ return `---
19
+ name: opsx-${content.id}
20
+ description: ${content.description}
21
+ invokable: true
22
+ ---
23
+
24
+ ${content.body}
25
25
  `;
26
26
  },
27
27
  };
@@ -15,12 +15,12 @@ export const costrictAdapter = {
15
15
  return path.join('.cospec', 'openspec', 'commands', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: "${content.description}"
20
- argument-hint: command arguments
21
- ---
22
-
23
- ${content.body}
18
+ return `---
19
+ description: "${content.description}"
20
+ argument-hint: command arguments
21
+ ---
22
+
23
+ ${content.body}
24
24
  `;
25
25
  },
26
26
  };
@@ -16,14 +16,14 @@ export const crushAdapter = {
16
16
  },
17
17
  formatFile(content) {
18
18
  const tagsStr = content.tags.join(', ');
19
- return `---
20
- name: ${content.name}
21
- description: ${content.description}
22
- category: ${content.category}
23
- tags: [${tagsStr}]
24
- ---
25
-
26
- ${content.body}
19
+ return `---
20
+ name: ${content.name}
21
+ description: ${content.description}
22
+ category: ${content.category}
23
+ tags: [${tagsStr}]
24
+ ---
25
+
26
+ ${content.body}
27
27
  `;
28
28
  },
29
29
  };
@@ -30,14 +30,14 @@ export const cursorAdapter = {
30
30
  return path.join('.cursor', 'commands', `opsx-${commandId}.md`);
31
31
  },
32
32
  formatFile(content) {
33
- return `---
34
- name: /opsx-${content.id}
35
- id: opsx-${content.id}
36
- category: ${escapeYamlValue(content.category)}
37
- description: ${escapeYamlValue(content.description)}
38
- ---
39
-
40
- ${content.body}
33
+ return `---
34
+ name: /opsx-${content.id}
35
+ id: opsx-${content.id}
36
+ category: ${escapeYamlValue(content.category)}
37
+ description: ${escapeYamlValue(content.description)}
38
+ ---
39
+
40
+ ${content.body}
41
41
  `;
42
42
  },
43
43
  };
@@ -15,12 +15,12 @@ export const factoryAdapter = {
15
15
  return path.join('.factory', 'commands', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- argument-hint: command arguments
21
- ---
22
-
23
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ argument-hint: command arguments
21
+ ---
22
+
23
+ ${content.body}
24
24
  `;
25
25
  },
26
26
  };
@@ -15,11 +15,11 @@ export const geminiAdapter = {
15
15
  return path.join('.gemini', 'commands', 'opsx', `${commandId}.toml`);
16
16
  },
17
17
  formatFile(content) {
18
- return `description = "${content.description}"
19
-
20
- prompt = """
21
- ${content.body}
22
- """
18
+ return `description = "${content.description}"
19
+
20
+ prompt = """
21
+ ${content.body}
22
+ """
23
23
  `;
24
24
  },
25
25
  };
@@ -15,11 +15,11 @@ export const githubCopilotAdapter = {
15
15
  return path.join('.github', 'prompts', `opsx-${commandId}.prompt.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- ---
21
-
22
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ ---
21
+
22
+ ${content.body}
23
23
  `;
24
24
  },
25
25
  };
@@ -15,14 +15,14 @@ export const iflowAdapter = {
15
15
  return path.join('.iflow', 'commands', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- name: /opsx-${content.id}
20
- id: opsx-${content.id}
21
- category: ${content.category}
22
- description: ${content.description}
23
- ---
24
-
25
- ${content.body}
18
+ return `---
19
+ name: /opsx-${content.id}
20
+ id: opsx-${content.id}
21
+ category: ${content.category}
22
+ description: ${content.description}
23
+ ---
24
+
25
+ ${content.body}
26
26
  `;
27
27
  },
28
28
  };
@@ -15,11 +15,11 @@ export const junieAdapter = {
15
15
  return path.join('.junie', 'commands', `opsx-${commandId}.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- ---
21
-
22
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ ---
21
+
22
+ ${content.body}
23
23
  `;
24
24
  },
25
25
  };
@@ -16,7 +16,7 @@ export const kilocodeAdapter = {
16
16
  return path.join('.kilocode', 'workflows', `opsx-${commandId}.md`);
17
17
  },
18
18
  formatFile(content) {
19
- return `${content.body}
19
+ return `${content.body}
20
20
  `;
21
21
  },
22
22
  };
@@ -15,11 +15,11 @@ export const kiroAdapter = {
15
15
  return path.join('.kiro', 'prompts', `opsx-${commandId}.prompt.md`);
16
16
  },
17
17
  formatFile(content) {
18
- return `---
19
- description: ${content.description}
20
- ---
21
-
22
- ${content.body}
18
+ return `---
19
+ description: ${content.description}
20
+ ---
21
+
22
+ ${content.body}
23
23
  `;
24
24
  },
25
25
  };
@@ -16,14 +16,14 @@ export const lingmaAdapter = {
16
16
  },
17
17
  formatFile(content) {
18
18
  const tagsStr = content.tags.join(', ');
19
- return `---
20
- name: ${content.name}
21
- description: ${content.description}
22
- category: ${content.category}
23
- tags: [${tagsStr}]
24
- ---
25
-
26
- ${content.body}
19
+ return `---
20
+ name: ${content.name}
21
+ description: ${content.description}
22
+ category: ${content.category}
23
+ tags: [${tagsStr}]
24
+ ---
25
+
26
+ ${content.body}
27
27
  `;
28
28
  },
29
29
  };
@@ -18,11 +18,11 @@ export const opencodeAdapter = {
18
18
  formatFile(content) {
19
19
  // Transform command references from colon to hyphen format for OpenCode
20
20
  const transformedBody = transformToHyphenCommands(content.body);
21
- return `---
22
- description: ${content.description}
23
- ---
24
-
25
- ${transformedBody}
21
+ return `---
22
+ description: ${content.description}
23
+ ---
24
+
25
+ ${transformedBody}
26
26
  `;
27
27
  },
28
28
  };
@@ -44,11 +44,11 @@ export const piAdapter = {
44
44
  formatFile(content) {
45
45
  // Transform /opsx: references to /opsx- and inject $@ for template args
46
46
  const transformedBody = transformToHyphenCommands(content.body);
47
- return `---
48
- description: ${escapeYamlValue(content.description)}
49
- ---
50
-
51
- ${injectPiArgs(transformedBody)}
47
+ return `---
48
+ description: ${escapeYamlValue(content.description)}
49
+ ---
50
+
51
+ ${injectPiArgs(transformedBody)}
52
52
  `;
53
53
  },
54
54
  };
@@ -16,14 +16,14 @@ export const qoderAdapter = {
16
16
  },
17
17
  formatFile(content) {
18
18
  const tagsStr = content.tags.join(', ');
19
- return `---
20
- name: ${content.name}
21
- description: ${content.description}
22
- category: ${content.category}
23
- tags: [${tagsStr}]
24
- ---
25
-
26
- ${content.body}
19
+ return `---
20
+ name: ${content.name}
21
+ description: ${content.description}
22
+ category: ${content.category}
23
+ tags: [${tagsStr}]
24
+ ---
25
+
26
+ ${content.body}
27
27
  `;
28
28
  },
29
29
  };
@@ -15,11 +15,11 @@ export const qwenAdapter = {
15
15
  return path.join('.qwen', 'commands', `opsx-${commandId}.toml`);
16
16
  },
17
17
  formatFile(content) {
18
- return `description = "${content.description}"
19
-
20
- prompt = """
21
- ${content.body}
22
- """
18
+ return `description = "${content.description}"
19
+
20
+ prompt = """
21
+ ${content.body}
22
+ """
23
23
  `;
24
24
  },
25
25
  };
@@ -16,11 +16,11 @@ export const roocodeAdapter = {
16
16
  return path.join('.roo', 'commands', `opsx-${commandId}.md`);
17
17
  },
18
18
  formatFile(content) {
19
- return `# ${content.name}
20
-
21
- ${content.description}
22
-
23
- ${content.body}
19
+ return `# ${content.name}
20
+
21
+ ${content.description}
22
+
23
+ ${content.body}
24
24
  `;
25
25
  },
26
26
  };
@@ -37,14 +37,14 @@ export const windsurfAdapter = {
37
37
  return path.join('.windsurf', 'workflows', `opsx-${commandId}.md`);
38
38
  },
39
39
  formatFile(content) {
40
- return `---
41
- name: ${escapeYamlValue(content.name)}
42
- description: ${escapeYamlValue(content.description)}
43
- category: ${escapeYamlValue(content.category)}
44
- tags: ${formatTagsArray(content.tags)}
45
- ---
46
-
47
- ${content.body}
40
+ return `---
41
+ name: ${escapeYamlValue(content.name)}
42
+ description: ${escapeYamlValue(content.description)}
43
+ category: ${escapeYamlValue(content.category)}
44
+ tags: ${formatTagsArray(content.tags)}
45
+ ---
46
+
47
+ ${content.body}
48
48
  `;
49
49
  },
50
50
  };
@@ -6,6 +6,7 @@ import { ZshInstaller } from './installers/zsh-installer.js';
6
6
  import { BashInstaller } from './installers/bash-installer.js';
7
7
  import { FishInstaller } from './installers/fish-installer.js';
8
8
  import { PowerShellInstaller } from './installers/powershell-installer.js';
9
+ import { COMPLETIONS_FACTORY_MESSAGES } from '../../messages/index.js';
9
10
  /**
10
11
  * Factory for creating completion generators and installers
11
12
  * This design makes it easy to add support for additional shells
@@ -30,7 +31,7 @@ export class CompletionFactory {
30
31
  case 'powershell':
31
32
  return new PowerShellGenerator();
32
33
  default:
33
- throw new Error(`Unsupported shell: ${shell}`);
34
+ throw new Error(COMPLETIONS_FACTORY_MESSAGES.unsupportedShell(shell));
34
35
  }
35
36
  }
36
37
  /**
@@ -51,7 +52,7 @@ export class CompletionFactory {
51
52
  case 'powershell':
52
53
  return new PowerShellInstaller();
53
54
  default:
54
- throw new Error(`Unsupported shell: ${shell}`);
55
+ throw new Error(COMPLETIONS_FACTORY_MESSAGES.unsupportedShell(shell));
55
56
  }
56
57
  }
57
58
  /**