@gordon.gan/specflow 1.3.1-beta → 1.3.3-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 +1 -1
- package/dist/core/artifact-language.js +3 -1
- package/dist/core/opener-launch.d.ts +6 -5
- package/dist/core/opener-launch.js +56 -12
- package/package.json +1 -1
- package/prompts/apply/phase-a-plan.md +4 -0
- package/prompts/propose/design-draft.md +2 -0
- package/prompts/propose/proposal.md +2 -0
- package/prompts/propose/specs.md +4 -2
- package/prompts/propose/tasks-draft.md +9 -8
- package/prompts/shared/artifact-language.md +7 -1
package/README.md
CHANGED
|
@@ -124,7 +124,7 @@ npm install -g @gordon.gan/specflow
|
|
|
124
124
|
npm install -g github:Gordon-Gan-Jiang/specflow
|
|
125
125
|
|
|
126
126
|
# 验证
|
|
127
|
-
specflow --version # 以 npm / package.json 为准(当前 1.3.
|
|
127
|
+
specflow --version # 以 npm / package.json 为准(当前 1.3.3-beta)
|
|
128
128
|
specflow --help
|
|
129
129
|
```
|
|
130
130
|
|
|
@@ -21,7 +21,9 @@ export function renderArtifactLanguageGuidance(language) {
|
|
|
21
21
|
return [
|
|
22
22
|
'## Artifact language policy',
|
|
23
23
|
'',
|
|
24
|
-
`Write
|
|
24
|
+
`Write all non-protocol business content in ${narrativeLanguage}.`,
|
|
25
|
+
'This includes proposal narrative, requirement and scenario descriptions, text after WHEN/THEN, design rationale, and task descriptions.',
|
|
26
|
+
'Do not mix narrative languages in the same artifact.',
|
|
25
27
|
'',
|
|
26
28
|
'Keep SpecFlow protocol markers unchanged, including:',
|
|
27
29
|
'- `ADDED Requirements`, `MODIFIED Requirements`, `REMOVED Requirements`, and `RENAMED Requirements`',
|
|
@@ -5,14 +5,15 @@ export interface OpenerSpawnPlan {
|
|
|
5
5
|
readonly args: string[];
|
|
6
6
|
readonly options: SpawnSyncOptions;
|
|
7
7
|
}
|
|
8
|
+
export declare function selectWindowsOpenerPath(output: string): string | null;
|
|
8
9
|
/**
|
|
9
10
|
* Build spawn argv for launching a workspace-file opener.
|
|
10
11
|
*
|
|
11
|
-
* On Windows, Cursor/VS Code ship as `*.cmd` shims.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* On Windows, Cursor/VS Code normally ship as `*.cmd` shims. Resolve the shim
|
|
13
|
+
* to an absolute path first: invoking a bare command while cwd is a project
|
|
14
|
+
* can make the shim resolve its own relative Cursor.exe path from the project.
|
|
15
|
+
* Batch shims run through cmd.exe; native executables run directly.
|
|
15
16
|
*/
|
|
16
|
-
export declare function buildOpenerSpawn(opener: OpenerDefinition, workspacePath: string, primaryPath: string, platform?: NodeJS.Platform, comSpec?: string): OpenerSpawnPlan;
|
|
17
|
+
export declare function buildOpenerSpawn(opener: OpenerDefinition, workspacePath: string, primaryPath: string, platform?: NodeJS.Platform, comSpec?: string, resolvedPath?: string): OpenerSpawnPlan;
|
|
17
18
|
export declare function isOpenerAvailable(opener: OpenerDefinition): boolean;
|
|
18
19
|
export declare function launchOpener(opener: OpenerDefinition, workspacePath: string, primaryPath: string): void;
|
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function selectWindowsOpenerPath(output) {
|
|
4
|
+
return (output
|
|
5
|
+
.split(/\r?\n/)
|
|
6
|
+
.map((candidate) => candidate.trim())
|
|
7
|
+
.find((candidate) => /\.(?:cmd|bat|exe)$/i.test(candidate)) ?? null);
|
|
8
|
+
}
|
|
9
|
+
function resolveWindowsOpenerPath(opener) {
|
|
10
|
+
for (const suffix of ['.cmd', '.bat', '.exe']) {
|
|
11
|
+
const result = spawnSync('where.exe', [`${opener.command}${suffix}`], {
|
|
12
|
+
encoding: 'utf8',
|
|
13
|
+
shell: false,
|
|
14
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
15
|
+
});
|
|
16
|
+
if (result.status === 0) {
|
|
17
|
+
const executablePath = selectWindowsOpenerPath(result.stdout ?? '');
|
|
18
|
+
if (executablePath) {
|
|
19
|
+
return executablePath;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
2
25
|
/**
|
|
3
26
|
* Build spawn argv for launching a workspace-file opener.
|
|
4
27
|
*
|
|
5
|
-
* On Windows, Cursor/VS Code ship as `*.cmd` shims.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
28
|
+
* On Windows, Cursor/VS Code normally ship as `*.cmd` shims. Resolve the shim
|
|
29
|
+
* to an absolute path first: invoking a bare command while cwd is a project
|
|
30
|
+
* can make the shim resolve its own relative Cursor.exe path from the project.
|
|
31
|
+
* Batch shims run through cmd.exe; native executables run directly.
|
|
9
32
|
*/
|
|
10
|
-
export function buildOpenerSpawn(opener, workspacePath, primaryPath, platform = process.platform, comSpec = process.env.ComSpec || 'cmd.exe') {
|
|
33
|
+
export function buildOpenerSpawn(opener, workspacePath, primaryPath, platform = process.platform, comSpec = process.env.ComSpec || 'cmd.exe', resolvedPath) {
|
|
11
34
|
const baseOptions = {
|
|
12
35
|
cwd: primaryPath,
|
|
13
36
|
shell: false,
|
|
@@ -20,27 +43,48 @@ export function buildOpenerSpawn(opener, workspacePath, primaryPath, platform =
|
|
|
20
43
|
options: baseOptions,
|
|
21
44
|
};
|
|
22
45
|
}
|
|
23
|
-
|
|
24
|
-
|
|
46
|
+
if (!resolvedPath) {
|
|
47
|
+
throw new Error(`Could not resolve '${opener.command}.cmd' or '${opener.command}.exe' on PATH.`);
|
|
48
|
+
}
|
|
49
|
+
const executablePath = path.win32.normalize(resolvedPath);
|
|
50
|
+
const extension = path.win32.extname(executablePath).toLowerCase();
|
|
51
|
+
const shimOptions = {
|
|
52
|
+
...baseOptions,
|
|
53
|
+
// Cursor's .cmd shim derives Cursor.exe from its own directory.
|
|
54
|
+
cwd: path.win32.dirname(executablePath),
|
|
55
|
+
};
|
|
56
|
+
if (extension === '.exe') {
|
|
57
|
+
return {
|
|
58
|
+
command: executablePath,
|
|
59
|
+
args: [workspacePath],
|
|
60
|
+
options: shimOptions,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// cmd.exe's /s removes the outer pair of quotes. Supply that pair explicitly
|
|
64
|
+
// so the batch-file path and workspace path retain their independent quotes.
|
|
65
|
+
const commandLine = `""${executablePath.replace(/"/g, '""')}" "${workspacePath.replace(/"/g, '""')}""`;
|
|
25
66
|
return {
|
|
26
67
|
command: comSpec,
|
|
27
|
-
args: ['/d', '/s', '/c',
|
|
68
|
+
args: ['/d', '/s', '/c', commandLine],
|
|
28
69
|
options: {
|
|
29
|
-
...
|
|
70
|
+
...shimOptions,
|
|
30
71
|
windowsVerbatimArguments: true,
|
|
31
72
|
},
|
|
32
73
|
};
|
|
33
74
|
}
|
|
34
75
|
export function isOpenerAvailable(opener) {
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
if (process.platform === 'win32') {
|
|
77
|
+
return resolveWindowsOpenerPath(opener) !== null;
|
|
78
|
+
}
|
|
79
|
+
const result = spawnSync('which', [opener.command], {
|
|
37
80
|
stdio: 'ignore',
|
|
38
81
|
shell: false,
|
|
39
82
|
});
|
|
40
83
|
return result.status === 0;
|
|
41
84
|
}
|
|
42
85
|
export function launchOpener(opener, workspacePath, primaryPath) {
|
|
43
|
-
const
|
|
86
|
+
const resolvedPath = process.platform === 'win32' ? resolveWindowsOpenerPath(opener) : undefined;
|
|
87
|
+
const plan = buildOpenerSpawn(opener, workspacePath, primaryPath, process.platform, process.env.ComSpec || 'cmd.exe', resolvedPath ?? undefined);
|
|
44
88
|
const child = spawnSync(plan.command, plan.args, plan.options);
|
|
45
89
|
if (child.error) {
|
|
46
90
|
throw child.error;
|
package/package.json
CHANGED
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
**Plan location (input and output):** `specflow/changes/<change-name>/tasks.md`
|
|
17
17
|
- (User preferences for propose location override this default)
|
|
18
18
|
|
|
19
|
+
## Artifact Language
|
|
20
|
+
|
|
21
|
+
Before rewriting, resolve `artifacts.language` from the active planning root's `specflow/config.yaml` (see shared `artifact-language.md`). Rewrite **group titles and task description prose** in that language (`zh-CN` → Simplified Chinese; `en` → English). Keep file paths, commands, code blocks, symbols, and Verify command text unchanged. Do not switch business narrative to English when `zh-CN` is configured.
|
|
22
|
+
|
|
19
23
|
## Input Order
|
|
20
24
|
|
|
21
25
|
Phase A MUST read inputs in the following order before making any analysis decisions:
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
Generate a substantive `design.md` representing your **first round of deep thinking** on this change. This is v1 of the design — refine will produce v2 by challenging assumptions, proposing alternative options, probing edge cases, and questioning scope. Your job here is to give refine something real to work with.
|
|
8
8
|
|
|
9
|
+
**Narrative language:** Follow the resolved `artifacts.language` for Context, Goals / Non-Goals, Decisions, Risks, and Open Questions. Keep paths, commands, code, symbols, and technical identifiers unchanged.
|
|
10
|
+
|
|
9
11
|
## Inputs You Must Read
|
|
10
12
|
|
|
11
13
|
- `specflow/changes/<change-name>/proposal.md` — the approved proposal
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Generate a proposal document following the SpecFlow format. The proposal establishes WHY a change is needed and WHAT it affects.
|
|
6
6
|
|
|
7
|
+
**Narrative language:** Follow the resolved `artifacts.language` for Why, What Changes, Impact, Capabilities prose, and success criteria. Keep change IDs, capability IDs, paths, commands, and code symbols unchanged.
|
|
8
|
+
|
|
7
9
|
## Process
|
|
8
10
|
|
|
9
11
|
### Step 1: Baseline Awareness
|
package/prompts/propose/specs.md
CHANGED
|
@@ -27,6 +27,8 @@ Create one spec file per capability at `specflow/changes/<change-name>/specs/<ca
|
|
|
27
27
|
|
|
28
28
|
**CRITICAL FORMAT — the specflow CLI parses this exact structure. Deviations will cause validation and archive failures.**
|
|
29
29
|
|
|
30
|
+
**Narrative language:** Follow the resolved `artifacts.language` for all business content — requirement descriptions, scenario titles, and the text after `WHEN` / `THEN`. Protocol markers below stay exactly English. Do not emit English narrative when `zh-CN` is configured.
|
|
31
|
+
|
|
30
32
|
Use `##` headers for delta operation sections. Use `###` for requirements. Use `####` for scenarios. Use `- **WHEN**` and `- **THEN**` bullet format for scenario steps.
|
|
31
33
|
|
|
32
34
|
```markdown
|
|
@@ -66,9 +68,9 @@ TO: <new requirement name>
|
|
|
66
68
|
|
|
67
69
|
**Format rules:**
|
|
68
70
|
- Each requirement: `### Requirement: <name>` followed by description
|
|
69
|
-
- Use SHALL/MUST for normative requirements (avoid should/may)
|
|
71
|
+
- Use SHALL/MUST (or 必须/应当 under zh-CN) for normative requirements (avoid should/may)
|
|
70
72
|
- Each scenario: `#### Scenario: <name>` — **MUST use exactly 4 hashtags (`####`)**. Using 3 hashtags will fail validation silently.
|
|
71
|
-
- Scenario steps: `- **WHEN** <condition>` and `- **THEN** <outcome>` — **MUST use bullet dash + bold format
|
|
73
|
+
- Scenario steps: `- **WHEN** <condition>` and `- **THEN** <outcome>` — **MUST use bullet dash + bold format**; `<condition>` / `<outcome>` use the resolved artifact language
|
|
72
74
|
- Every requirement MUST have at least one scenario
|
|
73
75
|
- Only include the sections you need (e.g., only `## ADDED Requirements` for new capabilities)
|
|
74
76
|
|
|
@@ -30,15 +30,15 @@ This marker tells future readers (and the Apply Phase A prompt) that this docume
|
|
|
30
30
|
- Group names are capability- or layer-oriented, not generic ("Core artifact management" — not "Phase 1")
|
|
31
31
|
|
|
32
32
|
2. **Each group contains 2 to 6 concrete checkbox items**
|
|
33
|
-
- Format: `- [ ] N.M
|
|
34
|
-
-
|
|
33
|
+
- Format: `- [ ] N.M <action phrase>` where N is the group number and M is the item index within the group
|
|
34
|
+
- Group titles and task descriptions MUST follow the resolved `artifacts.language` from Artifact Language Setup (zh-CN → Simplified Chinese; en → English). Keep paths, commands, code, and symbols unchanged. Do not default to English when `zh-CN` is configured, and do not mix narrative languages inside the same tasks.md.
|
|
35
35
|
|
|
36
36
|
3. **Items are concrete actions, not topics**
|
|
37
|
-
- "实现 searchNotes 纯函数"
|
|
38
|
-
- "添加 CLI --verbose 标志并接入 logger"
|
|
39
|
-
-
|
|
40
|
-
- "handle searching" (
|
|
41
|
-
- "完善测试"
|
|
37
|
+
- zh-CN good: "实现 searchNotes 纯函数"
|
|
38
|
+
- zh-CN good: "添加 CLI --verbose 标志并接入 logger"
|
|
39
|
+
- en good: "Implement searchNotes pure function"
|
|
40
|
+
- bad: "处理搜索逻辑" / "handle searching" (vague topic, not an action)
|
|
41
|
+
- bad: "完善测试" / "improve tests" (improvement verb without a subject)
|
|
42
42
|
|
|
43
43
|
4. **Each item should be a coherent unit of work** — roughly the size that Apply Phase A will later expand into a handful of bite-sized TDD steps. If an item feels like it's really 5 unrelated actions, split it.
|
|
44
44
|
|
|
@@ -55,7 +55,7 @@ If the change has both parallel capabilities and deep layering, a two-level grou
|
|
|
55
55
|
|
|
56
56
|
## Example — Good Granularity
|
|
57
57
|
|
|
58
|
-
Below is a short worked example for a hypothetical "add search to notes CLI" change. Study the **granularity and verb concreteness**, not the specific content.
|
|
58
|
+
Below is a short worked example for a hypothetical "add search to notes CLI" change when `artifacts.language` is `zh-CN`. Study the **granularity and verb concreteness**, not the specific content. If the active language is `en`, write the same structure entirely in English.
|
|
59
59
|
|
|
60
60
|
```markdown
|
|
61
61
|
<!-- Plan-phase first-iteration tasks. Will be rewritten to writing-plans precision in /specflow:apply Phase A. -->
|
|
@@ -119,5 +119,6 @@ specflow/changes/<change-name>/tasks.md
|
|
|
119
119
|
- Substantive, not placeholder
|
|
120
120
|
- 3 to 8 groups, 2 to 6 items each
|
|
121
121
|
- Concrete verbs on concrete subjects
|
|
122
|
+
- Business narrative language follows `artifacts.language`; protocol/tech tokens stay unchanged
|
|
122
123
|
- Top-of-file marker comment is mandatory
|
|
123
124
|
- Writing-plans precision comes in Apply Phase A — do not pre-write it here
|
|
@@ -8,7 +8,13 @@ Before creating or rewriting any SpecFlow artifact:
|
|
|
8
8
|
- `zh-CN` → write human-readable artifact content in Simplified Chinese.
|
|
9
9
|
- Missing → default to `en`.
|
|
10
10
|
- Invalid → report the invalid value and use `en`; do not invent another language.
|
|
11
|
-
3. Apply the selected language
|
|
11
|
+
3. Apply the selected language to **all non-protocol business content**, including:
|
|
12
|
+
- proposal Why / What / Impact / success criteria
|
|
13
|
+
- requirement descriptions and scenario titles
|
|
14
|
+
- text after protected `WHEN` / `THEN` markers
|
|
15
|
+
- design Context, Goals, Decisions, Risks, Open Questions
|
|
16
|
+
- task group titles and task descriptions (including Apply Phase A rewrites)
|
|
17
|
+
4. Do **not** mix languages for that business content. Protocol markers and technical identifiers stay as written below; everything else follows the resolved language.
|
|
12
18
|
|
|
13
19
|
## Protected Protocol
|
|
14
20
|
|