@dynamicworks/br-openspec 2.0.0 → 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.
- package/README.md +11 -2
- package/README.pt-BR.md +11 -2
- package/dist/commands/config.js +4 -0
- package/dist/commands/schema.js +21 -21
- package/dist/core/artifact-graph/instruction-loader.js +4 -4
- package/dist/core/artifact-graph/schema.js +5 -4
- package/dist/core/completions/factory.js +3 -2
- package/dist/core/completions/installers/fish-installer.js +13 -12
- package/dist/core/completions/installers/powershell-installer.js +16 -17
- package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
- package/dist/core/completions/installers/zsh-installer.js +4 -32
- package/dist/core/config.js +3 -2
- package/dist/core/global-config.d.ts +6 -1
- package/dist/core/global-config.js +15 -16
- package/dist/core/parsers/change-parser.js +7 -6
- package/dist/core/parsers/requirement-blocks.js +5 -5
- package/dist/core/parsers/spec-structure.js +1 -1
- package/dist/core/profile-sync-drift.js +1 -0
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -1
- package/dist/core/project-config.js +12 -13
- package/dist/core/shared/skill-generation.js +3 -1
- package/dist/core/shared/tool-detection.d.ts +2 -2
- package/dist/core/shared/tool-detection.js +2 -0
- package/dist/core/specs-apply.js +38 -39
- package/dist/core/templates/skill-templates.d.ts +1 -1
- package/dist/core/templates/skill-templates.js +1 -1
- package/dist/core/templates/workflows/code-review.d.ts +10 -0
- package/dist/core/templates/workflows/code-review.js +21 -0
- package/dist/core/templates/workflows/sync-specs.js +2 -2
- package/dist/core/tools-manager.js +3 -2
- package/dist/core/update.d.ts +6 -0
- package/dist/core/update.js +21 -0
- package/dist/core/validation/validator.js +2 -2
- package/dist/messages/index.d.ts +145 -2
- package/dist/messages/index.js +320 -12
- package/dist/utils/change-metadata.js +8 -7
- package/dist/utils/change-utils.js +12 -11
- package/package.json +1 -1
- package/schemas/spec-driven/schema.yaml +78 -78
- package/schemas/spec-driven/templates/design.md +5 -5
- package/schemas/spec-driven/templates/proposal.md +9 -9
- package/schemas/spec-driven/templates/spec.md +5 -5
- package/schemas/spec-driven/templates/tasks.md +6 -6
- package/dist/core/templates/workflows/upstream-sync.d.ts +0 -10
- package/dist/core/templates/workflows/upstream-sync.js +0 -116
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { MarkdownParser } from './markdown-parser.js';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { promises as fs } from 'fs';
|
|
4
|
+
import { CHANGE_PARSER_MESSAGES } from '../../messages/index.js';
|
|
4
5
|
export class ChangeParser extends MarkdownParser {
|
|
5
6
|
changeDir;
|
|
6
7
|
constructor(content, changeDir) {
|
|
@@ -12,10 +13,10 @@ export class ChangeParser extends MarkdownParser {
|
|
|
12
13
|
const why = this.findSection(sections, 'Why')?.content || '';
|
|
13
14
|
const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
|
|
14
15
|
if (!why) {
|
|
15
|
-
throw new Error(
|
|
16
|
+
throw new Error(CHANGE_PARSER_MESSAGES.mustHaveWhySection);
|
|
16
17
|
}
|
|
17
18
|
if (!whatChanges) {
|
|
18
|
-
throw new Error(
|
|
19
|
+
throw new Error(CHANGE_PARSER_MESSAGES.mustHaveWhatChangesSection);
|
|
19
20
|
}
|
|
20
21
|
// Parse deltas from the What Changes section (simple format)
|
|
21
22
|
const simpleDeltas = this.parseDeltas(whatChanges);
|
|
@@ -72,7 +73,7 @@ export class ChangeParser extends MarkdownParser {
|
|
|
72
73
|
deltas.push({
|
|
73
74
|
spec: specName,
|
|
74
75
|
operation: 'ADDED',
|
|
75
|
-
description:
|
|
76
|
+
description: CHANGE_PARSER_MESSAGES.addRequirement(req.text),
|
|
76
77
|
// Provide both single and plural forms for compatibility
|
|
77
78
|
requirement: req,
|
|
78
79
|
requirements: [req],
|
|
@@ -87,7 +88,7 @@ export class ChangeParser extends MarkdownParser {
|
|
|
87
88
|
deltas.push({
|
|
88
89
|
spec: specName,
|
|
89
90
|
operation: 'MODIFIED',
|
|
90
|
-
description:
|
|
91
|
+
description: CHANGE_PARSER_MESSAGES.modifyRequirement(req.text),
|
|
91
92
|
requirement: req,
|
|
92
93
|
requirements: [req],
|
|
93
94
|
});
|
|
@@ -101,7 +102,7 @@ export class ChangeParser extends MarkdownParser {
|
|
|
101
102
|
deltas.push({
|
|
102
103
|
spec: specName,
|
|
103
104
|
operation: 'REMOVED',
|
|
104
|
-
description:
|
|
105
|
+
description: CHANGE_PARSER_MESSAGES.removeRequirement(req.text),
|
|
105
106
|
requirement: req,
|
|
106
107
|
requirements: [req],
|
|
107
108
|
});
|
|
@@ -115,7 +116,7 @@ export class ChangeParser extends MarkdownParser {
|
|
|
115
116
|
deltas.push({
|
|
116
117
|
spec: specName,
|
|
117
118
|
operation: 'RENAMED',
|
|
118
|
-
description:
|
|
119
|
+
description: CHANGE_PARSER_MESSAGES.renameRequirement(rename.from, rename.to),
|
|
119
120
|
rename,
|
|
120
121
|
});
|
|
121
122
|
});
|
|
@@ -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 &&
|
|
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 &&
|
|
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 &&
|
|
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 &&
|
|
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
|
};
|
package/dist/core/profiles.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/core/profiles.js
CHANGED
|
@@ -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
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PROJECT_CONFIG_MESSAGES } from '../messages/index.js';
|
|
1
|
+
import { PROJECT_CONFIG_MESSAGES, PROJECT_CONFIG_SUGGEST_MESSAGES } from '../messages/index.js';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { parse as parseYaml } from 'yaml';
|
|
@@ -69,7 +69,7 @@ export function readProjectConfig(projectRoot) {
|
|
|
69
69
|
const content = readFileSync(configPath, 'utf-8');
|
|
70
70
|
const raw = parseYaml(content);
|
|
71
71
|
if (!raw || typeof raw !== 'object') {
|
|
72
|
-
console.warn(
|
|
72
|
+
console.warn(PROJECT_CONFIG_SUGGEST_MESSAGES.configNotValidYaml);
|
|
73
73
|
return null;
|
|
74
74
|
}
|
|
75
75
|
const config = {};
|
|
@@ -136,7 +136,7 @@ export function readProjectConfig(projectRoot) {
|
|
|
136
136
|
return Object.keys(config).length > 0 ? config : null;
|
|
137
137
|
}
|
|
138
138
|
catch (error) {
|
|
139
|
-
console.warn(
|
|
139
|
+
console.warn(PROJECT_CONFIG_SUGGEST_MESSAGES.configFailedToParse, error);
|
|
140
140
|
return null;
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -155,8 +155,7 @@ export function validateConfigRules(rules, validArtifactIds, schemaName) {
|
|
|
155
155
|
for (const artifactId of Object.keys(rules)) {
|
|
156
156
|
if (!validArtifactIds.has(artifactId)) {
|
|
157
157
|
const validIds = Array.from(validArtifactIds).sort().join(', ');
|
|
158
|
-
warnings.push(
|
|
159
|
-
`Valid IDs for schema "${schemaName}": ${validIds}`);
|
|
158
|
+
warnings.push(PROJECT_CONFIG_SUGGEST_MESSAGES.unknownArtifactId(artifactId, schemaName, validIds));
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
return warnings;
|
|
@@ -199,26 +198,26 @@ export function suggestSchemas(invalidSchemaName, availableSchemas) {
|
|
|
199
198
|
.slice(0, 3);
|
|
200
199
|
const builtIn = availableSchemas.filter((s) => s.isBuiltIn).map((s) => s.name);
|
|
201
200
|
const projectLocal = availableSchemas.filter((s) => !s.isBuiltIn).map((s) => s.name);
|
|
202
|
-
let message =
|
|
201
|
+
let message = PROJECT_CONFIG_SUGGEST_MESSAGES.schemaNotFound(invalidSchemaName);
|
|
203
202
|
if (suggestions.length > 0) {
|
|
204
|
-
message +=
|
|
203
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.didYouMean;
|
|
205
204
|
suggestions.forEach((s) => {
|
|
206
|
-
const type = s.isBuiltIn
|
|
205
|
+
const type = PROJECT_CONFIG_SUGGEST_MESSAGES.schemaType(s.isBuiltIn);
|
|
207
206
|
message += ` - ${s.name} (${type})\n`;
|
|
208
207
|
});
|
|
209
208
|
message += '\n';
|
|
210
209
|
}
|
|
211
|
-
message +=
|
|
210
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.availableSchemas;
|
|
212
211
|
if (builtIn.length > 0) {
|
|
213
|
-
message +=
|
|
212
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.builtInSchemas(builtIn.join(', '));
|
|
214
213
|
}
|
|
215
214
|
if (projectLocal.length > 0) {
|
|
216
|
-
message +=
|
|
215
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.projectLocalSchemas(projectLocal.join(', '));
|
|
217
216
|
}
|
|
218
217
|
else {
|
|
219
|
-
message +=
|
|
218
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.noProjectLocalSchemas;
|
|
220
219
|
}
|
|
221
|
-
message +=
|
|
220
|
+
message += PROJECT_CONFIG_SUGGEST_MESSAGES.fixSuggestion(invalidSchemaName);
|
|
222
221
|
return message;
|
|
223
222
|
}
|
|
224
223
|
//# sourceMappingURL=project-config.js.map
|
|
@@ -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
|
];
|
package/dist/core/specs-apply.js
CHANGED
|
@@ -10,7 +10,7 @@ import chalk from 'chalk';
|
|
|
10
10
|
import { extractRequirementsSection, parseDeltaSpec, normalizeRequirementName, } from './parsers/requirement-blocks.js';
|
|
11
11
|
import { findMainSpecStructureIssues } from './parsers/spec-structure.js';
|
|
12
12
|
import { Validator } from './validation/validator.js';
|
|
13
|
-
import { ARCHIVE_MESSAGES } from '../messages/index.js';
|
|
13
|
+
import { ARCHIVE_MESSAGES, SPECS_APPLY_MESSAGES } from '../messages/index.js';
|
|
14
14
|
// -----------------------------------------------------------------------------
|
|
15
15
|
// Public API
|
|
16
16
|
// -----------------------------------------------------------------------------
|
|
@@ -69,7 +69,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
69
69
|
for (const add of plan.added) {
|
|
70
70
|
const name = normalizeRequirementName(add.name);
|
|
71
71
|
if (addedNames.has(name)) {
|
|
72
|
-
throw new Error(
|
|
72
|
+
throw new Error(SPECS_APPLY_MESSAGES.duplicateInSection(specName, 'ADDED', add.name));
|
|
73
73
|
}
|
|
74
74
|
addedNames.add(name);
|
|
75
75
|
}
|
|
@@ -77,7 +77,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
77
77
|
for (const mod of plan.modified) {
|
|
78
78
|
const name = normalizeRequirementName(mod.name);
|
|
79
79
|
if (modifiedNames.has(name)) {
|
|
80
|
-
throw new Error(
|
|
80
|
+
throw new Error(SPECS_APPLY_MESSAGES.duplicateInSection(specName, 'MODIFIED', mod.name));
|
|
81
81
|
}
|
|
82
82
|
modifiedNames.add(name);
|
|
83
83
|
}
|
|
@@ -85,7 +85,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
85
85
|
for (const rem of plan.removed) {
|
|
86
86
|
const name = normalizeRequirementName(rem);
|
|
87
87
|
if (removedNamesSet.has(name)) {
|
|
88
|
-
throw new Error(
|
|
88
|
+
throw new Error(SPECS_APPLY_MESSAGES.duplicateInSection(specName, 'REMOVED', rem));
|
|
89
89
|
}
|
|
90
90
|
removedNamesSet.add(name);
|
|
91
91
|
}
|
|
@@ -95,10 +95,10 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
95
95
|
const fromNorm = normalizeRequirementName(from);
|
|
96
96
|
const toNorm = normalizeRequirementName(to);
|
|
97
97
|
if (renamedFromSet.has(fromNorm)) {
|
|
98
|
-
throw new Error(
|
|
98
|
+
throw new Error(SPECS_APPLY_MESSAGES.duplicateFromInRenamed(specName, from));
|
|
99
99
|
}
|
|
100
100
|
if (renamedToSet.has(toNorm)) {
|
|
101
|
-
throw new Error(
|
|
101
|
+
throw new Error(SPECS_APPLY_MESSAGES.duplicateToInRenamed(specName, to));
|
|
102
102
|
}
|
|
103
103
|
renamedFromSet.add(fromNorm);
|
|
104
104
|
renamedToSet.add(toNorm);
|
|
@@ -120,21 +120,20 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
120
120
|
const fromNorm = normalizeRequirementName(from);
|
|
121
121
|
const toNorm = normalizeRequirementName(to);
|
|
122
122
|
if (modifiedNames.has(fromNorm)) {
|
|
123
|
-
throw new Error(
|
|
123
|
+
throw new Error(SPECS_APPLY_MESSAGES.renamedModifiedMustReferenceNew(specName, to));
|
|
124
124
|
}
|
|
125
125
|
// Detect ADDED colliding with a RENAMED TO
|
|
126
126
|
if (addedNames.has(toNorm)) {
|
|
127
|
-
throw new Error(
|
|
127
|
+
throw new Error(SPECS_APPLY_MESSAGES.renamedToCollidesWithAdded(specName, to));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
if (conflicts.length > 0) {
|
|
131
131
|
const c = conflicts[0];
|
|
132
|
-
throw new Error(
|
|
132
|
+
throw new Error(SPECS_APPLY_MESSAGES.requirementInMultipleSections(specName, c.a, c.b, c.name));
|
|
133
133
|
}
|
|
134
134
|
const hasAnyDelta = plan.added.length + plan.modified.length + plan.removed.length + plan.renamed.length > 0;
|
|
135
135
|
if (!hasAnyDelta) {
|
|
136
|
-
throw new Error(
|
|
137
|
-
`Provide ADDED/MODIFIED/REMOVED/RENAMED sections in change spec.`);
|
|
136
|
+
throw new Error(SPECS_APPLY_MESSAGES.noDeltaOperations(path.basename(path.dirname(update.source))));
|
|
138
137
|
}
|
|
139
138
|
// Load or create base target content
|
|
140
139
|
let targetContent;
|
|
@@ -146,7 +145,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
146
145
|
// Target spec does not exist; MODIFIED and RENAMED are not allowed for new specs
|
|
147
146
|
// REMOVED will be ignored with a warning since there's nothing to remove
|
|
148
147
|
if (plan.modified.length > 0 || plan.renamed.length > 0) {
|
|
149
|
-
throw new Error(
|
|
148
|
+
throw new Error(SPECS_APPLY_MESSAGES.targetSpecNotExists(specName));
|
|
150
149
|
}
|
|
151
150
|
// Warn about REMOVED requirements being ignored for new specs
|
|
152
151
|
if (plan.removed.length > 0) {
|
|
@@ -160,7 +159,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
160
159
|
const details = structureIssues
|
|
161
160
|
.map(issue => `line ${issue.line}: ${issue.message}`)
|
|
162
161
|
.join('\n');
|
|
163
|
-
throw new Error(
|
|
162
|
+
throw new Error(SPECS_APPLY_MESSAGES.targetSpecStructurallyInvalid(specName, details));
|
|
164
163
|
}
|
|
165
164
|
// Extract requirements section and build name->block map
|
|
166
165
|
const parts = extractRequirementsSection(targetContent);
|
|
@@ -174,10 +173,10 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
174
173
|
const from = normalizeRequirementName(r.from);
|
|
175
174
|
const to = normalizeRequirementName(r.to);
|
|
176
175
|
if (!nameToBlock.has(from)) {
|
|
177
|
-
throw new Error(
|
|
176
|
+
throw new Error(SPECS_APPLY_MESSAGES.renamedFailedSourceNotFound(specName, r.from));
|
|
178
177
|
}
|
|
179
178
|
if (nameToBlock.has(to)) {
|
|
180
|
-
throw new Error(
|
|
179
|
+
throw new Error(SPECS_APPLY_MESSAGES.renamedFailedTargetExists(specName, r.to));
|
|
181
180
|
}
|
|
182
181
|
const block = nameToBlock.get(from);
|
|
183
182
|
const newHeader = `### Requirement: ${to}`;
|
|
@@ -198,7 +197,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
198
197
|
// For new specs, REMOVED requirements are already warned about and ignored
|
|
199
198
|
// For existing specs, missing requirements are an error
|
|
200
199
|
if (!isNewSpec) {
|
|
201
|
-
throw new Error(
|
|
200
|
+
throw new Error(SPECS_APPLY_MESSAGES.removedFailedNotFound(specName, name));
|
|
202
201
|
}
|
|
203
202
|
// Skip removal for new specs (already warned above)
|
|
204
203
|
continue;
|
|
@@ -209,12 +208,12 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
209
208
|
for (const mod of plan.modified) {
|
|
210
209
|
const key = normalizeRequirementName(mod.name);
|
|
211
210
|
if (!nameToBlock.has(key)) {
|
|
212
|
-
throw new Error(
|
|
211
|
+
throw new Error(SPECS_APPLY_MESSAGES.modifiedFailedNotFound(specName, mod.name));
|
|
213
212
|
}
|
|
214
213
|
// Replace block with provided raw (ensure header line matches key)
|
|
215
|
-
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);
|
|
216
215
|
if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
|
|
217
|
-
throw new Error(
|
|
216
|
+
throw new Error(SPECS_APPLY_MESSAGES.modifiedFailedHeaderMismatch(specName, mod.name));
|
|
218
217
|
}
|
|
219
218
|
nameToBlock.set(key, mod);
|
|
220
219
|
}
|
|
@@ -222,7 +221,7 @@ export async function buildUpdatedSpec(update, changeName) {
|
|
|
222
221
|
for (const add of plan.added) {
|
|
223
222
|
const key = normalizeRequirementName(add.name);
|
|
224
223
|
if (nameToBlock.has(key)) {
|
|
225
|
-
throw new Error(
|
|
224
|
+
throw new Error(SPECS_APPLY_MESSAGES.addedFailedAlreadyExists(specName, add.name));
|
|
226
225
|
}
|
|
227
226
|
nameToBlock.set(key, add);
|
|
228
227
|
}
|
|
@@ -272,22 +271,22 @@ export async function writeUpdatedSpec(update, rebuilt, counts) {
|
|
|
272
271
|
await fs.mkdir(targetDir, { recursive: true });
|
|
273
272
|
await fs.writeFile(update.target, rebuilt);
|
|
274
273
|
const specName = path.basename(path.dirname(update.target));
|
|
275
|
-
console.log(
|
|
274
|
+
console.log(SPECS_APPLY_MESSAGES.applyingChangesTo(specName));
|
|
276
275
|
if (counts.added)
|
|
277
|
-
console.log(
|
|
276
|
+
console.log(SPECS_APPLY_MESSAGES.countAdded(counts.added));
|
|
278
277
|
if (counts.modified)
|
|
279
|
-
console.log(
|
|
278
|
+
console.log(SPECS_APPLY_MESSAGES.countModified(counts.modified));
|
|
280
279
|
if (counts.removed)
|
|
281
|
-
console.log(
|
|
280
|
+
console.log(SPECS_APPLY_MESSAGES.countRemoved(counts.removed));
|
|
282
281
|
if (counts.renamed)
|
|
283
|
-
console.log(
|
|
282
|
+
console.log(SPECS_APPLY_MESSAGES.countRenamed(counts.renamed));
|
|
284
283
|
}
|
|
285
284
|
/**
|
|
286
285
|
* Build a skeleton spec for new capabilities.
|
|
287
286
|
*/
|
|
288
287
|
export function buildSpecSkeleton(specFolderName, changeName) {
|
|
289
288
|
const titleBase = specFolderName;
|
|
290
|
-
return `# ${titleBase} Specification\n\n## Purpose\
|
|
289
|
+
return `# ${titleBase} Specification\n\n## Purpose\n${SPECS_APPLY_MESSAGES.skeletonPurpose(changeName)}\n\n## Requirements\n`;
|
|
291
290
|
}
|
|
292
291
|
/**
|
|
293
292
|
* Apply all delta specs from a change to main specs.
|
|
@@ -304,11 +303,11 @@ export async function applySpecs(projectRoot, changeName, options = {}) {
|
|
|
304
303
|
try {
|
|
305
304
|
const stat = await fs.stat(changeDir);
|
|
306
305
|
if (!stat.isDirectory()) {
|
|
307
|
-
throw new Error(
|
|
306
|
+
throw new Error(SPECS_APPLY_MESSAGES.changeNotFound(changeName));
|
|
308
307
|
}
|
|
309
308
|
}
|
|
310
309
|
catch {
|
|
311
|
-
throw new Error(
|
|
310
|
+
throw new Error(SPECS_APPLY_MESSAGES.changeNotFound(changeName));
|
|
312
311
|
}
|
|
313
312
|
// Find specs to update
|
|
314
313
|
const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
|
|
@@ -337,7 +336,7 @@ export async function applySpecs(projectRoot, changeName, options = {}) {
|
|
|
337
336
|
.filter((i) => i.level === 'ERROR')
|
|
338
337
|
.map((i) => ` ✗ ${i.message}`)
|
|
339
338
|
.join('\n');
|
|
340
|
-
throw new Error(
|
|
339
|
+
throw new Error(SPECS_APPLY_MESSAGES.validationErrorsInRebuiltSpec(specName, errors));
|
|
341
340
|
}
|
|
342
341
|
}
|
|
343
342
|
}
|
|
@@ -352,27 +351,27 @@ export async function applySpecs(projectRoot, changeName, options = {}) {
|
|
|
352
351
|
await fs.mkdir(targetDir, { recursive: true });
|
|
353
352
|
await fs.writeFile(p.update.target, p.rebuilt);
|
|
354
353
|
if (!options.silent) {
|
|
355
|
-
console.log(
|
|
354
|
+
console.log(SPECS_APPLY_MESSAGES.applyingChangesTo(capability));
|
|
356
355
|
if (p.counts.added)
|
|
357
|
-
console.log(
|
|
356
|
+
console.log(SPECS_APPLY_MESSAGES.countAdded(p.counts.added));
|
|
358
357
|
if (p.counts.modified)
|
|
359
|
-
console.log(
|
|
358
|
+
console.log(SPECS_APPLY_MESSAGES.countModified(p.counts.modified));
|
|
360
359
|
if (p.counts.removed)
|
|
361
|
-
console.log(
|
|
360
|
+
console.log(SPECS_APPLY_MESSAGES.countRemoved(p.counts.removed));
|
|
362
361
|
if (p.counts.renamed)
|
|
363
|
-
console.log(
|
|
362
|
+
console.log(SPECS_APPLY_MESSAGES.countRenamed(p.counts.renamed));
|
|
364
363
|
}
|
|
365
364
|
}
|
|
366
365
|
else if (!options.silent) {
|
|
367
|
-
console.log(
|
|
366
|
+
console.log(SPECS_APPLY_MESSAGES.wouldApplyChangesTo(capability));
|
|
368
367
|
if (p.counts.added)
|
|
369
|
-
console.log(
|
|
368
|
+
console.log(SPECS_APPLY_MESSAGES.countAdded(p.counts.added));
|
|
370
369
|
if (p.counts.modified)
|
|
371
|
-
console.log(
|
|
370
|
+
console.log(SPECS_APPLY_MESSAGES.countModified(p.counts.modified));
|
|
372
371
|
if (p.counts.removed)
|
|
373
|
-
console.log(
|
|
372
|
+
console.log(SPECS_APPLY_MESSAGES.countRemoved(p.counts.removed));
|
|
374
373
|
if (p.counts.renamed)
|
|
375
|
-
console.log(
|
|
374
|
+
console.log(SPECS_APPLY_MESSAGES.countRenamed(p.counts.renamed));
|
|
376
375
|
}
|
|
377
376
|
capabilities.push({
|
|
378
377
|
capability,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
216
|
+
The system SHALL do something new.
|
|
217
217
|
|
|
218
218
|
#### Scenario: Basic case
|
|
219
219
|
- **WHEN** user does X
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Encapsulates adding and removing IDE/Code Agent OpenSpec configuration files.
|
|
5
5
|
* Shared by `openspec init` (via InitCommand) and `openspec tools`.
|
|
6
6
|
*/
|
|
7
|
-
import { TOOLS_MESSAGES } from '../messages/index.js';
|
|
7
|
+
import { TOOLS_MESSAGES, TOOLS_MANAGER_MESSAGES } from '../messages/index.js';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import { createRequire } from 'module';
|
|
@@ -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
|
};
|
|
@@ -100,7 +101,7 @@ export async function removeOpenSpecCommandFiles(projectPath, toolId) {
|
|
|
100
101
|
*/
|
|
101
102
|
export async function addTool(projectPath, tool) {
|
|
102
103
|
if (!tool.skillsDir) {
|
|
103
|
-
throw new Error(
|
|
104
|
+
throw new Error(TOOLS_MANAGER_MESSAGES.toolDoesNotSupportSkills(tool.value));
|
|
104
105
|
}
|
|
105
106
|
const globalConfig = getGlobalConfig();
|
|
106
107
|
const profile = globalConfig.profile ?? 'core';
|
package/dist/core/update.d.ts
CHANGED
|
@@ -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.
|