@dynamicworks/br-openspec 2.0.1 → 2.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 (37) hide show
  1. package/README.md +11 -2
  2. package/README.pt-BR.md +11 -2
  3. package/dist/commands/config.js +4 -0
  4. package/dist/commands/schema.js +21 -21
  5. package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
  6. package/dist/core/completions/installers/zsh-installer.js +3 -31
  7. package/dist/core/config.js +3 -2
  8. package/dist/core/global-config.d.ts +6 -1
  9. package/dist/core/global-config.js +15 -16
  10. package/dist/core/parsers/requirement-blocks.js +5 -5
  11. package/dist/core/parsers/spec-structure.js +1 -1
  12. package/dist/core/profile-sync-drift.js +1 -0
  13. package/dist/core/profiles.d.ts +2 -2
  14. package/dist/core/profiles.js +2 -1
  15. package/dist/core/shared/skill-generation.js +3 -1
  16. package/dist/core/shared/tool-detection.d.ts +2 -2
  17. package/dist/core/shared/tool-detection.js +2 -0
  18. package/dist/core/specs-apply.js +1 -1
  19. package/dist/core/templates/skill-templates.d.ts +1 -1
  20. package/dist/core/templates/skill-templates.js +1 -1
  21. package/dist/core/templates/workflows/code-review.d.ts +10 -0
  22. package/dist/core/templates/workflows/code-review.js +21 -0
  23. package/dist/core/templates/workflows/sync-specs.js +2 -2
  24. package/dist/core/tools-manager.js +1 -0
  25. package/dist/core/update.d.ts +6 -0
  26. package/dist/core/update.js +21 -0
  27. package/dist/core/validation/validator.js +2 -2
  28. package/dist/messages/index.d.ts +34 -2
  29. package/dist/messages/index.js +182 -12
  30. package/package.json +1 -1
  31. package/schemas/spec-driven/schema.yaml +78 -78
  32. package/schemas/spec-driven/templates/design.md +5 -5
  33. package/schemas/spec-driven/templates/proposal.md +9 -9
  34. package/schemas/spec-driven/templates/spec.md +5 -5
  35. package/schemas/spec-driven/templates/tasks.md +6 -6
  36. package/dist/core/templates/workflows/upstream-sync.d.ts +0 -10
  37. package/dist/core/templates/workflows/upstream-sync.js +0 -116
package/README.md CHANGED
@@ -97,7 +97,7 @@ openspec init
97
97
 
98
98
  Now tell your AI: `/opsx:propose <what-you-want-to-build>`
99
99
 
100
- If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
100
+ If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
101
101
 
102
102
  > [!NOTE]
103
103
  > Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 25+ tools and growing.
@@ -116,6 +116,13 @@ If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/
116
116
  → **[Customization](docs/customization.md)**: make it yours
117
117
 
118
118
 
