@lininn/openflow 0.1.7 → 0.1.8-beta.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 +17 -2
- package/README.zh-CN.md +17 -2
- package/dist/cli/init.js +42 -33
- package/dist/cli/status.js +3 -3
- package/dist/cli/update.js +1 -1
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +1 -1
- package/dist/core/dependency-check.d.ts +7 -1
- package/dist/core/dependency-check.js +39 -15
- package/dist/core/skill-generator.d.ts +1 -0
- package/dist/core/skill-generator.js +16 -11
- package/package.json +1 -1
- package/templates/SKILL.md +1 -1
- package/templates/brainstorming.md +7 -7
- package/templates/close.md +8 -2
- package/templates/proposal.md +7 -7
- package/templates/spec.md +10 -8
package/README.md
CHANGED
|
@@ -23,10 +23,25 @@ openflow init --tools claude
|
|
|
23
23
|
1. Detect and guide OpenSpec CLI installation
|
|
24
24
|
2. Detect Superpowers and show install instructions
|
|
25
25
|
3. Check if OpenSpec is initialized in the project
|
|
26
|
-
4. Generate openflow skills to `.claude/skills/openflow/`
|
|
26
|
+
4. Generate openflow skills to the selected tools' local skill directories, such as `.claude/skills/openflow/`, `.codex/skills/openflow/`, or `.cursor/skills/openflow/`
|
|
27
27
|
|
|
28
28
|
Supported tools: `claude`, `codex`, `cursor` (comma-separated, e.g. `--tools claude,codex`)
|
|
29
29
|
|
|
30
|
+
### Install skills globally
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
openflow init --tools claude -g
|
|
34
|
+
openflow init --tools claude,codex,cursor --global
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
With `-g` / `--global`, `openflow` installs skills under the selected tools' home directories:
|
|
38
|
+
|
|
39
|
+
| Tool | Global skill path |
|
|
40
|
+
|------|-------------------|
|
|
41
|
+
| `claude` | `~/.claude/skills/openflow/` |
|
|
42
|
+
| `codex` | `~/.codex/skills/openflow/` |
|
|
43
|
+
| `cursor` | `~/.cursor/skills/openflow/` |
|
|
44
|
+
|
|
30
45
|
### Check status
|
|
31
46
|
|
|
32
47
|
```bash
|
|
@@ -69,7 +84,7 @@ Works without them: yes, with manual-file fallback
|
|
|
69
84
|
|
|
70
85
|
| Layer | Mechanism | When missing |
|
|
71
86
|
|-------|-----------|-------------|
|
|
72
|
-
| **Init time** | Detect OpenSpec
|
|
87
|
+
| **Init time** | Detect OpenSpec CLI from `PATH`; detect project OpenSpec in `./openspec/`; detect Superpowers in the selected tools' local/global skill dirs | Non-blocking, skills still generated |
|
|
73
88
|
| **Runtime** | Dependency check injected into SKILL.md | Build phase falls back to manual step-by-step execution |
|
|
74
89
|
|
|
75
90
|
## Architecture
|
package/README.zh-CN.md
CHANGED
|
@@ -23,10 +23,25 @@ openflow init --tools claude
|
|
|
23
23
|
1. 检测并引导安装 OpenSpec CLI
|
|
24
24
|
2. 检测 Superpowers 并提示安装方式
|
|
25
25
|
3. 检测项目 OpenSpec 初始化状态
|
|
26
|
-
4. 生成 openflow skills
|
|
26
|
+
4. 生成 openflow skills 到所选工具的项目级 skill 目录,如 `.claude/skills/openflow/`、`.codex/skills/openflow/` 或 `.cursor/skills/openflow/`
|
|
27
27
|
|
|
28
28
|
支持的工具:`claude`、`codex`、`cursor`(逗号分隔,如 `--tools claude,codex`)
|
|
29
29
|
|
|
30
|
+
### 安装到全局 skills
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
openflow init --tools claude -g
|
|
34
|
+
openflow init --tools claude,codex,cursor --global
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
加 `-g` / `--global` 后,`openflow` 会把 skills 安装到所选工具的全局目录:
|
|
38
|
+
|
|
39
|
+
| 工具 | 全局 skill 路径 |
|
|
40
|
+
|------|-----------------|
|
|
41
|
+
| `claude` | `~/.claude/skills/openflow/` |
|
|
42
|
+
| `codex` | `~/.codex/skills/openflow/` |
|
|
43
|
+
| `cursor` | `~/.cursor/skills/openflow/` |
|
|
44
|
+
|
|
30
45
|
### 查看状态
|
|
31
46
|
|
|
32
47
|
```bash
|
|
@@ -69,7 +84,7 @@ Works without them: yes, with manual-file fallback
|
|
|
69
84
|
|
|
70
85
|
| 层 | 机制 | 缺失时 |
|
|
71
86
|
|----|------|--------|
|
|
72
|
-
| **init 时** | 检测 OpenSpec
|
|
87
|
+
| **init 时** | 从 `PATH` 检测 OpenSpec CLI;从 `./openspec/` 检测当前项目 OpenSpec;从所选工具的本地/全局 skill 目录检测 Superpowers | 不阻断,继续生成 skills |
|
|
73
88
|
| **运行时** | SKILL.md 注入依赖检测段 | build 阶段降级为手动拆解步骤执行 |
|
|
74
89
|
|
|
75
90
|
## 架构
|
package/dist/cli/init.js
CHANGED
|
@@ -9,15 +9,17 @@ const SUPPORTED_TOOLS = Object.keys(TOOL_PATHS);
|
|
|
9
9
|
export const initCommand = new Command('init')
|
|
10
10
|
.description('Initialize openflow skills in the current project')
|
|
11
11
|
.option('-t, --tools <tools>', 'Target tools, comma-separated', 'claude')
|
|
12
|
+
.option('-g, --global', 'Install skills globally under home tool directories')
|
|
12
13
|
.action(async (options) => {
|
|
13
14
|
const cwd = process.cwd();
|
|
14
15
|
const tools = options.tools.split(',').map((t) => t.trim());
|
|
16
|
+
const installGlobally = Boolean(options.global);
|
|
15
17
|
logger.blank();
|
|
16
|
-
logger.info(
|
|
18
|
+
logger.info(`openflow init — ${installGlobally ? 'global skill setup' : 'workflow orchestrator setup'}`);
|
|
17
19
|
logger.blank();
|
|
18
20
|
// Step 1: Check OpenSpec
|
|
19
21
|
logger.step('Checking OpenSpec ...');
|
|
20
|
-
let depStatus = checkDependencies();
|
|
22
|
+
let depStatus = checkDependencies({ cwd, tools });
|
|
21
23
|
if (!depStatus.openspec.installed) {
|
|
22
24
|
logger.warn('OpenSpec CLI not installed');
|
|
23
25
|
const { installOpenSpec } = await inquirer.prompt([
|
|
@@ -30,7 +32,7 @@ export const initCommand = new Command('init')
|
|
|
30
32
|
]);
|
|
31
33
|
if (installOpenSpec) {
|
|
32
34
|
const ok = tryAutoInstall(DEPS.openspec.npmPkg);
|
|
33
|
-
depStatus = checkDependencies(); // recheck
|
|
35
|
+
depStatus = checkDependencies({ cwd, tools }); // recheck
|
|
34
36
|
if (ok)
|
|
35
37
|
depStatus.openspec.autoInstalled = true;
|
|
36
38
|
}
|
|
@@ -49,44 +51,51 @@ export const initCommand = new Command('init')
|
|
|
49
51
|
logger.info('Re-run openflow init after installing, or build phase will use manual fallback');
|
|
50
52
|
}
|
|
51
53
|
else {
|
|
52
|
-
logger.success(
|
|
54
|
+
logger.success(`Superpowers installed${depStatus.superpowers.path ? ` (${depStatus.superpowers.path})` : ''}`);
|
|
53
55
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
if (installGlobally) {
|
|
57
|
+
logger.step('Skipping project OpenSpec initialization for global install');
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Step 3: Check if OpenSpec is initialized in project
|
|
61
|
+
logger.step('Checking project OpenSpec initialization ...');
|
|
62
|
+
if (!checkOpenSpecInitialized(cwd)) {
|
|
63
|
+
if (depStatus.openspec.installed) {
|
|
64
|
+
const { initOpenSpec } = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
name: 'initOpenSpec',
|
|
68
|
+
message: 'OpenSpec not initialized in this project. Run openspec init?',
|
|
69
|
+
default: true,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
if (initOpenSpec) {
|
|
73
|
+
const toolsFlag = tools.map((t) => t).join(',');
|
|
74
|
+
exec(`openspec init --tools ${toolsFlag}`, { stdio: 'inherit' });
|
|
75
|
+
logger.success('OpenSpec project initialized');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.info('OpenSpec not initialized — directories will be auto-created on first /openflow proposal');
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
82
|
else {
|
|
73
|
-
logger.
|
|
83
|
+
logger.success('OpenSpec project initialized');
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
|
-
else {
|
|
77
|
-
logger.success('OpenSpec project initialized');
|
|
78
|
-
}
|
|
79
86
|
// Step 4: Generate skills
|
|
80
87
|
logger.step('Generating openflow skills ...');
|
|
81
|
-
generateSkills({ cwd, tools, depStatus });
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
generateSkills({ cwd, tools, depStatus, global: installGlobally });
|
|
89
|
+
if (!installGlobally) {
|
|
90
|
+
// Step 5: Write state
|
|
91
|
+
writeState(cwd, {
|
|
92
|
+
openspec: depStatus.openspec.installed,
|
|
93
|
+
superpowers: depStatus.superpowers.installed,
|
|
94
|
+
openspecProjectInitialized: checkOpenSpecInitialized(cwd),
|
|
95
|
+
createdAt: new Date().toISOString(),
|
|
96
|
+
tools,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
90
99
|
logger.blank();
|
|
91
100
|
logger.success('openflow initialized!');
|
|
92
101
|
logger.blank();
|
package/dist/cli/status.js
CHANGED
|
@@ -11,9 +11,10 @@ export const statusCommand = new Command('status')
|
|
|
11
11
|
logger.blank();
|
|
12
12
|
logger.info('openflow status');
|
|
13
13
|
logger.blank();
|
|
14
|
+
const state = readState(cwd);
|
|
14
15
|
// Dependencies
|
|
15
16
|
logger.step('Dependencies:');
|
|
16
|
-
const depStatus = checkDependencies();
|
|
17
|
+
const depStatus = checkDependencies({ cwd, tools: state?.tools });
|
|
17
18
|
if (depStatus.openspec.installed) {
|
|
18
19
|
logger.success(`OpenSpec CLI${depStatus.openspec.version ? ` v${depStatus.openspec.version}` : ''}`);
|
|
19
20
|
}
|
|
@@ -21,7 +22,7 @@ export const statusCommand = new Command('status')
|
|
|
21
22
|
logger.warn('OpenSpec CLI — not installed');
|
|
22
23
|
}
|
|
23
24
|
if (depStatus.superpowers.installed) {
|
|
24
|
-
logger.success(
|
|
25
|
+
logger.success(`Superpowers${depStatus.superpowers.path ? ` (${depStatus.superpowers.path})` : ''}`);
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
27
28
|
logger.warn('Superpowers — not installed (build phase will use manual mode)');
|
|
@@ -29,7 +30,6 @@ export const statusCommand = new Command('status')
|
|
|
29
30
|
logger.blank();
|
|
30
31
|
// Project state
|
|
31
32
|
logger.step('Project:');
|
|
32
|
-
const state = readState(cwd);
|
|
33
33
|
if (state) {
|
|
34
34
|
logger.success(`Initialized (${state.tools.join(', ')})`);
|
|
35
35
|
logger.info(` Created at: ${state.createdAt}`);
|
package/dist/cli/update.js
CHANGED
|
@@ -14,7 +14,7 @@ export const updateCommand = new Command('update')
|
|
|
14
14
|
logger.blank();
|
|
15
15
|
logger.info('openflow update — regenerating skills');
|
|
16
16
|
logger.blank();
|
|
17
|
-
const depStatus = checkDependencies();
|
|
17
|
+
const depStatus = checkDependencies({ cwd, tools: state.tools });
|
|
18
18
|
generateSkills({
|
|
19
19
|
cwd,
|
|
20
20
|
tools: state.tools,
|
package/dist/core/constants.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const DEPS: {
|
|
|
13
13
|
readonly superpowers: {
|
|
14
14
|
readonly name: "Superpowers";
|
|
15
15
|
readonly checkPath: "writing-plans/SKILL.md";
|
|
16
|
-
readonly installHint: "
|
|
16
|
+
readonly installHint: "请在当前工具中安装 Superpowers writing-plans skill(Claude Code: /plugin install superpowers@claude-plugins-official)";
|
|
17
17
|
readonly autoInstallable: false;
|
|
18
18
|
};
|
|
19
19
|
};
|
package/dist/core/constants.js
CHANGED
|
@@ -13,7 +13,7 @@ export const DEPS = {
|
|
|
13
13
|
superpowers: {
|
|
14
14
|
name: 'Superpowers',
|
|
15
15
|
checkPath: 'writing-plans/SKILL.md',
|
|
16
|
-
installHint: '
|
|
16
|
+
installHint: '请在当前工具中安装 Superpowers writing-plans skill(Claude Code: /plugin install superpowers@claude-plugins-official)',
|
|
17
17
|
autoInstallable: false,
|
|
18
18
|
},
|
|
19
19
|
};
|
|
@@ -7,9 +7,15 @@ export interface DepStatus {
|
|
|
7
7
|
superpowers: {
|
|
8
8
|
installed: boolean;
|
|
9
9
|
hint?: string;
|
|
10
|
+
path?: string;
|
|
11
|
+
checkedPaths: string[];
|
|
10
12
|
};
|
|
11
13
|
}
|
|
12
|
-
export
|
|
14
|
+
export interface CheckDependencyOptions {
|
|
15
|
+
cwd?: string;
|
|
16
|
+
tools?: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function checkDependencies(options?: CheckDependencyOptions): DepStatus;
|
|
13
19
|
export declare function tryAutoInstall(pkg: string): boolean;
|
|
14
20
|
export declare function checkOpenSpecInitialized(cwd: string): boolean;
|
|
15
21
|
export interface InitState {
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import { cmdExists, fileExists, dirExists, exec } from '../utils/shell.js';
|
|
3
|
-
import { DEPS } from './constants.js';
|
|
3
|
+
import { DEPS, TOOL_PATHS } from './constants.js';
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import fs from 'fs';
|
|
8
|
-
export function checkDependencies() {
|
|
8
|
+
export function checkDependencies(options = {}) {
|
|
9
9
|
const home = os.homedir();
|
|
10
|
+
const cwd = options.cwd ?? process.cwd();
|
|
11
|
+
const tools = options.tools?.length ? options.tools : Object.keys(TOOL_PATHS);
|
|
10
12
|
// Check OpenSpec
|
|
11
13
|
const openspecInstalled = cmdExists(DEPS.openspec.cliCmd);
|
|
12
14
|
let openspecVersion;
|
|
13
15
|
if (openspecInstalled) {
|
|
14
16
|
openspecVersion = exec('openspec --version') || undefined;
|
|
15
17
|
}
|
|
16
|
-
// Check Superpowers
|
|
17
|
-
const
|
|
18
|
-
const
|
|
18
|
+
// Check Superpowers in the selected tools' local and global skill dirs.
|
|
19
|
+
const superpowersSkillPaths = getSuperpowersSkillPaths(cwd, home, tools);
|
|
20
|
+
const superpowersSkillPath = superpowersSkillPaths.find((candidate) => fs.existsSync(candidate));
|
|
21
|
+
const superpowersInstalled = Boolean(superpowersSkillPath);
|
|
19
22
|
return {
|
|
20
23
|
openspec: {
|
|
21
24
|
installed: openspecInstalled,
|
|
@@ -24,9 +27,22 @@ export function checkDependencies() {
|
|
|
24
27
|
superpowers: {
|
|
25
28
|
installed: superpowersInstalled,
|
|
26
29
|
hint: superpowersInstalled ? undefined : DEPS.superpowers.installHint,
|
|
30
|
+
path: superpowersSkillPath,
|
|
31
|
+
checkedPaths: superpowersSkillPaths,
|
|
27
32
|
},
|
|
28
33
|
};
|
|
29
34
|
}
|
|
35
|
+
function getSuperpowersSkillPaths(cwd, home, tools) {
|
|
36
|
+
const candidates = new Set();
|
|
37
|
+
for (const tool of tools) {
|
|
38
|
+
const toolPaths = TOOL_PATHS[tool];
|
|
39
|
+
if (!toolPaths)
|
|
40
|
+
continue;
|
|
41
|
+
candidates.add(path.join(cwd, toolPaths.skillsDir, DEPS.superpowers.checkPath));
|
|
42
|
+
candidates.add(path.join(home, toolPaths.skillsDir, DEPS.superpowers.checkPath));
|
|
43
|
+
}
|
|
44
|
+
return [...candidates];
|
|
45
|
+
}
|
|
30
46
|
export function tryAutoInstall(pkg) {
|
|
31
47
|
logger.step(`Installing ${pkg} ...`);
|
|
32
48
|
try {
|
|
@@ -43,21 +59,29 @@ export function checkOpenSpecInitialized(cwd) {
|
|
|
43
59
|
return dirExists(path.join(cwd, 'openspec'));
|
|
44
60
|
}
|
|
45
61
|
export function readState(cwd) {
|
|
46
|
-
const stateFile
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
for (const stateFile of getStateFileCandidates(cwd)) {
|
|
63
|
+
if (!fileExists(stateFile))
|
|
64
|
+
continue;
|
|
65
|
+
try {
|
|
66
|
+
return JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
54
71
|
}
|
|
72
|
+
return null;
|
|
55
73
|
}
|
|
56
74
|
export function writeState(cwd, state) {
|
|
57
|
-
const stateDir = path.join(cwd, '.
|
|
75
|
+
const stateDir = path.join(cwd, '.openflow');
|
|
58
76
|
if (!dirExists(stateDir)) {
|
|
59
77
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
60
78
|
}
|
|
61
|
-
const stateFile = path.join(stateDir, '
|
|
79
|
+
const stateFile = path.join(stateDir, 'state.json');
|
|
62
80
|
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2) + '\n');
|
|
63
81
|
}
|
|
82
|
+
function getStateFileCandidates(cwd) {
|
|
83
|
+
return [
|
|
84
|
+
path.join(cwd, '.openflow', 'state.json'),
|
|
85
|
+
path.join(cwd, '.claude', 'openflow-state.json'),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
|
-
import { fileExists
|
|
5
|
+
import { fileExists } from '../utils/shell.js';
|
|
5
6
|
import { logger } from '../utils/logger.js';
|
|
6
7
|
import { SKILL_NAME, TOOL_PATHS, DEPS } from './constants.js';
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -9,16 +10,20 @@ const __dirname = path.dirname(__filename);
|
|
|
9
10
|
// Resolve templates dir: from dist/core/ → ../../templates/
|
|
10
11
|
const TEMPLATES_DIR = path.resolve(__dirname, '..', '..', 'templates');
|
|
11
12
|
export function generateSkills(options) {
|
|
12
|
-
const { cwd, tools, depStatus } = options;
|
|
13
|
+
const { cwd, tools, depStatus, global = false } = options;
|
|
14
|
+
const baseDir = global ? os.homedir() : cwd;
|
|
13
15
|
for (const tool of tools) {
|
|
14
16
|
const toolPaths = TOOL_PATHS[tool];
|
|
15
17
|
if (!toolPaths) {
|
|
16
18
|
logger.warn(`Unknown tool: ${tool}, skipping`);
|
|
17
19
|
continue;
|
|
18
20
|
}
|
|
19
|
-
const skillsDir = path.join(
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const skillsDir = path.join(baseDir, toolPaths.skillsDir, SKILL_NAME);
|
|
22
|
+
const displayPath = global
|
|
23
|
+
? path.join('~', toolPaths.skillsDir, SKILL_NAME)
|
|
24
|
+
: path.relative(cwd, skillsDir);
|
|
25
|
+
logger.step(`Generating ${tool} skills to ${displayPath}/`);
|
|
26
|
+
if (!fs.existsSync(skillsDir)) {
|
|
22
27
|
fs.mkdirSync(skillsDir, { recursive: true });
|
|
23
28
|
}
|
|
24
29
|
// Generate main SKILL.md
|
|
@@ -61,7 +66,7 @@ function injectRuntimeDepCheck(content, depStatus) {
|
|
|
61
66
|
|
|
62
67
|
| 依赖 | 检测方式 | 不可用时 |
|
|
63
68
|
|------|----------|----------|
|
|
64
|
-
| Superpowers writing-plans |
|
|
69
|
+
| Superpowers writing-plans | 当前工具的本地或全局 skills 目录下是否存在 \`writing-plans/SKILL.md\` | 降级为手动拆解 plan-ready.md 中的步骤,逐条执行 |
|
|
65
70
|
| OpenSpec CLI | \`openspec\` 命令是否可执行 | 不影响 build 阶段,但 close 阶段归档需手动 mv |
|
|
66
71
|
|
|
67
72
|
如果 Superpowers 不可用,提示用户:
|
|
@@ -82,12 +87,12 @@ function injectRuntimeDepCheck(content, depStatus) {
|
|
|
82
87
|
}
|
|
83
88
|
function injectSpecRuntimeCheck(content, depStatus) {
|
|
84
89
|
const checkNote = `
|
|
85
|
-
> **OpenSpec
|
|
90
|
+
> **OpenSpec 检测**:根据 proposal.md 生成 design.md + specs/ + tasks.md;如果 \`openspec\` CLI 可用,生成后运行 \`openspec validate <变更名> --strict\` 校验。
|
|
86
91
|
`;
|
|
87
92
|
const lines = content.split('\n');
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
lines.splice(
|
|
93
|
+
const validateIdx = lines.findIndex((l) => l.includes('openspec validate'));
|
|
94
|
+
if (validateIdx >= 0) {
|
|
95
|
+
lines.splice(validateIdx, 0, checkNote);
|
|
91
96
|
}
|
|
92
97
|
return lines.join('\n');
|
|
93
98
|
}
|
|
@@ -136,7 +141,7 @@ description: "OpenSpec + Superpowers 工作流协调器。使用 /openflow propo
|
|
|
136
141
|
|
|
137
142
|
1. 如果用户指定了子命令(如 \`/openflow build\`),优先按指定阶段执行,但检查前置条件
|
|
138
143
|
2. 如果用户只输入 \`/openflow\`,执行状态检测,自动路由到对应阶段
|
|
139
|
-
3.
|
|
144
|
+
3. 读取当前 openflow skill 目录下的阶段文件:\`<阶段>.md\`(与本 \`SKILL.md\` 同目录;不要依赖 Claude 专属环境变量)
|
|
140
145
|
4. 按阶段文件中的流程执行
|
|
141
146
|
|
|
142
147
|
### 前置条件检查
|
package/package.json
CHANGED
package/templates/SKILL.md
CHANGED
|
@@ -41,7 +41,7 @@ description: "OpenSpec + Superpowers workflow orchestrator. Use /openflow propos
|
|
|
41
41
|
|
|
42
42
|
1. 如果用户指定了子命令(如 `/openflow build`),优先按指定阶段执行,但检查前置条件
|
|
43
43
|
2. 如果用户只输入 `/openflow`,执行状态检测,自动路由到对应阶段
|
|
44
|
-
3.
|
|
44
|
+
3. 读取当前 openflow skill 目录下的阶段文件:`<阶段>.md`(与本 `SKILL.md` 同目录;不要依赖 Claude 专属环境变量)
|
|
45
45
|
4. 按阶段文件中的流程执行
|
|
46
46
|
|
|
47
47
|
### 前置条件检查
|
|
@@ -37,22 +37,22 @@ description: Deep design — multi-round exploration to confirm architecture and
|
|
|
37
37
|
|
|
38
38
|
> "确认的设计方向:[方案名]。核心决策:[2-3 条]。这样对吗?"
|
|
39
39
|
|
|
40
|
-
### 5. 创建 OpenSpec
|
|
40
|
+
### 5. 创建 OpenSpec 变更目录
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
用户确认后,按 OpenSpec 目录约定创建变更。`<变更名>` 使用 kebab-case、动词开头(如 `add-user-login`):
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
|
|
45
|
+
mkdir -p openspec/changes/<变更名>/specs
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
将确认的需求描述和设计方向写入 `openspec/changes/<变更名>/proposal.md`。
|
|
49
|
+
|
|
50
|
+
如果 OpenSpec CLI 可用,可用以下命令检查当前变更列表:
|
|
49
51
|
|
|
50
52
|
```bash
|
|
51
|
-
|
|
53
|
+
openspec list
|
|
52
54
|
```
|
|
53
55
|
|
|
54
|
-
将确认的需求描述和设计方向写入 `openspec/changes/<变更名>/proposal.md`。
|
|
55
|
-
|
|
56
56
|
### 6. 提示下一步
|
|
57
57
|
|
|
58
58
|
> "需求已记录。接下来可以用 `/openflow spec` 生成完整规格。"
|
package/templates/close.md
CHANGED
|
@@ -52,10 +52,16 @@ description: Verify implementation consistency and archive
|
|
|
52
52
|
|
|
53
53
|
### 5. 归档
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
全部一致(或用户接受不一致项)后,先校验变更:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
openspec
|
|
58
|
+
openspec validate <变更名> --strict
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
校验通过后执行归档:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
openspec archive <变更名> --yes
|
|
59
65
|
```
|
|
60
66
|
|
|
61
67
|
如果 OpenSpec CLI 不可用,手动归档:
|
package/templates/proposal.md
CHANGED
|
@@ -27,22 +27,22 @@ description: Lightweight requirement capture — 3-5 questions to quickly conver
|
|
|
27
27
|
|
|
28
28
|
> "我理解的需求是:[一句话概括]。具体来说:[2-3 条要点]。这样理解对吗?"
|
|
29
29
|
|
|
30
|
-
### 3. 创建 OpenSpec
|
|
30
|
+
### 3. 创建 OpenSpec 变更目录
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
用户确认后,按 OpenSpec 目录约定创建变更。`<变更名>` 使用 kebab-case、动词开头(如 `add-user-login`):
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
|
|
35
|
+
mkdir -p openspec/changes/<变更名>/specs
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
将确认的需求描述写入 `openspec/changes/<变更名>/proposal.md`。
|
|
39
|
+
|
|
40
|
+
如果 OpenSpec CLI 可用,可用以下命令检查当前变更列表:
|
|
39
41
|
|
|
40
42
|
```bash
|
|
41
|
-
|
|
43
|
+
openspec list
|
|
42
44
|
```
|
|
43
45
|
|
|
44
|
-
将确认的需求描述写入 `openspec/changes/<变更名>/proposal.md`。
|
|
45
|
-
|
|
46
46
|
### 4. 提示下一步
|
|
47
47
|
|
|
48
48
|
> "需求已记录。接下来可以用 `/openflow spec` 生成完整规格,或继续补充细节。"
|
package/templates/spec.md
CHANGED
|
@@ -26,21 +26,23 @@ description: Call OpenSpec to generate specs, auto-translate to plan-ready.md af
|
|
|
26
26
|
如果有多个,列出并让用户选择:
|
|
27
27
|
> "检测到多个活跃变更:[列表]。要对哪个生成规格?"
|
|
28
28
|
|
|
29
|
-
### 2.
|
|
29
|
+
### 2. 生成 OpenSpec 规格文件
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
openspec propose <变更名>
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
如果 OpenSpec CLI 不可用,手动根据 proposal.md 的内容生成以下文件:
|
|
31
|
+
根据 proposal.md 的内容生成或补齐以下文件:
|
|
38
32
|
|
|
39
33
|
- `openspec/changes/<变更名>/proposal.md` — 已存在,可补充
|
|
40
34
|
- `openspec/changes/<变更名>/design.md` — 技术方案
|
|
41
35
|
- `openspec/changes/<变更名>/specs/` — 具体规格变更(标记新增/修改/删除)
|
|
42
36
|
- `openspec/changes/<变更名>/tasks.md` — 实现任务清单
|
|
43
37
|
|
|
38
|
+
如果 OpenSpec CLI 可用,生成后运行校验:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
openspec validate <变更名> --strict
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
如果校验失败,根据错误修正上述文件后重新校验。
|
|
45
|
+
|
|
44
46
|
### 3. 与用户确认规格
|
|
45
47
|
|
|
46
48
|
展示规格摘要,逐项确认:
|