@gordon.gan/specflow 1.2.0-beta → 1.2.1-beta
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 +31 -9
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +20 -6
- package/dist/cli/commands/instructions.js +4 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +6 -0
- package/dist/core/artifact-graph/instruction-loader.js +4 -0
- package/dist/core/artifact-graph/types.d.ts +2 -2
- package/dist/core/artifact-language.d.ts +7 -0
- package/dist/core/artifact-language.js +33 -0
- package/dist/core/project-config.d.ts +2 -0
- package/dist/core/project-config.js +32 -1
- package/package.json +1 -1
- package/prompts/shared/artifact-language.md +30 -0
- package/skills/specflow-apply/SKILL.md +34 -5
- package/skills/specflow-explore/SKILL.md +6 -0
- package/skills/specflow-propose/SKILL.md +6 -0
- package/skills/specflow-refine/SKILL.md +6 -0
- package/skills/specflow-snap/SKILL.md +6 -0
package/README.md
CHANGED
|
@@ -14,8 +14,10 @@ SpecFlow 把 **OpenSpec**(结构化需求规划)和 **Superpowers**(TDD、
|
|
|
14
14
|
| Cursor | `specflow:propose` 等命令 | [Cursor 指南](./CURSOR_PACKAGING_AND_USAGE_GUIDE.md) |
|
|
15
15
|
| OpenAI Codex | `$specflow-propose` 等技能 | [Codex 指南](./CODEX_PACKAGING_AND_USAGE_GUIDE.md) |
|
|
16
16
|
| 多语言 / Go Profile | SpecFlow + 语言专项 skills | [语言 Profile 指南](./LANGUAGE_PROFILE_USAGE_GUIDE.md) |
|
|
17
|
-
| 多仓 /
|
|
17
|
+
| 多仓 / 前后端一体需求(**权威**) | Contract Hub + Spokes + 产物与 CLI | [多仓完整指南](./MULTI_REPO_GUIDE.md) |
|
|
18
18
|
| 多仓原生能力(设计) | Store / References / Workset | [多仓技术方案](./MULTI_REPO_TECHNICAL_DESIGN.md) |
|
|
19
|
+
| 多仓场景示例(TALOS) | contracts + api + web 实操路径 | [TALOS 多仓教程](./TALOS_MULTI_REPO_TUTORIAL.md) |
|
|
20
|
+
| TALOS 公共需求详例 | 工作区邀请成员(Hub→BE→FE→联调) | [公共需求 Walkthrough](./docs/TALOS_SHARED_REQUIREMENT_WALKTHROUGH.md) |
|
|
19
21
|
| 产物与作用 | explore / proposal / delta / design / tasks / 主 specs | [产物说明](./SPECFLOW_ARTIFACTS.md) |
|
|
20
22
|
|
|
21
23
|
---
|
|
@@ -115,7 +117,7 @@ npm install -g @gordon.gan/specflow
|
|
|
115
117
|
npm install -g github:Gordon-Gan-Jiang/specflow
|
|
116
118
|
|
|
117
119
|
# 验证
|
|
118
|
-
specflow --version #
|
|
120
|
+
specflow --version # 以 npm / package.json 为准(开发中可见 1.2.0-beta)
|
|
119
121
|
specflow --help
|
|
120
122
|
```
|
|
121
123
|
|
|
@@ -130,13 +132,32 @@ cd specflow && npm install && npm run build && npm link
|
|
|
130
132
|
|
|
131
133
|
```bash
|
|
132
134
|
cd your-project
|
|
133
|
-
specflow init
|
|
134
|
-
specflow init --
|
|
135
|
-
specflow init --
|
|
135
|
+
specflow init # 默认:Claude + Cursor,英文产物
|
|
136
|
+
specflow init --artifact-language zh-CN # 简体中文产物
|
|
137
|
+
specflow init --artifact-language zh # zh 是 zh-CN 的 CLI 别名
|
|
138
|
+
specflow init --ide codex # 仅 Codex
|
|
139
|
+
specflow init --ide all --artifact-language en # Claude + Cursor + Codex
|
|
136
140
|
```
|
|
137
141
|
|
|
138
142
|
init 会创建 `specflow/` 目录、写入 `config.yaml`,并按 `--ide` 生成对应托管资产(skills、commands、prompts 等),同时幂等地追加 `.gitignore` 忽略可再生的 prompts/schemas/templates。
|
|
139
143
|
|
|
144
|
+
产物语言保存在项目配置中:
|
|
145
|
+
|
|
146
|
+
```yaml
|
|
147
|
+
schema: specflow
|
|
148
|
+
|
|
149
|
+
artifacts:
|
|
150
|
+
language: zh-CN # en | zh-CN;未配置时默认为 en
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
语言配置控制 proposal、spec、design、tasks 等产物的自然语言叙述。SpecFlow
|
|
154
|
+
解析依赖的 `ADDED Requirements`、`Requirement:`、`Scenario:`、`WHEN`、`THEN`
|
|
155
|
+
等协议标记始终保持英文;capability ID、change name、路径、命令和代码也不会翻译。
|
|
156
|
+
|
|
157
|
+
`init` 对已有 `specflow/config.yaml` 永不覆盖,包括使用
|
|
158
|
+
`--force-assets` 或传入不同的 `--artifact-language`。已有项目需要直接编辑
|
|
159
|
+
`artifacts.language`;修改只影响后续新建或主动重写的内容,不会自动翻译历史产物。
|
|
160
|
+
|
|
140
161
|
### 跑通第一个变更
|
|
141
162
|
|
|
142
163
|
**需求已清晰:**
|
|
@@ -149,6 +170,8 @@ init 会创建 `specflow/` 目录、写入 `config.yaml`,并按 `--ide` 生成
|
|
|
149
170
|
/specflow:archive
|
|
150
171
|
```
|
|
151
172
|
|
|
173
|
+
需要少打断确认时,可用 `/specflow:apply --yes`:自动接受成功的 Gate A/B 确认,并在构建成功后串行执行 review → test → verify;失败、Block、设计缺口仍会停下,且不会自动 archive。
|
|
174
|
+
|
|
152
175
|
**需求模糊:**
|
|
153
176
|
|
|
154
177
|
```
|
|
@@ -247,7 +270,7 @@ CLI 从当前目录**向上查找**项目根(识别 `specflow/config.yaml`)
|
|
|
247
270
|
|
|
248
271
|
| 命令 | 说明 |
|
|
249
272
|
|------|------|
|
|
250
|
-
| `specflow init [--ide claude\|cursor\|codex\|both\|all]` |
|
|
273
|
+
| `specflow init [--ide claude\|cursor\|codex\|both\|all] [--artifact-language en\|zh-CN]` | 初始化项目目录、产物语言与 IDE 托管资产;`zh` 是 `zh-CN` 的别名 |
|
|
251
274
|
| `specflow sync [--ide ...] [--no-parity-strict]` | 从已安装的 specflow 包同步/更新 IDE 资产(升级后用) |
|
|
252
275
|
| `specflow doctor [--parity] [--json]` | 诊断 IDE 资产是否完整、迁移状态是否正常 |
|
|
253
276
|
| `specflow parity-report [--json]` | 对比 Claude / Cursor / Codex 能力是否一致 |
|
|
@@ -393,11 +416,10 @@ specflow doctor --parity
|
|
|
393
416
|
specflow parity-report
|
|
394
417
|
```
|
|
395
418
|
|
|
396
|
-
|
|
419
|
+
刷新已初始化项目的托管资产(不会覆盖 `specflow/config.yaml`):
|
|
397
420
|
|
|
398
421
|
```bash
|
|
399
|
-
|
|
400
|
-
specflow init
|
|
422
|
+
specflow init --force-assets --ide all
|
|
401
423
|
```
|
|
402
424
|
|
|
403
425
|
---
|
|
@@ -7,13 +7,19 @@ import { validateReportAgainstManifest } from '../../integrations/shared/parity-
|
|
|
7
7
|
import { appendManagedBlock } from '../../integrations/shared/marker-write.js';
|
|
8
8
|
import { getRegeneratableIgnoreLines } from '../../integrations/shared/managed-assets.js';
|
|
9
9
|
import { detectMigrationState } from '../../integrations/shared/migration-state.js';
|
|
10
|
-
|
|
10
|
+
import { DEFAULT_ARTIFACT_LANGUAGE, requireArtifactLanguage, } from '../../core/artifact-language.js';
|
|
11
|
+
function renderConfigYaml(artifactLanguage) {
|
|
12
|
+
return `schema: specflow
|
|
13
|
+
|
|
14
|
+
artifacts:
|
|
15
|
+
language: ${artifactLanguage}
|
|
11
16
|
|
|
12
17
|
# Project context (customize for your project)
|
|
13
18
|
# context: |
|
|
14
19
|
# Tech stack: ...
|
|
15
20
|
# Architecture: ...
|
|
16
21
|
`;
|
|
22
|
+
}
|
|
17
23
|
function findPackageRoot() {
|
|
18
24
|
const currentFile = fileURLToPath(import.meta.url);
|
|
19
25
|
let dir = join(currentFile, '..');
|
|
@@ -32,8 +38,8 @@ async function createDirectoryStructure(projectRoot) {
|
|
|
32
38
|
fs.mkdir(join(projectRoot, 'specflow', 'specs'), { recursive: true }),
|
|
33
39
|
]);
|
|
34
40
|
}
|
|
35
|
-
async function writeConfig(projectRoot) {
|
|
36
|
-
await fs.writeFile(join(projectRoot, 'specflow', 'config.yaml'),
|
|
41
|
+
async function writeConfig(projectRoot, artifactLanguage) {
|
|
42
|
+
await fs.writeFile(join(projectRoot, 'specflow', 'config.yaml'), renderConfigYaml(artifactLanguage), 'utf-8');
|
|
37
43
|
}
|
|
38
44
|
async function pathExists(path) {
|
|
39
45
|
try {
|
|
@@ -52,6 +58,9 @@ async function hasIdeAssets(projectRoot, ide) {
|
|
|
52
58
|
return validateReportAgainstManifest(report).ok;
|
|
53
59
|
}
|
|
54
60
|
export async function initProject(projectRoot, packageRoot, options = {}) {
|
|
61
|
+
const artifactLanguage = options.artifactLanguage === undefined
|
|
62
|
+
? DEFAULT_ARTIFACT_LANGUAGE
|
|
63
|
+
: requireArtifactLanguage(options.artifactLanguage);
|
|
55
64
|
const ide = options.ide ?? 'both';
|
|
56
65
|
const forceAssets = options.forceAssets ?? false;
|
|
57
66
|
const parityStrict = options.parityStrict ?? true;
|
|
@@ -60,14 +69,17 @@ export async function initProject(projectRoot, packageRoot, options = {}) {
|
|
|
60
69
|
const migrationState = await detectMigrationState(projectRoot);
|
|
61
70
|
if (!initialized) {
|
|
62
71
|
await createDirectoryStructure(projectRoot);
|
|
63
|
-
await writeConfig(projectRoot);
|
|
72
|
+
await writeConfig(projectRoot, artifactLanguage);
|
|
64
73
|
}
|
|
74
|
+
const languageEditMessage = options.artifactLanguage
|
|
75
|
+
? ' Edit specflow/config.yaml explicitly to change artifact language in an initialized project.'
|
|
76
|
+
: '';
|
|
65
77
|
const selectedIdeTargets = adapters.map((a) => a.ide);
|
|
66
78
|
const allTargetsHealthy = (await Promise.all(selectedIdeTargets.map((target) => hasIdeAssets(projectRoot, target)))).every(Boolean);
|
|
67
79
|
if (initialized && !forceAssets && allTargetsHealthy) {
|
|
68
80
|
return {
|
|
69
81
|
status: 'already_initialized',
|
|
70
|
-
message: `Project at ${projectRoot} is already initialized for ide=${ide} (state=${migrationState}). Use --force-assets to refresh managed assets
|
|
82
|
+
message: `Project at ${projectRoot} is already initialized for ide=${ide} (state=${migrationState}). Use --force-assets to refresh managed assets.${languageEditMessage}`,
|
|
71
83
|
};
|
|
72
84
|
}
|
|
73
85
|
for (const adapter of adapters) {
|
|
@@ -86,7 +98,7 @@ export async function initProject(projectRoot, packageRoot, options = {}) {
|
|
|
86
98
|
return {
|
|
87
99
|
status: initialized ? 'updated_assets' : 'initialized',
|
|
88
100
|
message: initialized
|
|
89
|
-
? `Updated ${ide} assets at ${projectRoot}`
|
|
101
|
+
? `Updated ${ide} assets at ${projectRoot}.${languageEditMessage}`
|
|
90
102
|
: `Initialized specflow project at ${projectRoot}`,
|
|
91
103
|
};
|
|
92
104
|
}
|
|
@@ -102,6 +114,7 @@ export function registerInitCommand(program) {
|
|
|
102
114
|
.command('init')
|
|
103
115
|
.description('Initialize a project with specflow directory structure and assets')
|
|
104
116
|
.option('--ide <target>', 'Target IDE assets: claude | cursor | codex | both | all', 'both')
|
|
117
|
+
.option('--artifact-language <language>', 'Artifact content language: en | zh-CN (alias: zh)')
|
|
105
118
|
.option('--force-assets', 'Refresh managed IDE assets even when project is initialized')
|
|
106
119
|
.option('--no-parity-strict', 'Disable strict parity validation after asset generation')
|
|
107
120
|
.action(async (opts) => {
|
|
@@ -109,6 +122,7 @@ export function registerInitCommand(program) {
|
|
|
109
122
|
const packageRoot = findPackageRoot();
|
|
110
123
|
const result = await initProject(projectRoot, packageRoot, {
|
|
111
124
|
ide: opts.ide ?? 'both',
|
|
125
|
+
artifactLanguage: opts.artifactLanguage,
|
|
112
126
|
forceAssets: opts.forceAssets ?? false,
|
|
113
127
|
parityStrict: opts.parityStrict ?? true,
|
|
114
128
|
});
|
|
@@ -26,6 +26,7 @@ function readProjectConfig(projectRoot) {
|
|
|
26
26
|
const projectConfig = parseProjectConfig(parsed);
|
|
27
27
|
const config = {
|
|
28
28
|
context: projectConfig.context,
|
|
29
|
+
artifactLanguage: projectConfig.artifactLanguage,
|
|
29
30
|
references: projectConfig.references,
|
|
30
31
|
};
|
|
31
32
|
return { schemaName, config: config };
|
|
@@ -56,6 +57,7 @@ async function enrichInstructionConfig(projectRoot, options) {
|
|
|
56
57
|
});
|
|
57
58
|
return {
|
|
58
59
|
context: parsed.context,
|
|
60
|
+
artifactLanguage: parsed.artifactLanguage,
|
|
59
61
|
referencedStores,
|
|
60
62
|
rootProvenance: {
|
|
61
63
|
source: resolved.source,
|
|
@@ -105,9 +107,11 @@ export function registerInstructionsCommand(program) {
|
|
|
105
107
|
}
|
|
106
108
|
console.info(`Generates: ${instructions.generates}`);
|
|
107
109
|
console.info(`Description: ${instructions.description}`);
|
|
110
|
+
console.info(`Artifact language: ${instructions.artifactLanguage}`);
|
|
108
111
|
if (instructions.instruction) {
|
|
109
112
|
console.info(`\nInstruction:\n${instructions.instruction}`);
|
|
110
113
|
}
|
|
114
|
+
console.info(`\n${instructions.languageGuidance}`);
|
|
111
115
|
const referenceGuidance = collectReferenceGuidance({ referencedStores: instructions.referencedStores });
|
|
112
116
|
const guidance = referenceGuidance.claude;
|
|
113
117
|
if (guidance) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SchemaYaml } from './types.js';
|
|
2
2
|
import type { Diagnostic } from '../diagnostics.js';
|
|
3
3
|
import type { RootSource } from '../root-selection.js';
|
|
4
|
+
import { type ArtifactLanguage } from '../artifact-language.js';
|
|
4
5
|
/**
|
|
5
6
|
* Dependency information included in artifact instructions.
|
|
6
7
|
*/
|
|
@@ -29,6 +30,10 @@ export interface ArtifactInstructions {
|
|
|
29
30
|
readonly instruction: string | undefined;
|
|
30
31
|
/** Project context from the config */
|
|
31
32
|
readonly context: string | undefined;
|
|
33
|
+
/** Canonical language for human-readable artifact content */
|
|
34
|
+
readonly artifactLanguage: ArtifactLanguage;
|
|
35
|
+
/** Rendered generation policy for the selected artifact language */
|
|
36
|
+
readonly languageGuidance: string;
|
|
32
37
|
/** Separately budgeted referenced-store index */
|
|
33
38
|
readonly referencedStores?: ReferencedStoresSection;
|
|
34
39
|
/** Selected planning root provenance */
|
|
@@ -45,6 +50,7 @@ export interface ArtifactInstructions {
|
|
|
45
50
|
*/
|
|
46
51
|
export interface ProjectConfig {
|
|
47
52
|
readonly context?: string;
|
|
53
|
+
readonly artifactLanguage?: ArtifactLanguage;
|
|
48
54
|
readonly referencedStores?: ReferencedStoresSection;
|
|
49
55
|
readonly rootProvenance?: RootProvenanceSection;
|
|
50
56
|
readonly [key: string]: unknown;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_ARTIFACT_LANGUAGE, renderArtifactLanguageGuidance, } from '../artifact-language.js';
|
|
1
2
|
/**
|
|
2
3
|
* Loads enriched instructions for creating an artifact.
|
|
3
4
|
*
|
|
@@ -14,9 +15,12 @@ export function loadInstructions(artifactId, changeDir, schema, config) {
|
|
|
14
15
|
throw new Error(`Artifact '${artifactId}' not found in schema '${schema.name}'`);
|
|
15
16
|
}
|
|
16
17
|
const dependencies = buildDependencyInfo(artifact, schema);
|
|
18
|
+
const artifactLanguage = config?.artifactLanguage ?? DEFAULT_ARTIFACT_LANGUAGE;
|
|
17
19
|
return {
|
|
18
20
|
instruction: artifact.instruction,
|
|
19
21
|
context: config?.context ?? undefined,
|
|
22
|
+
artifactLanguage,
|
|
23
|
+
languageGuidance: renderArtifactLanguageGuidance(artifactLanguage),
|
|
20
24
|
referencedStores: config?.referencedStores,
|
|
21
25
|
rootProvenance: config?.rootProvenance,
|
|
22
26
|
dependencies,
|
|
@@ -78,7 +78,6 @@ export declare const SchemaYamlSchema: z.ZodObject<{
|
|
|
78
78
|
}>>;
|
|
79
79
|
}, "strip", z.ZodTypeAny, {
|
|
80
80
|
version: number;
|
|
81
|
-
name: string;
|
|
82
81
|
artifacts: {
|
|
83
82
|
id: string;
|
|
84
83
|
generates: string;
|
|
@@ -86,6 +85,7 @@ export declare const SchemaYamlSchema: z.ZodObject<{
|
|
|
86
85
|
requires: string[];
|
|
87
86
|
instruction?: string | undefined;
|
|
88
87
|
}[];
|
|
88
|
+
name: string;
|
|
89
89
|
apply?: {
|
|
90
90
|
requires: string[];
|
|
91
91
|
instruction?: string | undefined;
|
|
@@ -94,7 +94,6 @@ export declare const SchemaYamlSchema: z.ZodObject<{
|
|
|
94
94
|
description?: string | undefined;
|
|
95
95
|
}, {
|
|
96
96
|
version: number;
|
|
97
|
-
name: string;
|
|
98
97
|
artifacts: {
|
|
99
98
|
id: string;
|
|
100
99
|
generates: string;
|
|
@@ -102,6 +101,7 @@ export declare const SchemaYamlSchema: z.ZodObject<{
|
|
|
102
101
|
instruction?: string | undefined;
|
|
103
102
|
requires?: string[] | undefined;
|
|
104
103
|
}[];
|
|
104
|
+
name: string;
|
|
105
105
|
apply?: {
|
|
106
106
|
requires: string[];
|
|
107
107
|
instruction?: string | undefined;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const ARTIFACT_LANGUAGES: readonly ["en", "zh-CN"];
|
|
2
|
+
export type ArtifactLanguage = (typeof ARTIFACT_LANGUAGES)[number];
|
|
3
|
+
export declare const DEFAULT_ARTIFACT_LANGUAGE: ArtifactLanguage;
|
|
4
|
+
export declare function isArtifactLanguage(value: unknown): value is ArtifactLanguage;
|
|
5
|
+
export declare function normalizeArtifactLanguage(value: unknown): ArtifactLanguage | undefined;
|
|
6
|
+
export declare function requireArtifactLanguage(value: unknown): ArtifactLanguage;
|
|
7
|
+
export declare function renderArtifactLanguageGuidance(language: ArtifactLanguage): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const ARTIFACT_LANGUAGES = ['en', 'zh-CN'];
|
|
2
|
+
export const DEFAULT_ARTIFACT_LANGUAGE = 'en';
|
|
3
|
+
export function isArtifactLanguage(value) {
|
|
4
|
+
return value === 'en' || value === 'zh-CN';
|
|
5
|
+
}
|
|
6
|
+
export function normalizeArtifactLanguage(value) {
|
|
7
|
+
if (value === 'zh') {
|
|
8
|
+
return 'zh-CN';
|
|
9
|
+
}
|
|
10
|
+
return isArtifactLanguage(value) ? value : undefined;
|
|
11
|
+
}
|
|
12
|
+
export function requireArtifactLanguage(value) {
|
|
13
|
+
const language = normalizeArtifactLanguage(value);
|
|
14
|
+
if (language) {
|
|
15
|
+
return language;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`Unsupported artifact language ${JSON.stringify(value)}. Supported values: en, zh-CN (alias: zh).`);
|
|
18
|
+
}
|
|
19
|
+
export function renderArtifactLanguageGuidance(language) {
|
|
20
|
+
const narrativeLanguage = language === 'zh-CN' ? 'Simplified Chinese' : 'English';
|
|
21
|
+
return [
|
|
22
|
+
'## Artifact language policy',
|
|
23
|
+
'',
|
|
24
|
+
`Write human-readable artifact content in ${narrativeLanguage}.`,
|
|
25
|
+
'',
|
|
26
|
+
'Keep SpecFlow protocol markers unchanged, including:',
|
|
27
|
+
'- `ADDED Requirements`, `MODIFIED Requirements`, `REMOVED Requirements`, and `RENAMED Requirements`',
|
|
28
|
+
'- `Requirement:`, `Scenario:`, `WHEN`, and `THEN`',
|
|
29
|
+
'',
|
|
30
|
+
'Keep capability IDs, change names, file paths, commands, code, and symbols unchanged.',
|
|
31
|
+
'When modifying an existing requirement, preserve its exact name when delta matching requires it.',
|
|
32
|
+
].join('\n');
|
|
33
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Diagnostic } from './diagnostics.js';
|
|
2
|
+
import { type ArtifactLanguage } from './artifact-language.js';
|
|
2
3
|
export interface NormalizedReference {
|
|
3
4
|
readonly id: string;
|
|
4
5
|
readonly remote?: string;
|
|
@@ -6,6 +7,7 @@ export interface NormalizedReference {
|
|
|
6
7
|
export interface ParsedProjectConfig {
|
|
7
8
|
readonly schema: string;
|
|
8
9
|
readonly context?: string;
|
|
10
|
+
readonly artifactLanguage: ArtifactLanguage;
|
|
9
11
|
readonly store?: string;
|
|
10
12
|
readonly references: readonly NormalizedReference[];
|
|
11
13
|
readonly diagnostics: readonly Diagnostic[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { DEFAULT_ARTIFACT_LANGUAGE, isArtifactLanguage, } from './artifact-language.js';
|
|
2
3
|
const KEBAB_CASE_ID = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
3
4
|
const ReferenceObjectSchema = z
|
|
4
5
|
.object({
|
|
@@ -20,6 +21,29 @@ export function parseProjectConfig(raw) {
|
|
|
20
21
|
const record = typeof raw === 'object' && raw !== null ? raw : {};
|
|
21
22
|
const schema = typeof record.schema === 'string' ? record.schema : 'specflow';
|
|
22
23
|
const context = typeof record.context === 'string' ? record.context : undefined;
|
|
24
|
+
let artifactLanguage = DEFAULT_ARTIFACT_LANGUAGE;
|
|
25
|
+
if (record.artifacts !== undefined) {
|
|
26
|
+
const artifacts = typeof record.artifacts === 'object' &&
|
|
27
|
+
record.artifacts !== null &&
|
|
28
|
+
!Array.isArray(record.artifacts)
|
|
29
|
+
? record.artifacts
|
|
30
|
+
: undefined;
|
|
31
|
+
const configuredLanguage = artifacts?.language;
|
|
32
|
+
if (!artifacts ||
|
|
33
|
+
(configuredLanguage !== undefined &&
|
|
34
|
+
!isArtifactLanguage(configuredLanguage))) {
|
|
35
|
+
diagnostics.push({
|
|
36
|
+
severity: 'error',
|
|
37
|
+
code: 'invalid_artifact_language',
|
|
38
|
+
message: "Project artifact language must be one of: 'en', 'zh-CN'.",
|
|
39
|
+
target: 'config.artifacts.language',
|
|
40
|
+
fix: "Set artifacts.language to 'en' or 'zh-CN'.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else if (isArtifactLanguage(configuredLanguage)) {
|
|
44
|
+
artifactLanguage = configuredLanguage;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
23
47
|
let store;
|
|
24
48
|
if (record.store !== undefined) {
|
|
25
49
|
if (typeof record.store === 'string' && KEBAB_CASE_ID.test(record.store)) {
|
|
@@ -66,7 +90,14 @@ export function parseProjectConfig(raw) {
|
|
|
66
90
|
});
|
|
67
91
|
}
|
|
68
92
|
}
|
|
69
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
schema,
|
|
95
|
+
context,
|
|
96
|
+
artifactLanguage,
|
|
97
|
+
store,
|
|
98
|
+
references,
|
|
99
|
+
diagnostics,
|
|
100
|
+
};
|
|
70
101
|
}
|
|
71
102
|
export function loadProjectConfigFromObject(raw) {
|
|
72
103
|
return parseProjectConfig(raw);
|
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Artifact Language Policy
|
|
2
|
+
|
|
3
|
+
Before creating or rewriting any SpecFlow artifact:
|
|
4
|
+
|
|
5
|
+
1. Read `specflow/config.yaml` from the active planning root.
|
|
6
|
+
2. Resolve `artifacts.language`:
|
|
7
|
+
- `en` → write human-readable artifact content in English.
|
|
8
|
+
- `zh-CN` → write human-readable artifact content in Simplified Chinese.
|
|
9
|
+
- Missing → default to `en`.
|
|
10
|
+
- Invalid → report the invalid value and use `en`; do not invent another language.
|
|
11
|
+
3. Apply the selected language consistently to narrative prose, requirement and scenario descriptions, design rationale, and task descriptions.
|
|
12
|
+
|
|
13
|
+
## Protected Protocol
|
|
14
|
+
|
|
15
|
+
Language selection never changes SpecFlow's machine-readable protocol. Keep these tokens exactly as written:
|
|
16
|
+
|
|
17
|
+
- `## ADDED Requirements`
|
|
18
|
+
- `## MODIFIED Requirements`
|
|
19
|
+
- `## REMOVED Requirements`
|
|
20
|
+
- `## RENAMED Requirements`
|
|
21
|
+
- `### Requirement:`
|
|
22
|
+
- `#### Scenario:`
|
|
23
|
+
- `- **WHEN**`
|
|
24
|
+
- `- **THEN**`
|
|
25
|
+
- `FROM:`
|
|
26
|
+
- `TO:`
|
|
27
|
+
|
|
28
|
+
Also preserve capability IDs, change names, file paths, commands, code, symbols, and established technical identifiers.
|
|
29
|
+
|
|
30
|
+
When modifying an existing requirement, preserve its exact requirement name if delta matching depends on that name. Do not translate historical artifacts automatically; the configured language applies prospectively to content created or intentionally rewritten in the current workflow.
|
|
@@ -5,6 +5,15 @@ description: "Two-phase apply — task rewrite + subagent TDD execution"
|
|
|
5
5
|
|
|
6
6
|
# SpecFlow: Apply
|
|
7
7
|
|
|
8
|
+
## Invocation mode
|
|
9
|
+
|
|
10
|
+
Inspect the current user invocation before any stage work:
|
|
11
|
+
|
|
12
|
+
- **Default mode:** `/specflow:apply` preserves every existing user confirmation gate and the default interactive behavior.
|
|
13
|
+
- **Yes mode:** `/specflow:apply --yes` records non-interactive confirmation mode for the current invocation only. It auto-accepts only successful acknowledgement gates; it never bypasses prerequisites, gap detection, failing verification, or a review `Block`.
|
|
14
|
+
|
|
15
|
+
Report the selected mode once at the start of the session. Do not persist it to future invocations.
|
|
16
|
+
|
|
8
17
|
> **HARD GATE (prerequisite)**: phase must be `refined`. Run /specflow:refine first if not.
|
|
9
18
|
> **HARD GATE (Phase A)**: rewritten tasks.md must be user-confirmed before Phase B.
|
|
10
19
|
> **HARD GATE (Phase B)**: each task must be reviewed (spec + code quality) and user-confirmed before next task.
|
|
@@ -18,6 +27,13 @@ description: "Two-phase apply — task rewrite + subagent TDD execution"
|
|
|
18
27
|
- Existing `tasks.md` is present (coarse first-iteration from `/specflow:propose`, or a refine-updated version).
|
|
19
28
|
- `specflow` CLI is available on PATH.
|
|
20
29
|
|
|
30
|
+
## Artifact Language Setup
|
|
31
|
+
|
|
32
|
+
Before Phase A rewrites `tasks.md`, read
|
|
33
|
+
`.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
34
|
+
planning root's `specflow/config.yaml`. Apply the policy to rewritten task
|
|
35
|
+
descriptions while preserving paths, commands, code, and symbols.
|
|
36
|
+
|
|
21
37
|
---
|
|
22
38
|
|
|
23
39
|
## Phase A: Task Rewrite
|
|
@@ -56,7 +72,10 @@ Present the audit summary to the user: per-group task count (coarse → atomic),
|
|
|
56
72
|
|
|
57
73
|
### Gate A: Rewrite Confirmation
|
|
58
74
|
|
|
59
|
-
Present the rewritten `tasks.md`
|
|
75
|
+
- **Default mode:** Present the rewritten `tasks.md` and ask the user to confirm the rewrite. Do NOT proceed to Phase B until the user explicitly confirms.
|
|
76
|
+
- **Yes mode:** Present the rewrite audit and continue directly to Phase B after recording the rewrite as automatically accepted for this invocation.
|
|
77
|
+
|
|
78
|
+
If Phase A reports a reorganization choice or a design gap, stop regardless of mode; `--yes` does not choose a reorganization or fill a missing design decision.
|
|
60
79
|
|
|
61
80
|
---
|
|
62
81
|
|
|
@@ -108,16 +127,26 @@ ECC reviewer verdict routing:
|
|
|
108
127
|
|
|
109
128
|
#### Gate B: Per-task Confirmation
|
|
110
129
|
|
|
111
|
-
Present the task output and both review reports
|
|
130
|
+
- **Default mode:** Present the task output and both review reports. Ask the user to confirm the task is complete. Do NOT proceed to the next task until confirmation is received.
|
|
131
|
+
- **Yes mode:** Present the task output and both review reports. If spec review passes and code-quality review returns `Approve` or `Warning`, record the task as automatically accepted and start the next task without waiting.
|
|
112
132
|
|
|
113
|
-
If
|
|
133
|
+
If either review blocks, return to Stage B2a for the same task. `--yes` never advances past a blocked review.
|
|
114
134
|
|
|
115
135
|
### Stage B3: Phase Transition
|
|
116
136
|
|
|
117
|
-
Once all tasks are confirmed:
|
|
137
|
+
Once all tasks are confirmed in default mode or automatically accepted in yes mode:
|
|
118
138
|
- Invoke `specflow change phase <name> --set apply` to transition the phase.
|
|
119
139
|
- Summarize the build results and overall coverage.
|
|
120
|
-
|
|
140
|
+
|
|
141
|
+
#### Yes-mode downstream sequence
|
|
142
|
+
|
|
143
|
+
When yes mode reaches this point:
|
|
144
|
+
1. Invoke `/specflow:review`. Stop if it reports a CRITICAL or HIGH finding.
|
|
145
|
+
2. Invoke `/specflow:test` only after review has no CRITICAL/HIGH findings. Stop if tests cannot be made green.
|
|
146
|
+
3. Invoke `/specflow:verify` only after test succeeds. Stop if verify returns `FAIL`.
|
|
147
|
+
4. Report review, test, and verification evidence. Do NOT invoke `/specflow:archive` automatically; archive remains the user's manual choice.
|
|
148
|
+
|
|
149
|
+
When default mode reaches this point, inform the user that they can now run `/specflow:review` or `/specflow:verify`.
|
|
121
150
|
|
|
122
151
|
---
|
|
123
152
|
|
|
@@ -33,6 +33,12 @@ Skip explore when you already know exactly what to build — run `/specflow:prop
|
|
|
33
33
|
- `specflow/` exists (suggest `specflow init` if missing)
|
|
34
34
|
- `specflow` CLI available on PATH
|
|
35
35
|
|
|
36
|
+
## Artifact Language Setup
|
|
37
|
+
|
|
38
|
+
Read `.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
39
|
+
planning root's `specflow/config.yaml` before writing or rewriting `explore.md`.
|
|
40
|
+
Apply that policy to every artifact created by this workflow.
|
|
41
|
+
|
|
36
42
|
## Stage 1: Create or Reuse Change
|
|
37
43
|
|
|
38
44
|
If no active change directory exists for this work:
|
|
@@ -18,6 +18,12 @@ Treat every artifact here as "v1, to be iterated on" — depth matters, but so d
|
|
|
18
18
|
- `specflow/specs/` directory should exist, indicating this is a specflow-initialized project. For a brand-new greenfield change with no existing specs, proceed — the prompt handles that case. If `specflow/` itself does not exist, suggest running `specflow init` first.
|
|
19
19
|
- `specflow` CLI must be available on PATH.
|
|
20
20
|
|
|
21
|
+
## Artifact Language Setup
|
|
22
|
+
|
|
23
|
+
Read `.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
24
|
+
planning root's `specflow/config.yaml` before generating proposal, specs, design,
|
|
25
|
+
or tasks. Reuse the resolved policy for all four artifacts.
|
|
26
|
+
|
|
21
27
|
## Stage 0: Explore Handoff (when explore.md exists)
|
|
22
28
|
|
|
23
29
|
Before creating a new change or generating a proposal, check for an existing exploration artifact:
|
|
@@ -26,6 +26,12 @@ emits a round diff summary.
|
|
|
26
26
|
|
|
27
27
|
If any precondition fails, stop and instruct the user to run `/specflow:propose` first.
|
|
28
28
|
|
|
29
|
+
## Artifact Language Setup
|
|
30
|
+
|
|
31
|
+
Read `.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
32
|
+
planning root's `specflow/config.yaml` before refining any artifact. Preserve
|
|
33
|
+
existing requirement names where delta matching requires exact identity.
|
|
34
|
+
|
|
29
35
|
## Stage 1: Pre-loop setup
|
|
30
36
|
|
|
31
37
|
Read, in order:
|
|
@@ -10,6 +10,12 @@ description: "Post-hoc change documentation from git diff"
|
|
|
10
10
|
- Git repository must have uncommitted or recent committed changes to document.
|
|
11
11
|
- `specflow` CLI must be available on PATH.
|
|
12
12
|
|
|
13
|
+
## Artifact Language Setup
|
|
14
|
+
|
|
15
|
+
Read `.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
16
|
+
planning root's `specflow/config.yaml` before synthesizing proposal, specs, or
|
|
17
|
+
tasks. Reuse the resolved policy for every generated artifact.
|
|
18
|
+
|
|
13
19
|
## Stage 1: Analyze Changes
|
|
14
20
|
|
|
15
21
|
Run `git diff` and `git log` to capture what changed.
|