119
+ ## Community schemas
120
+
121
+ Third-party schema bundles distributed via standalone repositories — these provide opinionated workflows that integrate BR-OpenSpec with other tools, similar to how [github/spec-kit's community extension catalog](https://github.com/github/spec-kit/tree/main/extensions) handles tool integrations.
122
+
123
+ → **[Browse the catalog](docs/customization.md#community-schemas)** in the customization docs.
124
+
125
+
119
126
  ## Why BR-OpenSpec?
120
127
 
121
128
  AI coding assistants are powerful but unpredictable when requirements live only in chat history. BR-OpenSpec adds a lightweight spec layer so you agree on what to build before any code is written.
@@ -161,10 +168,12 @@ openspec tools --remove windsurf
161
168
 
162
169
  ## Usage Notes
163
170
 
164
- **Model selection**: BR-OpenSpec works best with high-reasoning models. We recommend Opus 4.5 and GPT 5.2 for both planning and implementation.
171
+ **Model selection**: BR-OpenSpec works best with high-reasoning models. We recommend Codex 5.5 and Opus 4.8 for both planning and implementation.
165
172
 
166
173
  **Context hygiene**: BR-OpenSpec benefits from a clean context window. Clear your context before starting implementation and maintain good context hygiene throughout your session.
167
174
 
175
+ **Spec keywords stay in English**: BR-OpenSpec is PT-BR first, but the spec format is a protocol read by the tooling. Structural markers (`## ADDED Requirements`, `### Requirement:`, `#### Scenario:`) and normative/scenario keywords (RFC 2119 — `MUST`, `SHALL`, `SHOULD`, `MAY`, … — plus `WHEN`, `THEN`, `AND`, `GIVEN`, `ELSE`) are always written in English and UPPERCASE; only the descriptive text is in Portuguese. Translating these keywords breaks `openspec validate`. See the full list in [AGENTS.md](AGENTS.md#reserved-english-terms-never-translate).
176
+
168
177
  ## Contributing
169
178
 
170
179
  **Small fixes** — Bug fixes, typo corrections, and minor improvements can be submitted directly as PRs.
package/README.pt-BR.md CHANGED
@@ -97,7 +97,7 @@ openspec init
97
97
 
98
98
  Agora diga à sua IA: `/opsx:propose <o-que-você-quer-construir>`
99
99
 
100
- Se você quiser o fluxo de trabalho expandido (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:sync`, `/opsx:bulk-archive`, `/opsx:onboard`), selecione-o com `openspec config profile` e aplique com `openspec update`.
100
+ Se você quiser o fluxo de trabalho expandido (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), selecione-o com `openspec config profile` e aplique com `openspec update`.
101
101
 
102
102
  > [!NOTE]
103
103
  > Não tem certeza se sua ferramenta é suportada? [Veja a lista completa](docs/pt-BR/supported-tools.md) – suportamos mais de 25 ferramentas e crescendo.
@@ -116,6 +116,13 @@ Se você quiser o fluxo de trabalho expandido (`/opsx:new`, `/opsx:continue`, `/
116
116
  → **[Personalização](docs/pt-BR/customization.md)**: faça do seu jeito
117
117
 
118
118
 
119
+ ## Schemas da comunidade
120
+
121
+ Bundles de schema de terceiros distribuídos em repositórios independentes — oferecem fluxos de trabalho opinativos que integram o BR-OpenSpec a outras ferramentas, de forma semelhante a como o [catálogo de extensões da comunidade do github/spec-kit](https://github.com/github/spec-kit/tree/main/extensions) trata integrações de ferramentas.
122
+
123
+ → **[Veja o catálogo](docs/pt-BR/customization.md#schemas-da-comunidade)** na documentação de personalização.
124
+
125
+
119
126
  ## Por que o BR-OpenSpec?
120
127
 
121
128
  Assistentes de codificação com IA são poderosos, mas imprevisíveis quando os requisitos vivem apenas no histórico do chat. O BR-OpenSpec adiciona uma camada leve de especificação para que você concorde sobre o que construir antes de qualquer código ser escrito.
@@ -163,10 +170,12 @@ openspec tools --remove windsurf
163
170
 
164
171
  ## Notas de Uso
165
172
 
166
- **Seleção de modelo**: O BR-OpenSpec funciona melhor com modelos de alto raciocínio. Recomendamos Opus 4.5 e GPT 5.2 tanto para planejamento quanto para implementação.
173
+ **Seleção de modelo**: O BR-OpenSpec funciona melhor com modelos de alto raciocínio. Recomendamos Codex 5.5 e Opus 4.8 tanto para planejamento quanto para implementação.
167
174
 
168
175
  **Higiene de contexto**: O BR-OpenSpec se beneficia de uma janela de contexto limpa. Limpe seu contexto antes de iniciar a implementação e mantenha uma boa higiene de contexto ao longo da sua sessão.
169
176
 
177
+ **Palavras-chave do formato de spec ficam em inglês**: O BR-OpenSpec é PT-BR first, mas o formato de spec é um protocolo lido pelas ferramentas. Os marcadores estruturais (`## ADDED Requirements`, `### Requirement:`, `#### Scenario:`) e as palavras-chave normativas/de cenário (RFC 2119 — `MUST`, `SHALL`, `SHOULD`, `MAY`, … — além de `WHEN`, `THEN`, `AND`, `GIVEN`, `ELSE`) são sempre escritas em inglês e em CAIXA ALTA; apenas o texto descritivo fica em português. Traduzir essas palavras-chave quebra o `openspec validate`. Veja a lista completa em [AGENTS.md](AGENTS.md#reserved-english-terms-never-translate).
178
+
170
179
  ## Contribuindo
171
180
 
172
181
  **Pequenas correções** — Correções de bugs, erros de digitação e melhorias menores podem ser enviadas diretamente como PRs.
@@ -48,6 +48,10 @@ const WORKFLOW_PROMPT_META = {
48
48
  name: CONFIG_MESSAGES.workflowVerifyName,
49
49
  description: CONFIG_MESSAGES.workflowVerifyDesc,
50
50
  },
51
+ 'code-review': {
52
+ name: CONFIG_MESSAGES.workflowCodeReviewName,
53
+ description: CONFIG_MESSAGES.workflowCodeReviewDesc,
54
+ },
51
55
  onboard: {
52
56
  name: CONFIG_MESSAGES.workflowOnboardName,
53
57
  description: CONFIG_MESSAGES.workflowOnboardDesc,
@@ -797,72 +797,72 @@ function createDefaultTemplate(artifactId) {
797
797
  case 'proposal':
798
798
  return `## Why
799
799
 
800
- <!-- Describe the motivation for this change -->
800
+ <!-- Descreva a motivação para esta mudança -->
801
801
 
802
802
  ## What Changes
803
803
 
804
- <!-- Describe what will change -->
804
+ <!-- Descreva o que vai mudar -->
805
805
 
806
806
  ## Capabilities
807
807
 
808
808
  ### New Capabilities
809
- <!-- List new capabilities -->
809
+ <!-- Liste as novas capabilities -->
810
810
 
811
811
  ### Modified Capabilities
812
- <!-- List modified capabilities -->
812
+ <!-- Liste as capabilities modificadas -->
813
813
 
814
814
  ## Impact
815
815
 
816
- <!-- Describe the impact on existing functionality -->
816
+ <!-- Descreva o impacto sobre a funcionalidade existente -->
817
817
  `;
818
818
  case 'specs':
819
819
  return `## ADDED Requirements
820
820
 
821
- ### Requirement: Example requirement
821
+ ### Requirement: Requisito de exemplo
822
822
 
823
- Description of the requirement.
823
+ Descrição do requisito.
824
824
 
825
- #### Scenario: Example scenario
826
- - **WHEN** some condition
827
- - **THEN** some outcome
825
+ #### Scenario: Cenário de exemplo
826
+ - **WHEN** alguma condição
827
+ - **THEN** algum resultado
828
828
  `;
829
829
  case 'design':
830
830
  return `## Context
831
831
 
832
- <!-- Background and context -->
832
+ <!-- Contexto e antecedentes -->
833
833
 
834
834
  ## Goals / Non-Goals
835
835
 
836
836
  **Goals:**
837
- <!-- List goals -->
837
+ <!-- Liste os objetivos -->
838
838
 
839
839
  **Non-Goals:**
840
- <!-- List non-goals -->
840
+ <!-- Liste os não-objetivos -->
841
841
 
842
842
  ## Decisions
843
843
 
844
- ### 1. Decision Name
844
+ ### 1. Nome da Decisão
845
845
 
846
- Description and rationale.
846
+ Descrição e justificativa.
847
847
 
848
848
  **Alternatives considered:**
849
- - Alternative 1: Rejected because...
849
+ - Alternativa 1: Rejeitada porque...
850
850
 
851
851
  ## Risks / Trade-offs
852
852
 
853
- <!-- List risks and trade-offs -->
853
+ <!-- Liste os riscos e trade-offs -->
854
854
  `;
855
855
  case 'tasks':
856
856
  return `## Implementation Tasks
857
857
 
858
- - [ ] Task 1
859
- - [ ] Task 2
860
- - [ ] Task 3
858
+ - [ ] Tarefa 1
859
+ - [ ] Tarefa 2
860
+ - [ ] Tarefa 3
861
861
  `;
862
862
  default:
863
863
  return `## ${artifactId}
864
864
 
865
- <!-- Add content here -->
865
+ <!-- Adicione o conteúdo aqui -->
866
866
  `;
867
867
  }
868
868
  }
@@ -59,14 +59,6 @@ export declare class ZshInstaller {
59
59
  * @returns true if .zshrc exists and has markers
60
60
  */
61
61
  private hasZshrcConfig;
62
- /**
63
- * Check if fpath configuration is needed for a given directory
64
- * Used to verify if Oh My Zsh (or other) completions directory is already in fpath
65
- *
66
- * @param completionsDir - Directory to check for in fpath
67
- * @returns true if configuration is needed, false if directory is already referenced
68
- */
69
- private needsFpathConfig;
70
62
  /**
71
63
  * Remove .zshrc configuration
72
64
  * Used during uninstallation
@@ -148,26 +148,6 @@ export class ZshInstaller {
148
148
  return false;
149
149
  }
150
150
  }
151
- /**
152
- * Check if fpath configuration is needed for a given directory
153
- * Used to verify if Oh My Zsh (or other) completions directory is already in fpath
154
- *
155
- * @param completionsDir - Directory to check for in fpath
156
- * @returns true if configuration is needed, false if directory is already referenced
157
- */
158
- async needsFpathConfig(completionsDir) {
159
- try {
160
- const zshrcPath = this.getZshrcPath();
161
- const content = await fs.readFile(zshrcPath, 'utf-8');
162
- // Check if fpath already includes this directory
163
- return !content.includes(completionsDir);
164
- }
165
- catch (error) {
166
- // If we can't read .zshrc, assume config is needed
167
- console.debug(`Unable to read .zshrc to check fpath config: ${error instanceof Error ? error.message : String(error)}`);
168
- return true;
169
- }
170
- }
171
151
  /**
172
152
  * Remove .zshrc configuration
173
153
  * Used during uninstallation
@@ -256,18 +236,10 @@ export class ZshInstaller {
256
236
  const backupPath = isUpdate ? await this.backupExistingFile(targetPath) : undefined;
257
237
  // Write the completion script
258
238
  await fs.writeFile(targetPath, completionScript, 'utf-8');
259
- // Auto-configure .zshrc
239
+ // Auto-configure .zshrc for standard Zsh only.
240
+ // Oh My Zsh loads custom/completions and runs compinit itself.
260
241
  let zshrcConfigured = false;
261
- if (isOhMyZsh) {
262
- // For Oh My Zsh, verify that custom/completions is in fpath
263
- // If not, add it to .zshrc
264
- const needsConfig = await this.needsFpathConfig(targetDir);
265
- if (needsConfig) {
266
- zshrcConfigured = await this.configureZshrc(targetDir);
267
- }
268
- }
269
- else {
270
- // Standard Zsh always needs .zshrc configuration
242
+ if (!isOhMyZsh) {
271
243
  zshrcConfigured = await this.configureZshrc(targetDir);
272
244
  }
273
245
  // Generate instructions (only if .zshrc wasn't auto-configured)
@@ -23,12 +23,13 @@ export const AI_TOOLS = [
23
23
  { name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
24
24
  { name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie' },
25
25
  { name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
26
- { name: 'Kimi Code CLI', value: 'kimi', available: true, successLabel: 'Kimi Code CLI', skillsDir: '.kimi' },
26
+ { name: 'Kimi CLI', value: 'kimi', available: true, successLabel: 'Kimi CLI', skillsDir: '.kimi' },
27
27
  { name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro' },
28
+ { name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
29
+ { name: 'Mistral Vibe', value: 'vibe', available: true, successLabel: 'Mistral Vibe', skillsDir: '.vibe' },
28
30
  { name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode' },
29
31
  { name: 'Pi', value: 'pi', available: true, successLabel: 'Pi', skillsDir: '.pi' },
30
32
  { name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder' },
31
- { name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
32
33
  { name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
33
34
  { name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo' },
34
35
  { name: 'Trae', value: 'trae', available: true, successLabel: 'Trae', skillsDir: '.trae' },
@@ -25,7 +25,12 @@ export declare function getGlobalConfigDir(): string;
25
25
  * - Unix/macOS fallback: ~/.local/share/openspec/
26
26
  * - Windows fallback: %LOCALAPPDATA%/openspec/
27
27
  */
28
- export declare function getGlobalDataDir(): string;
28
+ export interface GlobalDataDirOptions {
29
+ env?: NodeJS.ProcessEnv;
30
+ platform?: NodeJS.Platform;
31
+ homedir?: string;
32
+ }
33
+ export declare function getGlobalDataDir(options?: GlobalDataDirOptions): string;
29
34
  /**
30
35
  * Gets the path to the global config file.
31
36
  */
@@ -36,32 +36,31 @@ export function getGlobalConfigDir() {
36
36
  // Unix/macOS fallback: ~/.config
37
37
  return path.join(os.homedir(), '.config', GLOBAL_CONFIG_DIR_NAME);
38
38
  }
39
- /**
40
- * Gets the global data directory path following XDG Base Directory Specification.
41
- * Used for user data like schema overrides.
42
- *
43
- * - All platforms: $XDG_DATA_HOME/openspec/ if XDG_DATA_HOME is set
44
- * - Unix/macOS fallback: ~/.local/share/openspec/
45
- * - Windows fallback: %LOCALAPPDATA%/openspec/
46
- */
47
- export function getGlobalDataDir() {
39
+ function joinGlobalDataPath(platform, ...segments) {
40
+ return platform === 'win32'
41
+ ? path.win32.join(...segments)
42
+ : path.posix.join(...segments);
43
+ }
44
+ export function getGlobalDataDir(options = {}) {
45
+ const env = options.env ?? process.env;
46
+ const platform = options.platform ?? os.platform();
48
47
  // XDG_DATA_HOME takes precedence on all platforms when explicitly set
49
- const xdgDataHome = process.env.XDG_DATA_HOME;
48
+ const xdgDataHome = env.XDG_DATA_HOME;
50
49
  if (xdgDataHome) {
51
- return path.join(xdgDataHome, GLOBAL_DATA_DIR_NAME);
50
+ return joinGlobalDataPath(platform, xdgDataHome, GLOBAL_DATA_DIR_NAME);
52
51
  }
53
- const platform = os.platform();
52
+ const homedir = options.homedir ?? os.homedir();
54
53
  if (platform === 'win32') {
55
54
  // Windows: use %LOCALAPPDATA%
56
- const localAppData = process.env.LOCALAPPDATA;
55
+ const localAppData = env.LOCALAPPDATA;
57
56
  if (localAppData) {
58
- return path.join(localAppData, GLOBAL_DATA_DIR_NAME);
57
+ return joinGlobalDataPath(platform, localAppData, GLOBAL_DATA_DIR_NAME);
59
58
  }
60
59
  // Fallback for Windows if LOCALAPPDATA is not set
61
- return path.join(os.homedir(), 'AppData', 'Local', GLOBAL_DATA_DIR_NAME);
60
+ return joinGlobalDataPath(platform, homedir, 'AppData', 'Local', GLOBAL_DATA_DIR_NAME);
62
61
  }
63
62
  // Unix/macOS fallback: ~/.local/share
64
- return path.join(os.homedir(), '.local', 'share', GLOBAL_DATA_DIR_NAME);
63
+ return joinGlobalDataPath(platform, homedir, '.local', 'share', GLOBAL_DATA_DIR_NAME);
65
64
  }
66
65
  /**
67
66
  * Gets the path to the global config file.
@@ -1,7 +1,7 @@
1
1
  export function normalizeRequirementName(name) {
2
2
  return name.trim();
3
3
  }
4
- const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/;
4
+ const REQUIREMENT_HEADER_REGEX = /^###\s*Requirement:\s*(.+)\s*$/i;
5
5
  /**
6
6
  * Extracts the Requirements section from a spec file and parses requirement blocks.
7
7
  */
@@ -37,7 +37,7 @@ export function extractRequirementsSection(content) {
37
37
  let cursor = 0;
38
38
  let preambleLines = [];
39
39
  // Collect preamble lines until first requirement header
40
- while (cursor < sectionBodyLines.length && !/^###\s+Requirement:/.test(sectionBodyLines[cursor])) {
40
+ while (cursor < sectionBodyLines.length && !REQUIREMENT_HEADER_REGEX.test(sectionBodyLines[cursor])) {
41
41
  preambleLines.push(sectionBodyLines[cursor]);
42
42
  cursor++;
43
43
  }
@@ -54,7 +54,7 @@ export function extractRequirementsSection(content) {
54
54
  cursor++;
55
55
  // Gather lines until next requirement header or end of section
56
56
  const bodyLines = [headerLineCandidate];
57
- while (cursor < sectionBodyLines.length && !/^###\s+Requirement:/.test(sectionBodyLines[cursor]) && !/^##\s+/.test(sectionBodyLines[cursor])) {
57
+ while (cursor < sectionBodyLines.length && !REQUIREMENT_HEADER_REGEX.test(sectionBodyLines[cursor]) && !/^##\s+/.test(sectionBodyLines[cursor])) {
58
58
  bodyLines.push(sectionBodyLines[cursor]);
59
59
  cursor++;
60
60
  }
@@ -136,7 +136,7 @@ function parseRequirementBlocksFromSection(sectionBody) {
136
136
  let i = 0;
137
137
  while (i < lines.length) {
138
138
  // Seek next requirement header
139
- while (i < lines.length && !/^###\s+Requirement:/.test(lines[i]))
139
+ while (i < lines.length && !REQUIREMENT_HEADER_REGEX.test(lines[i]))
140
140
  i++;
141
141
  if (i >= lines.length)
142
142
  break;
@@ -149,7 +149,7 @@ function parseRequirementBlocksFromSection(sectionBody) {
149
149
  const name = normalizeRequirementName(m[1]);
150
150
  const buf = [headerLine];
151
151
  i++;
152
- while (i < lines.length && !/^###\s+Requirement:/.test(lines[i]) && !/^##\s+/.test(lines[i])) {
152
+ while (i < lines.length && !REQUIREMENT_HEADER_REGEX.test(lines[i]) && !/^##\s+/.test(lines[i])) {
153
153
  buf.push(lines[i]);
154
154
  i++;
155
155
  }
@@ -1,7 +1,7 @@
1
1
  const REQUIREMENTS_SECTION_HEADER = /^##\s+Requirements\s*$/i;
2
2
  const TOP_LEVEL_SECTION_HEADER = /^##\s+/;
3
3
  const DELTA_HEADER = /^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements\s*$/i;
4
- const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/;
4
+ const REQUIREMENT_HEADER = /^###\s+Requirement:\s*(.+)\s*$/i;
5
5
  export function findMainSpecStructureIssues(content) {
6
6
  const normalized = content.replace(/\r\n?/g, '\n');
7
7
  const stripped = stripFencedCodeBlocksPreservingLines(normalized);
@@ -17,6 +17,7 @@ export const WORKFLOW_TO_SKILL_DIR = {
17
17
  'archive': 'openspec-archive-change',
18
18
  'bulk-archive': 'openspec-bulk-archive-change',
19
19
  'verify': 'openspec-verify-change',
20
+ 'code-review': 'openspec-code-review',
20
21
  'onboard': 'openspec-onboard',
21
22
  'propose': 'openspec-propose',
22
23
  };
@@ -9,11 +9,11 @@ import type { Profile } from './global-config.js';
9
9
  * Core workflows included in the 'core' profile.
10
10
  * These provide the streamlined experience for new users.
11
11
  */
12
- export declare const CORE_WORKFLOWS: readonly ["propose", "explore", "apply", "archive"];
12
+ export declare const CORE_WORKFLOWS: readonly ["propose", "explore", "apply", "sync", "archive"];
13
13
  /**
14
14
  * All available workflows in the system.
15
15
  */
16
- export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "onboard"];
16
+ export declare const ALL_WORKFLOWS: readonly ["propose", "explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "code-review", "onboard"];
17
17
  export type WorkflowId = (typeof ALL_WORKFLOWS)[number];
18
18
  export type CoreWorkflowId = (typeof CORE_WORKFLOWS)[number];
19
19
  /**
@@ -8,7 +8,7 @@
8
8
  * Core workflows included in the 'core' profile.
9
9
  * These provide the streamlined experience for new users.
10
10
  */
11
- export const CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'archive'];
11
+ export const CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'sync', 'archive'];
12
12
  /**
13
13
  * All available workflows in the system.
14
14
  */
@@ -23,6 +23,7 @@ export const ALL_WORKFLOWS = [
23
23
  'archive',
24
24
  'bulk-archive',
25
25
  'verify',
26
+ 'code-review',
26
27
  'onboard',
27
28
  ];
28
29
  /**
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Shared utilities for generating skill and command files.
5
5
  */
6
- import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getArchiveChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getOnboardSkillTemplate, getOpsxProposeSkillTemplate, getOpsxExploreCommandTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate, getOpsxFfCommandTemplate, getOpsxSyncCommandTemplate, getOpsxArchiveCommandTemplate, getOpsxBulkArchiveCommandTemplate, getOpsxVerifyCommandTemplate, getOpsxOnboardCommandTemplate, getOpsxProposeCommandTemplate, } from '../templates/skill-templates.js';
6
+ import { getExploreSkillTemplate, getNewChangeSkillTemplate, getContinueChangeSkillTemplate, getApplyChangeSkillTemplate, getFfChangeSkillTemplate, getSyncSpecsSkillTemplate, getArchiveChangeSkillTemplate, getBulkArchiveChangeSkillTemplate, getVerifyChangeSkillTemplate, getCodeReviewSkillTemplate, getOnboardSkillTemplate, getOpsxProposeSkillTemplate, getOpsxExploreCommandTemplate, getOpsxNewCommandTemplate, getOpsxContinueCommandTemplate, getOpsxApplyCommandTemplate, getOpsxFfCommandTemplate, getOpsxSyncCommandTemplate, getOpsxArchiveCommandTemplate, getOpsxBulkArchiveCommandTemplate, getOpsxVerifyCommandTemplate, getOpsxCodeReviewCommandTemplate, getOpsxOnboardCommandTemplate, getOpsxProposeCommandTemplate, } from '../templates/skill-templates.js';
7
7
  /**
8
8
  * Gets skill templates with their directory names, optionally filtered by workflow IDs.
9
9
  *
@@ -20,6 +20,7 @@ export function getSkillTemplates(workflowFilter) {
20
20
  { template: getArchiveChangeSkillTemplate(), dirName: 'openspec-archive-change', workflowId: 'archive' },
21
21
  { template: getBulkArchiveChangeSkillTemplate(), dirName: 'openspec-bulk-archive-change', workflowId: 'bulk-archive' },
22
22
  { template: getVerifyChangeSkillTemplate(), dirName: 'openspec-verify-change', workflowId: 'verify' },
23
+ { template: getCodeReviewSkillTemplate(), dirName: 'openspec-code-review', workflowId: 'code-review' },
23
24
  { template: getOnboardSkillTemplate(), dirName: 'openspec-onboard', workflowId: 'onboard' },
24
25
  { template: getOpsxProposeSkillTemplate(), dirName: 'openspec-propose', workflowId: 'propose' },
25
26
  ];
@@ -44,6 +45,7 @@ export function getCommandTemplates(workflowFilter) {
44
45
  { template: getOpsxArchiveCommandTemplate(), id: 'archive' },
45
46
  { template: getOpsxBulkArchiveCommandTemplate(), id: 'bulk-archive' },
46
47
  { template: getOpsxVerifyCommandTemplate(), id: 'verify' },
48
+ { template: getOpsxCodeReviewCommandTemplate(), id: 'code-review' },
47
49
  { template: getOpsxOnboardCommandTemplate(), id: 'onboard' },
48
50
  { template: getOpsxProposeCommandTemplate(), id: 'propose' },
49
51
  ];
@@ -6,12 +6,12 @@
6
6
  /**
7
7
  * Names of skill directories created by openspec init.
8
8
  */
9
- export declare const SKILL_NAMES: readonly ["openspec-explore", "openspec-new-change", "openspec-continue-change", "openspec-apply-change", "openspec-ff-change", "openspec-sync-specs", "openspec-archive-change", "openspec-bulk-archive-change", "openspec-verify-change", "openspec-onboard", "openspec-propose"];
9
+ export declare const SKILL_NAMES: readonly ["openspec-explore", "openspec-new-change", "openspec-continue-change", "openspec-apply-change", "openspec-ff-change", "openspec-sync-specs", "openspec-archive-change", "openspec-bulk-archive-change", "openspec-verify-change", "openspec-code-review", "openspec-onboard", "openspec-propose"];
10
10
  export type SkillName = (typeof SKILL_NAMES)[number];
11
11
  /**
12
12
  * IDs of command templates created by openspec init.
13
13
  */
14
- export declare const COMMAND_IDS: readonly ["explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "onboard", "propose"];
14
+ export declare const COMMAND_IDS: readonly ["explore", "new", "continue", "apply", "ff", "sync", "archive", "bulk-archive", "verify", "code-review", "onboard", "propose"];
15
15
  export type CommandId = (typeof COMMAND_IDS)[number];
16
16
  /**
17
17
  * Status of skill configuration for a tool.
@@ -19,6 +19,7 @@ export const SKILL_NAMES = [
19
19
  'openspec-archive-change',
20
20
  'openspec-bulk-archive-change',
21
21
  'openspec-verify-change',
22
+ 'openspec-code-review',
22
23
  'openspec-onboard',
23
24
  'openspec-propose',
24
25
  ];
@@ -35,6 +36,7 @@ export const COMMAND_IDS = [
35
36
  'archive',
36
37
  'bulk-archive',
37
38
  'verify',
39
+ 'code-review',
38
40
  'onboard',
39
41
  'propose',
40
42
  ];
@@ -211,7 +211,7 @@ export async function buildUpdatedSpec(update, changeName) {
211
211
  throw new Error(SPECS_APPLY_MESSAGES.modifiedFailedNotFound(specName, mod.name));
212
212
  }
213
213
  // Replace block with provided raw (ensure header line matches key)
214
- const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/);
214
+ const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/i);
215
215
  if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
216
216
  throw new Error(SPECS_APPLY_MESSAGES.modifiedFailedHeaderMismatch(specName, mod.name));
217
217
  }
@@ -13,8 +13,8 @@ export { getSyncSpecsSkillTemplate, getOpsxSyncCommandTemplate } from './workflo
13
13
  export { getArchiveChangeSkillTemplate, getOpsxArchiveCommandTemplate } from './workflows/archive-change.js';
14
14
  export { getBulkArchiveChangeSkillTemplate, getOpsxBulkArchiveCommandTemplate } from './workflows/bulk-archive-change.js';
15
15
  export { getVerifyChangeSkillTemplate, getOpsxVerifyCommandTemplate } from './workflows/verify-change.js';
16
+ export { getCodeReviewSkillTemplate, getOpsxCodeReviewCommandTemplate } from './workflows/code-review.js';
16
17
  export { getOnboardSkillTemplate, getOpsxOnboardCommandTemplate } from './workflows/onboard.js';
17
18
  export { getOpsxProposeSkillTemplate, getOpsxProposeCommandTemplate } from './workflows/propose.js';
18
19
  export { getFeedbackSkillTemplate } from './workflows/feedback.js';
19
- export { getUpstreamSyncSkillTemplate, getOpsxUpstreamSyncCommandTemplate } from './workflows/upstream-sync.js';
20
20
  //# sourceMappingURL=skill-templates.d.ts.map
@@ -12,8 +12,8 @@ export { getSyncSpecsSkillTemplate, getOpsxSyncCommandTemplate } from './workflo
12
12
  export { getArchiveChangeSkillTemplate, getOpsxArchiveCommandTemplate } from './workflows/archive-change.js';
13
13
  export { getBulkArchiveChangeSkillTemplate, getOpsxBulkArchiveCommandTemplate } from './workflows/bulk-archive-change.js';
14
14
  export { getVerifyChangeSkillTemplate, getOpsxVerifyCommandTemplate } from './workflows/verify-change.js';
15
+ export { getCodeReviewSkillTemplate, getOpsxCodeReviewCommandTemplate } from './workflows/code-review.js';
15
16
  export { getOnboardSkillTemplate, getOpsxOnboardCommandTemplate } from './workflows/onboard.js';
16
17
  export { getOpsxProposeSkillTemplate, getOpsxProposeCommandTemplate } from './workflows/propose.js';
17
18
  export { getFeedbackSkillTemplate } from './workflows/feedback.js';
18
- export { getUpstreamSyncSkillTemplate, getOpsxUpstreamSyncCommandTemplate } from './workflows/upstream-sync.js';
19
19
  //# sourceMappingURL=skill-templates.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Skill Template Workflow Modules
3
+ *
4
+ * This file is generated by splitting the legacy monolithic
5
+ * templates file into workflow-focused modules.
6
+ */
7
+ import type { SkillTemplate, CommandTemplate } from '../types.js';
8
+ export declare function getCodeReviewSkillTemplate(): SkillTemplate;
9
+ export declare function getOpsxCodeReviewCommandTemplate(): CommandTemplate;
10
+ //# sourceMappingURL=code-review.d.ts.map
@@ -0,0 +1,21 @@
1
+ import { CODE_REVIEW_TEMPLATE_MESSAGES } from '../../../messages/index.js';
2
+ export function getCodeReviewSkillTemplate() {
3
+ return {
4
+ name: 'openspec-code-review',
5
+ description: CODE_REVIEW_TEMPLATE_MESSAGES.skillDescription,
6
+ instructions: CODE_REVIEW_TEMPLATE_MESSAGES.instructions,
7
+ license: 'MIT',
8
+ compatibility: CODE_REVIEW_TEMPLATE_MESSAGES.skillCompatibility,
9
+ metadata: { author: 'openspec', version: '1.0' },
10
+ };
11
+ }
12
+ export function getOpsxCodeReviewCommandTemplate() {
13
+ return {
14
+ name: 'OPSX: Code Review',
15
+ description: CODE_REVIEW_TEMPLATE_MESSAGES.opsxDescription,
16
+ category: 'Workflow',
17
+ tags: ['workflow', 'review', 'code-review', 'experimental'],
18
+ content: CODE_REVIEW_TEMPLATE_MESSAGES.instructions,
19
+ };
20
+ }
21
+ //# sourceMappingURL=code-review.js.map
@@ -75,7 +75,7 @@ Esta é uma operação **dirigida por agente** — você lerá os delta specs e
75
75
  ## ADDED Requirements
76
76
 
77
77
  ### Requirement: New Feature
78
- O sistema DEVERÁ fazer algo novo.
78
+ The system SHALL do something new.
79
79
 
80
80
  #### Scenario: Basic case
81
81
  - **WHEN** user does X
@@ -213,7 +213,7 @@ Esta é uma operação **dirigida por agente** — você lerá os delta specs e
213
213
  ## ADDED Requirements
214
214
 
215
215
  ### Requirement: New Feature
216
- O sistema DEVERÁ fazer algo novo.
216
+ The system SHALL do something new.
217
217
 
218
218
  #### Scenario: Basic case
219
219
  - **WHEN** user does X
@@ -29,6 +29,7 @@ const WORKFLOW_TO_SKILL_DIR = {
29
29
  archive: 'openspec-archive-change',
30
30
  'bulk-archive': 'openspec-bulk-archive-change',
31
31
  verify: 'openspec-verify-change',
32
+ 'code-review': 'openspec-code-review',
32
33
  onboard: 'openspec-onboard',
33
34
  propose: 'openspec-propose',
34
35
  };
@@ -38,6 +38,12 @@ export declare class UpdateCommand {
38
38
  * Displays a note about extra workflows installed that aren't in the current profile.
39
39
  */
40
40
  private displayExtraWorkflowsNote;
41
+ /**
42
+ * Sugere voltar ao perfil core quando um perfil personalizado ainda
43
+ * corresponde ao conjunto core antigo (anterior ao sync). Mantém os perfis
44
+ * personalizados sob controle do usuário; não os altera.
45
+ */
46
+ private displayOldCoreCustomProfileNote;
41
47
  /**
42
48
  * Removes skill directories for workflows when delivery changed to commands-only.
43
49
  * Returns the number of directories removed.