@gordon.gan/specflow 1.2.0-beta → 1.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -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 +7 -0
- 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
|
**需求已清晰:**
|
|
@@ -247,7 +268,7 @@ CLI 从当前目录**向上查找**项目根(识别 `specflow/config.yaml`)
|
|
|
247
268
|
|
|
248
269
|
| 命令 | 说明 |
|
|
249
270
|
|------|------|
|
|
250
|
-
| `specflow init [--ide claude\|cursor\|codex\|both\|all]` |
|
|
271
|
+
| `specflow init [--ide claude\|cursor\|codex\|both\|all] [--artifact-language en\|zh-CN]` | 初始化项目目录、产物语言与 IDE 托管资产;`zh` 是 `zh-CN` 的别名 |
|
|
251
272
|
| `specflow sync [--ide ...] [--no-parity-strict]` | 从已安装的 specflow 包同步/更新 IDE 资产(升级后用) |
|
|
252
273
|
| `specflow doctor [--parity] [--json]` | 诊断 IDE 资产是否完整、迁移状态是否正常 |
|
|
253
274
|
| `specflow parity-report [--json]` | 对比 Claude / Cursor / Codex 能力是否一致 |
|
|
@@ -393,11 +414,10 @@ specflow doctor --parity
|
|
|
393
414
|
specflow parity-report
|
|
394
415
|
```
|
|
395
416
|
|
|
396
|
-
|
|
417
|
+
刷新已初始化项目的托管资产(不会覆盖 `specflow/config.yaml`):
|
|
397
418
|
|
|
398
419
|
```bash
|
|
399
|
-
|
|
400
|
-
specflow init
|
|
420
|
+
specflow init --force-assets --ide all
|
|
401
421
|
```
|
|
402
422
|
|
|
403
423
|
---
|
|
@@ -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.
|
|
@@ -18,6 +18,13 @@ description: "Two-phase apply — task rewrite + subagent TDD execution"
|
|
|
18
18
|
- Existing `tasks.md` is present (coarse first-iteration from `/specflow:propose`, or a refine-updated version).
|
|
19
19
|
- `specflow` CLI is available on PATH.
|
|
20
20
|
|
|
21
|
+
## Artifact Language Setup
|
|
22
|
+
|
|
23
|
+
Before Phase A rewrites `tasks.md`, read
|
|
24
|
+
`.claude/specflow/prompts/shared/artifact-language.md` and resolve the active
|
|
25
|
+
planning root's `specflow/config.yaml`. Apply the policy to rewritten task
|
|
26
|
+
descriptions while preserving paths, commands, code, and symbols.
|
|
27
|
+
|
|
21
28
|
---
|
|
22
29
|
|
|
23
30
|
## Phase A: Task Rewrite
|
|
@@ -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.
|