@lininn/openflow 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Emerson Towne
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @lininn/openflow
2
+
3
+ OpenSpec + Superpowers 工作流协调器,串联需求规格与工程执行,消除格式鸿沟。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g @lininn/openflow
9
+ ```
10
+
11
+ ## 使用
12
+
13
+ ### 初始化项目
14
+
15
+ ```bash
16
+ cd your-project
17
+ openflow init --tools claude
18
+ ```
19
+
20
+ `init` 会自动:
21
+ 1. 检测并引导安装 OpenSpec CLI
22
+ 2. 检测 Superpowers 并提示安装方式
23
+ 3. 检测项目 OpenSpec 初始化状态
24
+ 4. 生成 openflow skills 到项目 `.claude/skills/openflow/`
25
+
26
+ 支持的工具:`claude`、`codex`、`cursor`(逗号分隔,如 `--tools claude,codex`)
27
+
28
+ ### 查看状态
29
+
30
+ ```bash
31
+ openflow status
32
+ ```
33
+
34
+ 显示依赖安装状态和项目中的活跃变更。
35
+
36
+ ### 更新 skills
37
+
38
+ ```bash
39
+ openflow update
40
+ ```
41
+
42
+ 升级 npm 包后运行,重新生成项目内的 skills 文件。
43
+
44
+ ## 工作流命令
45
+
46
+ | 命令 | 阶段 | 说明 |
47
+ |------|------|------|
48
+ | `/openflow proposal` | proposal | 轻量提问,3-5 问快速收敛需求 |
49
+ | `/openflow brainstorming` | brainstorming | 深度设计,多轮方案探索 |
50
+ | `/openflow spec` | spec | 调用 OpenSpec 生成规格 + 自动翻译 |
51
+ | `/openflow build` | build | 调用 Superpowers 执行实现 |
52
+ | `/openflow close` | close | 验证一致性 + 归档 |
53
+
54
+ ## 依赖策略
55
+
56
+ ```
57
+ Best with: OpenSpec + Superpowers
58
+ Works without them: yes, with manual-file fallback
59
+ ```
60
+
61
+ | 依赖 | 安装方式 | 缺失时降级 |
62
+ |------|----------|-----------|
63
+ | OpenSpec | `npm install -g @fission-ai/openspec@latest` | 手动创建 `openspec/changes/` 目录和文件 |
64
+ | Superpowers | `/plugin install superpowers@claude-plugins-official` | build 阶段手动拆解 plan-ready.md 步骤执行 |
65
+
66
+ ## 架构
67
+
68
+ ```
69
+ 用户需求
70
+
71
+ ├── 轻量 ──→ /openflow proposal ──┐
72
+ │ 3-5问快速收敛 │
73
+ │ ├─→ proposal.md
74
+ └── 深度 ──→ /openflow brainstorming ─┘ (openspec/changes/<name>/)
75
+ 多轮方案探索
76
+
77
+ ┌──────────▼───────────┐
78
+ │ /openflow spec │
79
+ │ OpenSpec 生成规格 │
80
+ └──────────┬───────────┘
81
+
82
+ ┌──────────▼───────────┐
83
+ │ 翻译层 (核心) │
84
+ │ 需求视角 → 工程视角 │
85
+ └──────────┬───────────┘
86
+
87
+ plan-ready.md
88
+
89
+ ┌──────────▼───────────┐
90
+ │ /openflow build │
91
+ │ Superpowers 执行 │
92
+ │ TDD 铁律 + 断点恢复 │
93
+ └──────────┬───────────┘
94
+
95
+ ┌──────────▼───────────┐
96
+ │ /openflow close │
97
+ │ 验证一致性 + 归档 │
98
+ └──────────────────────┘
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from '../dist/cli/index.js';
4
+
5
+ run();
@@ -0,0 +1 @@
1
+ export declare function run(): void;
@@ -0,0 +1,15 @@
1
+ import { Command } from 'commander';
2
+ import { initCommand } from './init.js';
3
+ import { statusCommand } from './status.js';
4
+ import { updateCommand } from './update.js';
5
+ export function run() {
6
+ const program = new Command();
7
+ program
8
+ .name('openflow')
9
+ .description('OpenSpec + Superpowers 工作流协调器')
10
+ .version('0.1.0');
11
+ program.addCommand(initCommand);
12
+ program.addCommand(statusCommand);
13
+ program.addCommand(updateCommand);
14
+ program.parse();
15
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const initCommand: Command;
@@ -0,0 +1,105 @@
1
+ import { Command } from 'commander';
2
+ import inquirer from 'inquirer';
3
+ import { checkDependencies, tryAutoInstall, checkOpenSpecInitialized, writeState } from '../core/dependency-check.js';
4
+ import { generateSkills } from '../core/skill-generator.js';
5
+ import { TOOL_PATHS, DEPS } from '../core/constants.js';
6
+ import { logger } from '../utils/logger.js';
7
+ import { exec } from '../utils/shell.js';
8
+ const SUPPORTED_TOOLS = Object.keys(TOOL_PATHS);
9
+ export const initCommand = new Command('init')
10
+ .description('在当前项目初始化 openflow skills')
11
+ .option('-t, --tools <tools>', '目标工具,逗号分隔', 'claude')
12
+ .action(async (options) => {
13
+ const cwd = process.cwd();
14
+ const tools = options.tools.split(',').map((t) => t.trim());
15
+ logger.blank();
16
+ logger.info('openflow init — 初始化工作流协调器');
17
+ logger.blank();
18
+ // Step 1: Check OpenSpec
19
+ logger.step('检测 OpenSpec ...');
20
+ let depStatus = checkDependencies();
21
+ if (!depStatus.openspec.installed) {
22
+ logger.warn('OpenSpec CLI 未安装');
23
+ const { installOpenSpec } = await inquirer.prompt([
24
+ {
25
+ type: 'confirm',
26
+ name: 'installOpenSpec',
27
+ message: `是否自动安装?(npm install -g ${DEPS.openspec.npmPkg}@latest)`,
28
+ default: true,
29
+ },
30
+ ]);
31
+ if (installOpenSpec) {
32
+ const ok = tryAutoInstall(DEPS.openspec.npmPkg);
33
+ depStatus = checkDependencies(); // recheck
34
+ if (ok)
35
+ depStatus.openspec.autoInstalled = true;
36
+ }
37
+ else {
38
+ logger.warn('跳过 OpenSpec 安装,spec 阶段将使用手动降级模式');
39
+ }
40
+ }
41
+ else {
42
+ logger.success(`OpenSpec CLI 已安装${depStatus.openspec.version ? ` (v${depStatus.openspec.version})` : ''}`);
43
+ }
44
+ // Step 2: Check Superpowers
45
+ logger.step('检测 Superpowers ...');
46
+ if (!depStatus.superpowers.installed) {
47
+ logger.warn('Superpowers 未安装');
48
+ logger.info(DEPS.superpowers.installHint);
49
+ logger.info('安装后可重新运行 openflow init,或 build 阶段将使用手动降级模式');
50
+ }
51
+ else {
52
+ logger.success('Superpowers 已安装');
53
+ }
54
+ // Step 3: Check if OpenSpec is initialized in project
55
+ logger.step('检测项目 OpenSpec 初始化 ...');
56
+ if (!checkOpenSpecInitialized(cwd)) {
57
+ if (depStatus.openspec.installed) {
58
+ const { initOpenSpec } = await inquirer.prompt([
59
+ {
60
+ type: 'confirm',
61
+ name: 'initOpenSpec',
62
+ message: '项目未初始化 OpenSpec,是否运行 openspec init?',
63
+ default: true,
64
+ },
65
+ ]);
66
+ if (initOpenSpec) {
67
+ const toolsFlag = tools.map((t) => t).join(',');
68
+ exec(`openspec init --tools ${toolsFlag}`, { stdio: 'inherit' });
69
+ logger.success('OpenSpec 项目初始化完成');
70
+ }
71
+ }
72
+ else {
73
+ logger.info('项目未初始化 OpenSpec,将在首次使用 /openflow proposal 时自动创建目录');
74
+ }
75
+ }
76
+ else {
77
+ logger.success('OpenSpec 项目已初始化');
78
+ }
79
+ // Step 4: Generate skills
80
+ logger.step('生成 openflow skills ...');
81
+ generateSkills({ cwd, tools, depStatus });
82
+ // Step 5: Write state
83
+ writeState(cwd, {
84
+ openspec: depStatus.openspec.installed,
85
+ superpowers: depStatus.superpowers.installed,
86
+ openspecProjectInitialized: checkOpenSpecInitialized(cwd),
87
+ createdAt: new Date().toISOString(),
88
+ tools,
89
+ });
90
+ logger.blank();
91
+ logger.success('openflow 初始化完成!');
92
+ logger.blank();
93
+ if (!depStatus.superpowers.installed) {
94
+ logger.warn('提示: Superpowers 未安装,/openflow build 将使用手动执行模式');
95
+ logger.info(` 安装方式: ${DEPS.superpowers.installHint}`);
96
+ logger.blank();
97
+ }
98
+ logger.info('可用命令:');
99
+ logger.info(' /openflow proposal 轻量需求捕获');
100
+ logger.info(' /openflow brainstorming 深度设计探索');
101
+ logger.info(' /openflow spec 生成规格 + 翻译');
102
+ logger.info(' /openflow build 执行实现');
103
+ logger.info(' /openflow close 验证归档');
104
+ logger.blank();
105
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const statusCommand: Command;
@@ -0,0 +1,79 @@
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { checkDependencies, readState, checkOpenSpecInitialized } from '../core/dependency-check.js';
5
+ import { logger } from '../utils/logger.js';
6
+ import { dirExists } from '../utils/shell.js';
7
+ export const statusCommand = new Command('status')
8
+ .description('显示依赖状态和活跃变更')
9
+ .action(() => {
10
+ const cwd = process.cwd();
11
+ logger.blank();
12
+ logger.info('openflow status');
13
+ logger.blank();
14
+ // Dependencies
15
+ logger.step('依赖:');
16
+ const depStatus = checkDependencies();
17
+ if (depStatus.openspec.installed) {
18
+ logger.success(`OpenSpec CLI${depStatus.openspec.version ? ` v${depStatus.openspec.version}` : ''}`);
19
+ }
20
+ else {
21
+ logger.warn('OpenSpec CLI — 未安装');
22
+ }
23
+ if (depStatus.superpowers.installed) {
24
+ logger.success('Superpowers');
25
+ }
26
+ else {
27
+ logger.warn('Superpowers — 未安装 (build 阶段将使用手动模式)');
28
+ }
29
+ logger.blank();
30
+ // Project state
31
+ logger.step('项目:');
32
+ const state = readState(cwd);
33
+ if (state) {
34
+ logger.success(`已初始化 (${state.tools.join(', ')})`);
35
+ logger.info(` 初始化时间: ${state.createdAt}`);
36
+ }
37
+ else {
38
+ logger.warn('未初始化,请运行 openflow init');
39
+ return;
40
+ }
41
+ if (checkOpenSpecInitialized(cwd)) {
42
+ logger.success('OpenSpec 项目已初始化');
43
+ }
44
+ else {
45
+ logger.warn('OpenSpec 项目未初始化');
46
+ }
47
+ logger.blank();
48
+ // Active changes
49
+ logger.step('活跃变更:');
50
+ const changesDir = path.join(cwd, 'openspec', 'changes');
51
+ const archiveDir = path.join(changesDir, 'archive');
52
+ if (!dirExists(changesDir)) {
53
+ logger.info(' 无');
54
+ return;
55
+ }
56
+ const entries = fs.readdirSync(changesDir, { withFileTypes: true })
57
+ .filter((d) => d.isDirectory() && d.name !== 'archive');
58
+ if (entries.length === 0) {
59
+ logger.info(' 无');
60
+ return;
61
+ }
62
+ for (const entry of entries) {
63
+ const changeDir = path.join(changesDir, entry.name);
64
+ const hasPlanReady = fs.existsSync(path.join(changeDir, 'plan-ready.md'));
65
+ const hasProposal = fs.existsSync(path.join(changeDir, 'proposal.md'));
66
+ let status = '';
67
+ if (hasPlanReady) {
68
+ status = '→ ready for /openflow build';
69
+ }
70
+ else if (hasProposal) {
71
+ status = '→ needs /openflow spec';
72
+ }
73
+ else {
74
+ status = '→ needs /openflow proposal';
75
+ }
76
+ logger.info(` ${entry.name} ${status}`);
77
+ }
78
+ logger.blank();
79
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const updateCommand: Command;
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import { checkDependencies, readState, writeState, checkOpenSpecInitialized } from '../core/dependency-check.js';
3
+ import { generateSkills } from '../core/skill-generator.js';
4
+ import { logger } from '../utils/logger.js';
5
+ export const updateCommand = new Command('update')
6
+ .description('重新生成项目内的 openflow skills')
7
+ .action(() => {
8
+ const cwd = process.cwd();
9
+ const state = readState(cwd);
10
+ if (!state) {
11
+ logger.error('项目未初始化,请先运行 openflow init');
12
+ return;
13
+ }
14
+ logger.blank();
15
+ logger.info('openflow update — 重新生成 skills');
16
+ logger.blank();
17
+ const depStatus = checkDependencies();
18
+ generateSkills({
19
+ cwd,
20
+ tools: state.tools,
21
+ depStatus,
22
+ });
23
+ writeState(cwd, {
24
+ ...state,
25
+ openspec: depStatus.openspec.installed,
26
+ superpowers: depStatus.superpowers.installed,
27
+ openspecProjectInitialized: checkOpenSpecInitialized(cwd),
28
+ });
29
+ logger.blank();
30
+ logger.success('skills 已更新');
31
+ logger.blank();
32
+ });
@@ -0,0 +1,23 @@
1
+ export declare const PKG_NAME = "@lininn/openflow";
2
+ export declare const PKG_BIN = "openflow";
3
+ export declare const SKILL_NAME = "openflow";
4
+ export declare const COMMAND_PREFIX = "/openflow";
5
+ export declare const DEPS: {
6
+ readonly openspec: {
7
+ readonly name: "OpenSpec";
8
+ readonly cliCmd: "openspec";
9
+ readonly npmPkg: "@fission-ai/openspec";
10
+ readonly installHint: "npm install -g @fission-ai/openspec@latest";
11
+ readonly autoInstallable: true;
12
+ };
13
+ readonly superpowers: {
14
+ readonly name: "Superpowers";
15
+ readonly checkPath: "writing-plans/SKILL.md";
16
+ readonly installHint: "请在 Claude Code 中执行: /plugin install superpowers@claude-plugins-official";
17
+ readonly autoInstallable: false;
18
+ };
19
+ };
20
+ export declare const TOOL_PATHS: Record<string, {
21
+ skillsDir: string;
22
+ commandsDir?: string;
23
+ }>;
@@ -0,0 +1,31 @@
1
+ export const PKG_NAME = '@lininn/openflow';
2
+ export const PKG_BIN = 'openflow';
3
+ export const SKILL_NAME = 'openflow';
4
+ export const COMMAND_PREFIX = '/openflow';
5
+ export const DEPS = {
6
+ openspec: {
7
+ name: 'OpenSpec',
8
+ cliCmd: 'openspec',
9
+ npmPkg: '@fission-ai/openspec',
10
+ installHint: 'npm install -g @fission-ai/openspec@latest',
11
+ autoInstallable: true,
12
+ },
13
+ superpowers: {
14
+ name: 'Superpowers',
15
+ checkPath: 'writing-plans/SKILL.md',
16
+ installHint: '请在 Claude Code 中执行: /plugin install superpowers@claude-plugins-official',
17
+ autoInstallable: false,
18
+ },
19
+ };
20
+ export const TOOL_PATHS = {
21
+ claude: {
22
+ skillsDir: '.claude/skills',
23
+ commandsDir: '.claude/commands',
24
+ },
25
+ codex: {
26
+ skillsDir: '.codex/skills',
27
+ },
28
+ cursor: {
29
+ skillsDir: '.cursor/skills',
30
+ },
31
+ };
@@ -0,0 +1,23 @@
1
+ export interface DepStatus {
2
+ openspec: {
3
+ installed: boolean;
4
+ version?: string;
5
+ autoInstalled?: boolean;
6
+ };
7
+ superpowers: {
8
+ installed: boolean;
9
+ hint?: string;
10
+ };
11
+ }
12
+ export declare function checkDependencies(): DepStatus;
13
+ export declare function tryAutoInstall(pkg: string): boolean;
14
+ export declare function checkOpenSpecInitialized(cwd: string): boolean;
15
+ export interface InitState {
16
+ openspec: boolean;
17
+ superpowers: boolean;
18
+ openspecProjectInitialized: boolean;
19
+ createdAt: string;
20
+ tools: string[];
21
+ }
22
+ export declare function readState(cwd: string): InitState | null;
23
+ export declare function writeState(cwd: string, state: InitState): void;
@@ -0,0 +1,63 @@
1
+ import { execSync } from 'child_process';
2
+ import { cmdExists, fileExists, dirExists, exec } from '../utils/shell.js';
3
+ import { DEPS } from './constants.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import path from 'path';
6
+ import os from 'os';
7
+ import fs from 'fs';
8
+ export function checkDependencies() {
9
+ const home = os.homedir();
10
+ // Check OpenSpec
11
+ const openspecInstalled = cmdExists(DEPS.openspec.cliCmd);
12
+ let openspecVersion;
13
+ if (openspecInstalled) {
14
+ openspecVersion = exec('openspec --version') || undefined;
15
+ }
16
+ // Check Superpowers
17
+ const superpowersSkillPath = path.join(home, '.claude', 'skills', DEPS.superpowers.checkPath);
18
+ const superpowersInstalled = fileExists(superpowersSkillPath);
19
+ return {
20
+ openspec: {
21
+ installed: openspecInstalled,
22
+ version: openspecVersion,
23
+ },
24
+ superpowers: {
25
+ installed: superpowersInstalled,
26
+ hint: superpowersInstalled ? undefined : DEPS.superpowers.installHint,
27
+ },
28
+ };
29
+ }
30
+ export function tryAutoInstall(pkg) {
31
+ logger.step(`正在安装 ${pkg} ...`);
32
+ try {
33
+ execSync(`npm install -g ${pkg}@latest`, { stdio: 'inherit' });
34
+ logger.success(`${pkg} 安装成功`);
35
+ return true;
36
+ }
37
+ catch {
38
+ logger.error(`${pkg} 安装失败,请手动执行: npm install -g ${pkg}@latest`);
39
+ return false;
40
+ }
41
+ }
42
+ export function checkOpenSpecInitialized(cwd) {
43
+ return dirExists(path.join(cwd, 'openspec'));
44
+ }
45
+ export function readState(cwd) {
46
+ const stateFile = path.join(cwd, '.claude', 'openflow-state.json');
47
+ if (!fileExists(stateFile))
48
+ return null;
49
+ try {
50
+ return JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ export function writeState(cwd, state) {
57
+ const stateDir = path.join(cwd, '.claude');
58
+ if (!dirExists(stateDir)) {
59
+ fs.mkdirSync(stateDir, { recursive: true });
60
+ }
61
+ const stateFile = path.join(stateDir, 'openflow-state.json');
62
+ fs.writeFileSync(stateFile, JSON.stringify(state, null, 2) + '\n');
63
+ }
@@ -0,0 +1,7 @@
1
+ import type { DepStatus } from './dependency-check.js';
2
+ export interface GenerateOptions {
3
+ cwd: string;
4
+ tools: string[];
5
+ depStatus: DepStatus;
6
+ }
7
+ export declare function generateSkills(options: GenerateOptions): void;
@@ -0,0 +1,154 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { fileExists, dirExists } from '../utils/shell.js';
5
+ import { logger } from '../utils/logger.js';
6
+ import { SKILL_NAME, TOOL_PATHS, DEPS } from './constants.js';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ // Resolve templates dir: from dist/core/ → ../../templates/
10
+ const TEMPLATES_DIR = path.resolve(__dirname, '..', '..', 'templates');
11
+ export function generateSkills(options) {
12
+ const { cwd, tools, depStatus } = options;
13
+ for (const tool of tools) {
14
+ const toolPaths = TOOL_PATHS[tool];
15
+ if (!toolPaths) {
16
+ logger.warn(`未知工具: ${tool},跳过`);
17
+ continue;
18
+ }
19
+ const skillsDir = path.join(cwd, toolPaths.skillsDir, SKILL_NAME);
20
+ logger.step(`生成 ${tool} skills 到 ${path.relative(cwd, skillsDir)}/`);
21
+ if (!dirExists(skillsDir)) {
22
+ fs.mkdirSync(skillsDir, { recursive: true });
23
+ }
24
+ // Generate main SKILL.md
25
+ generateSkillFile(skillsDir, 'SKILL.md', depStatus);
26
+ // Generate phase files
27
+ const phases = ['proposal', 'brainstorming', 'spec', 'build', 'close'];
28
+ for (const phase of phases) {
29
+ generateSkillFile(skillsDir, `${phase}.md`, depStatus);
30
+ }
31
+ logger.success(`${tool} skills 生成完成`);
32
+ }
33
+ }
34
+ function generateSkillFile(skillsDir, filename, depStatus) {
35
+ const templatePath = path.join(TEMPLATES_DIR, filename);
36
+ let content;
37
+ if (fileExists(templatePath)) {
38
+ content = fs.readFileSync(templatePath, 'utf-8');
39
+ }
40
+ else {
41
+ // Fallback: use inline template
42
+ content = getInlineTemplate(filename, depStatus);
43
+ }
44
+ // Inject runtime dependency checks into build.md
45
+ if (filename === 'build.md') {
46
+ content = injectRuntimeDepCheck(content, depStatus);
47
+ }
48
+ // Inject runtime dependency checks into spec.md for OpenSpec
49
+ if (filename === 'spec.md') {
50
+ content = injectSpecRuntimeCheck(content, depStatus);
51
+ }
52
+ const targetPath = path.join(skillsDir, filename);
53
+ fs.writeFileSync(targetPath, content);
54
+ logger.step(` ${filename}`);
55
+ }
56
+ function injectRuntimeDepCheck(content, depStatus) {
57
+ const checkSection = `
58
+ ### 0. 依赖检测
59
+
60
+ 执行前检查以下依赖是否可用:
61
+
62
+ | 依赖 | 检测方式 | 不可用时 |
63
+ |------|----------|----------|
64
+ | Superpowers writing-plans | \\\`~/.claude/skills/writing-plans/SKILL.md\\\` 是否存在 | 降级为手动拆解 plan-ready.md 中的步骤,逐条执行 |
65
+ | OpenSpec CLI | \\\`openspec\\\` 命令是否可执行 | 不影响 build 阶段,但 close 阶段归档需手动 mv |
66
+
67
+ 如果 Superpowers 不可用,提示用户:
68
+ > "Superpowers 未安装,build 将使用手动执行模式。安装后体验更佳:${DEPS.superpowers.installHint}"
69
+
70
+ 如果 Superpowers 可用,调用其 \\\`writing-plans\\\` skill 生成详细实现计划。
71
+ `;
72
+ // Insert after the first heading
73
+ const lines = content.split('\n');
74
+ const firstH2Idx = lines.findIndex((l) => l.startsWith('## '));
75
+ if (firstH2Idx >= 0) {
76
+ lines.splice(firstH2Idx + 1, 0, checkSection);
77
+ }
78
+ else {
79
+ lines.unshift(checkSection);
80
+ }
81
+ return lines.join('\n');
82
+ }
83
+ function injectSpecRuntimeCheck(content, depStatus) {
84
+ const checkNote = `
85
+ > **OpenSpec 检测**:如果 \\\`openspec\\\` CLI 可用,调用 \\\`openspec propose\\\` 生成完整规格;否则手动根据 proposal.md 生成 design.md + specs/ + tasks.md。
86
+ `;
87
+ const lines = content.split('\n');
88
+ const proposeIdx = lines.findIndex((l) => l.includes('openspec propose'));
89
+ if (proposeIdx >= 0) {
90
+ lines.splice(proposeIdx + 1, 0, checkNote);
91
+ }
92
+ return lines.join('\n');
93
+ }
94
+ function getInlineTemplate(filename, depStatus) {
95
+ const templates = {
96
+ 'SKILL.md': `---
97
+ name: openflow
98
+ description: "OpenSpec + Superpowers 工作流协调器。使用 /openflow proposal 轻量提问、/openflow brainstorming 深度设计、/openflow spec 生成规格、/openflow build 执行实现、/openflow close 验证归档。串联需求规格与工程执行,消除格式鸿沟。"
99
+ ---
100
+
101
+ # openflow: 工作流协调器
102
+
103
+ 根据用户调用的子命令和项目当前状态,路由到对应阶段。
104
+
105
+ ## 子命令
106
+
107
+ | 命令 | 阶段 | 说明 |
108
+ |------|------|------|
109
+ | \\\`/openflow proposal\\\` | proposal | 轻量提问,快速收敛需求 |
110
+ | \\\`/openflow brainstorming\\\` | brainstorming | 深度设计,多轮探索 |
111
+ | \\\`/openflow spec\\\` | spec | 调用 OpenSpec 生成规格 + 翻译 |
112
+ | \\\`/openflow build\\\` | build | 调用 Superpowers 执行实现 |
113
+ | \\\`/openflow close\\\` | close | 验证一致性 + 归档 |
114
+
115
+ ## 状态检测
116
+
117
+ 当用户调用 \\\`/openflow\\\` 不带子命令,或调用某个子命令需要确认前置条件时,执行以下状态检测:
118
+
119
+ | 检查项 | 怎么查 | 结果 |
120
+ |--------|--------|------|
121
+ | 有活跃变更? | \\\`openspec/changes/\\\` 下是否有非 archive 子目录 | 有→继续 |
122
+ | 有 plan-ready.md? | 变更目录下是否有 \\\`plan-ready.md\\\` | 有→看实现状态 |
123
+ | 实现已开始? | \\\`docs/superpowers/plans/\\\` 下是否有计划文件 | 有→看是否完成 |
124
+ | 实现已完成? | 计划文件全部 checkbox 已勾选 | 是→close 阶段 |
125
+
126
+ 判定结果:
127
+ - 无活跃变更 → proposal 阶段
128
+ - 有规格但无 plan-ready.md → spec 阶段(补生成翻译)
129
+ - 有 plan-ready.md 但实现未开始 → build 阶段
130
+ - 实现进行中 → 继续 build 阶段(断点恢复)
131
+ - 实现已完成 → close 阶段
132
+
133
+ ## 路由
134
+
135
+ 根据子命令或状态检测结果,读取对应阶段文件并执行:
136
+
137
+ 1. 如果用户指定了子命令(如 \\\`/openflow build\\\`),优先按指定阶段执行,但检查前置条件
138
+ 2. 如果用户只输入 \\\`/openflow\\\`,执行状态检测,自动路由到对应阶段
139
+ 3. 读取阶段文件:\\\`\\\${CLAUDE_SKILL_DIR}/<阶段>.md\\\`
140
+ 4. 按阶段文件中的流程执行
141
+
142
+ ### 前置条件检查
143
+
144
+ | 阶段 | 前置条件 | 不满足时提示 |
145
+ |------|----------|-------------|
146
+ | proposal | 无 | — |
147
+ | brainstorming | 无 | — |
148
+ | spec | 需要有活跃变更目录或有用户需求 | "请先用 /openflow proposal 或 /openflow brainstorming 描述需求" |
149
+ | build | 需要存在 plan-ready.md | "请先完成 /openflow spec 生成规格和翻译" |
150
+ | close | 需要实现已完成 | "实现尚未完成,请先用 /openflow build 执行" |
151
+ `,
152
+ };
153
+ return templates[filename] ?? `# ${filename}\n\nTODO: implement\n`;
154
+ }
@@ -0,0 +1,8 @@
1
+ export declare const logger: {
2
+ info: (msg: string) => void;
3
+ success: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ step: (msg: string) => void;
7
+ blank: () => void;
8
+ };
@@ -0,0 +1,9 @@
1
+ import chalk from 'chalk';
2
+ export const logger = {
3
+ info: (msg) => console.log(chalk.cyan('ℹ'), msg),
4
+ success: (msg) => console.log(chalk.green('✔'), msg),
5
+ warn: (msg) => console.log(chalk.yellow('⚠'), msg),
6
+ error: (msg) => console.log(chalk.red('✖'), msg),
7
+ step: (msg) => console.log(chalk.gray('→'), msg),
8
+ blank: () => console.log(),
9
+ };
@@ -0,0 +1,6 @@
1
+ export declare function exec(cmd: string, options?: {
2
+ stdio?: 'inherit' | 'pipe';
3
+ }): string;
4
+ export declare function cmdExists(cmd: string): boolean;
5
+ export declare function fileExists(path: string): boolean;
6
+ export declare function dirExists(path: string): boolean;
@@ -0,0 +1,39 @@
1
+ import { execSync } from 'child_process';
2
+ export function exec(cmd, options) {
3
+ try {
4
+ return execSync(cmd, {
5
+ encoding: 'utf-8',
6
+ stdio: options?.stdio ?? 'pipe',
7
+ }).trim();
8
+ }
9
+ catch {
10
+ return '';
11
+ }
12
+ }
13
+ export function cmdExists(cmd) {
14
+ try {
15
+ execSync(`which ${cmd}`, { encoding: 'utf-8', stdio: 'pipe' });
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function fileExists(path) {
23
+ try {
24
+ execSync(`test -f ${path}`, { stdio: 'pipe' });
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ export function dirExists(path) {
32
+ try {
33
+ execSync(`test -d ${path}`, { stdio: 'pipe' });
34
+ return true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@lininn/openflow",
3
+ "version": "0.1.0",
4
+ "description": "OpenSpec + Superpowers workflow orchestrator for Claude Code",
5
+ "bin": {
6
+ "openflow": "./bin/openflow.js"
7
+ },
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "prepublishOnly": "npm run build",
13
+ "postinstall": "node ./scripts/postinstall.js"
14
+ },
15
+ "keywords": [
16
+ "claude-code",
17
+ "openspec",
18
+ "superpowers",
19
+ "workflow",
20
+ "skill"
21
+ ],
22
+ "author": "lininn",
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/lininn/openflow.git"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ },
31
+ "files": [
32
+ "bin",
33
+ "dist",
34
+ "templates",
35
+ "scripts"
36
+ ],
37
+ "dependencies": {
38
+ "chalk": "^5.3.0",
39
+ "commander": "^12.0.0",
40
+ "inquirer": "^13.4.3",
41
+ "ora": "^8.0.0",
42
+ "yaml": "^2.4.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/inquirer": "^9.0.9",
46
+ "@types/node": "^20.0.0",
47
+ "typescript": "^5.5.0"
48
+ }
49
+ }
@@ -0,0 +1,12 @@
1
+ console.log(`
2
+ openflow — OpenSpec + Superpowers 工作流协调器
3
+
4
+ 初始化项目:
5
+ openflow init --tools claude
6
+
7
+ 查看状态:
8
+ openflow status
9
+
10
+ 更新 skills:
11
+ openflow update
12
+ `);
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: openflow
3
+ description: "OpenSpec + Superpowers 工作流协调器。使用 /openflow proposal 轻量提问、/openflow brainstorming 深度设计、/openflow spec 生成规格、/openflow build 执行实现、/openflow close 验证归档。串联需求规格与工程执行,消除格式鸿沟。"
4
+ ---
5
+
6
+ # openflow: 工作流协调器
7
+
8
+ 根据用户调用的子命令和项目当前状态,路由到对应阶段。
9
+
10
+ ## 子命令
11
+
12
+ | 命令 | 阶段 | 说明 |
13
+ |------|------|------|
14
+ | `/openflow proposal` | proposal | 轻量提问,快速收敛需求 |
15
+ | `/openflow brainstorming` | brainstorming | 深度设计,多轮探索 |
16
+ | `/openflow spec` | spec | 调用 OpenSpec 生成规格 + 翻译 |
17
+ | `/openflow build` | build | 调用 Superpowers 执行实现 |
18
+ | `/openflow close` | close | 验证一致性 + 归档 |
19
+
20
+ ## 状态检测
21
+
22
+ 当用户调用 `/openflow` 不带子命令,或调用某个子命令需要确认前置条件时,执行以下状态检测:
23
+
24
+ | 检查项 | 怎么查 | 结果 |
25
+ |--------|--------|------|
26
+ | 有活跃变更? | `openspec/changes/` 下是否有非 archive 子目录 | 有→继续 |
27
+ | 有 plan-ready.md? | 变更目录下是否有 `plan-ready.md` | 有→看实现状态 |
28
+ | 实现已开始? | `docs/superpowers/plans/` 下是否有计划文件 | 有→看是否完成 |
29
+ | 实现已完成? | 计划文件全部 checkbox 已勾选 | 是→close 阶段 |
30
+
31
+ 判定结果:
32
+ - 无活跃变更 → proposal 阶段
33
+ - 有规格但无 plan-ready.md → spec 阶段(补生成翻译)
34
+ - 有 plan-ready.md 但实现未开始 → build 阶段
35
+ - 实现进行中 → 继续 build 阶段(断点恢复)
36
+ - 实现已完成 → close 阶段
37
+
38
+ ## 路由
39
+
40
+ 根据子命令或状态检测结果,读取对应阶段文件并执行:
41
+
42
+ 1. 如果用户指定了子命令(如 `/openflow build`),优先按指定阶段执行,但检查前置条件
43
+ 2. 如果用户只输入 `/openflow`,执行状态检测,自动路由到对应阶段
44
+ 3. 读取阶段文件:`${CLAUDE_SKILL_DIR}/<阶段>.md`
45
+ 4. 按阶段文件中的流程执行
46
+
47
+ ### 前置条件检查
48
+
49
+ | 阶段 | 前置条件 | 不满足时提示 |
50
+ |------|----------|-------------|
51
+ | proposal | 无 | — |
52
+ | brainstorming | 无 | — |
53
+ | spec | 需要有活跃变更目录或有用户需求 | "请先用 /openflow proposal 或 /openflow brainstorming 描述需求" |
54
+ | build | 需要存在 plan-ready.md | "请先完成 /openflow spec 生成规格和翻译" |
55
+ | close | 需要实现已完成 | "实现尚未完成,请先用 /openflow build 执行" |
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: openflow/brainstorming
3
+ description: 深度设计 — 多轮探索,确认架构和方案
4
+ ---
5
+
6
+ # Brainstorming: 深度设计
7
+
8
+ ## 目标
9
+
10
+ 通过多轮对话,深入探索需求、方案取舍和架构决策。产出比 proposal 更完整的需求描述和设计方向。
11
+
12
+ ## 流程
13
+
14
+ ### 1. 理解背景
15
+
16
+ 先快速检查项目上下文:
17
+ - 最近的相关 git 提交
18
+ - 现有代码结构
19
+ - 相关文档
20
+
21
+ ### 2. 逐个提问
22
+
23
+ 一次只问一个问题,逐步深入。问题类型:
24
+
25
+ - **目的** — "这个功能的核心用户场景是什么?"
26
+ - **取舍** — "A 方案更简单但扩展性差,B 方案更灵活但复杂。你倾向哪个?"
27
+ - **边界** — "如果 X 情况发生,期望的行为是什么?"
28
+ - **优先级** — "这几个需求里,哪个最重要?"
29
+
30
+ ### 3. 提出 2-3 种方案
31
+
32
+ 基于讨论,提出 2-3 种实现方案,附上取舍分析。推荐一种并说明理由。
33
+
34
+ ### 4. 确认设计
35
+
36
+ 用户选定方案后,整理设计要点并与用户确认:
37
+
38
+ > "确认的设计方向:[方案名]。核心决策:[2-3 条]。这样对吗?"
39
+
40
+ ### 5. 创建 OpenSpec 变更
41
+
42
+ 用户确认后,调用 OpenSpec 创建变更:
43
+
44
+ ```bash
45
+ openspec new <变更名>
46
+ ```
47
+
48
+ 如果 OpenSpec CLI 不可用,手动创建目录结构:
49
+
50
+ ```bash
51
+ mkdir -p openspec/changes/<变更名>/specs
52
+ ```
53
+
54
+ 将确认的需求描述和设计方向写入 `openspec/changes/<变更名>/proposal.md`。
55
+
56
+ ### 6. 提示下一步
57
+
58
+ > "需求已记录。接下来可以用 `/openflow spec` 生成完整规格。"
59
+
60
+ ## 注意
61
+
62
+ - 不要写代码
63
+ - 不要跳过取舍讨论直接给答案
64
+ - 如果项目很大,建议先拆分成独立的子项目
65
+ - 允许用户改变方向,不要过早锁定
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: openflow/build
3
+ description: 调用 Superpowers 执行实现,支持断点恢复
4
+ ---
5
+
6
+ # Build: Superpowers 执行
7
+
8
+ ## 目标
9
+
10
+ 读取 plan-ready.md,调用 Superpowers 的 writing-plans 生成详细实现计划,然后按 TDD 铁律执行。
11
+
12
+ ## 前置条件
13
+
14
+ - `openspec/changes/<变更名>/plan-ready.md` 存在
15
+
16
+ 如果不满足,提示:
17
+ > "还没生成 plan-ready.md。请先完成 /openflow spec。"
18
+
19
+ ## 流程
20
+
21
+ ### 1. 检测状态
22
+
23
+ 检查以下文件确定当前状态:
24
+
25
+ | 检查 | 怎么查 | 结果 |
26
+ |------|--------|------|
27
+ | 有活跃变更? | `openspec/changes/` 下非 archive 子目录 | 找到变更名 |
28
+ | 有 plan-ready.md? | 变更目录下是否存在 | 不存在→提示先 spec |
29
+ | 实现已开始? | `docs/superpowers/plans/` 下是否有对应计划文件 | 已开始→断点恢复 |
30
+
31
+ 如果有多个活跃变更,列出并让用户选择。
32
+
33
+ ### 2. 断点恢复(如适用)
34
+
35
+ 如果检测到已有计划文件,检查其中 checkbox 状态:
36
+
37
+ - 全部勾选 → 提示实现已完成,建议 /openflow close
38
+ - 部分勾选 → 从未完成的 task 继续执行
39
+ - 无勾选 → 从头开始
40
+
41
+ ### 3. 生成详细实现计划
42
+
43
+ 调用 Superpowers 的 `writing-plans` skill,以 `plan-ready.md` 为输入,生成符合 Superpowers 格式的详细实现计划。
44
+
45
+ 每个步骤:
46
+ - 2-5 分钟工作量
47
+ - 包含代码、文件路径、验证命令
48
+ - 使用 checkbox 语法 `- [ ]` 跟踪
49
+
50
+ 将实现计划保存到:
51
+ ```
52
+ docs/superpowers/plans/YYYY-MM-DD-<变更名>.md
53
+ ```
54
+
55
+ ### 4. 执行实现
56
+
57
+ 按照 Superpowers 的执行流程:
58
+
59
+ 1. **TDD 铁律**:先写失败测试,再写实现代码
60
+ 2. **每个 task 一个 commit**
61
+ 3. 多任务可派子代理并行(参见 subagent-driven-development skill)
62
+ 4. 编译/测试不通过不让提交
63
+
64
+ ### 5. 执行完成
65
+
66
+ 所有 task 完成后,提示用户:
67
+
68
+ > "所有实现任务已完成。接下来可以用 /openflow close 验证一致性并归档。"
69
+
70
+ ## 关键原则
71
+
72
+ - **不允许在 build 阶段修改规格文档** — 发现问题先记录,留到 close 阶段处理
73
+ - plan-ready.md 是锁定的设计决策,Superpowers 按计划展开执行,不重新理解需求
74
+ - 断点恢复依赖文件系统状态,不依赖 AI 的会话记忆
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: openflow/close
3
+ description: 验证实现一致性并归档
4
+ ---
5
+
6
+ # Close: 验证归档
7
+
8
+ ## 目标
9
+
10
+ 验证代码实现与设计文档一致,确认规格变更全部体现,然后归档。
11
+
12
+ ## 前置条件
13
+
14
+ - `docs/superpowers/plans/` 下的实现计划全部 checkbox 已勾选
15
+ - `openspec/changes/<变更名>/plan-ready.md` 存在
16
+
17
+ ## 流程
18
+
19
+ ### 1. 确认实现状态
20
+
21
+ 检查 `docs/superpowers/plans/` 下对应的计划文件,确认所有 checkbox 已勾选。
22
+
23
+ 如果有未完成的 task:
24
+ > "还有 N 个任务未完成。请先用 /openflow build 完成实现。"
25
+
26
+ ### 2. 验证设计一致性
27
+
28
+ 读取 `openspec/changes/<变更名>/design.md`,逐项检查代码实现:
29
+
30
+ - design.md 中的技术决策是否在代码中体现?
31
+ - 标记的架构选择是否与代码结构一致?
32
+
33
+ 对每一项,给出判定:✅ 一致 / ❌ 不一致(附具体差异)
34
+
35
+ ### 3. 验证规格完整性
36
+
37
+ 读取 `openspec/changes/<变更名>/specs/` 目录,检查每个规格变更:
38
+
39
+ - 标记为"新增"的功能是否已实现?
40
+ - 标记为"修改"的行为是否已更新?
41
+ - 标记为"删除"的旧逻辑是否已移除?
42
+
43
+ 对每一项,给出判定:✅ 已体现 / ❌ 未体现(附具体缺失)
44
+
45
+ ### 4. 处理不一致
46
+
47
+ 如果发现不一致:
48
+ - **不在 close 阶段改代码**
49
+ - 将不一致项记录到 `openspec/changes/<变更名>/close-issues.md`
50
+ - 提示用户:
51
+ > "发现 N 处不一致,已记录到 close-issues.md。是否需要开启新的变更来修复?"
52
+
53
+ ### 5. 归档
54
+
55
+ 全部一致(或用户接受不一致项)后,执行归档:
56
+
57
+ ```bash
58
+ openspec archive <变更名>
59
+ ```
60
+
61
+ 如果 OpenSpec CLI 不可用,手动归档:
62
+
63
+ ```bash
64
+ mkdir -p openspec/changes/archive
65
+ mv openspec/changes/<变更名> openspec/changes/archive/$(date +%Y-%m-%d)-<变更名>/
66
+ ```
67
+
68
+ 归档后确认:
69
+ - 规格增量已合并到主规格库(如适用)
70
+ - 变更记录已移到 archive 目录
71
+
72
+ ### 6. 完成提示
73
+
74
+ > "变更 '<变更名>' 已验证并归档。可以开始下一个变更了。"
75
+
76
+ ## 关键原则
77
+
78
+ - close 阶段不做代码修改,只做验证和归档
79
+ - 不一致先记录,不现场修复
80
+ - 防止边写代码边改需求的恶性循环
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: openflow/proposal
3
+ description: 轻量需求捕获 — 3-5 个问题快速收敛需求
4
+ ---
5
+
6
+ # Proposal: 轻量需求捕获
7
+
8
+ ## 目标
9
+
10
+ 用最少的提问,把用户脑子里的需求变成可执行的变更描述。不做深度设计,不生成代码。
11
+
12
+ ## 流程
13
+
14
+ ### 1. 提出关键问题
15
+
16
+ 一次性提出以下 3-5 个核心问题(根据上下文调整措辞):
17
+
18
+ 1. **做什么** — 你想实现什么功能/变更?
19
+ 2. **为什么** — 解决什么问题?给谁用的?
20
+ 3. **成功标准** — 怎样算做完了?验收条件是什么?
21
+ 4. **边界** — 什么不在范围内?
22
+ 5. **现有约束** — 有没有技术栈、兼容性、时间上的限制?
23
+
24
+ ### 2. 确认需求
25
+
26
+ 用户回答后,整理成一段简洁的需求描述,与用户确认:
27
+
28
+ > "我理解的需求是:[一句话概括]。具体来说:[2-3 条要点]。这样理解对吗?"
29
+
30
+ ### 3. 创建 OpenSpec 变更
31
+
32
+ 用户确认后,调用 OpenSpec 创建变更:
33
+
34
+ ```bash
35
+ openspec new <变更名>
36
+ ```
37
+
38
+ 如果 OpenSpec CLI 不可用,手动创建目录结构:
39
+
40
+ ```bash
41
+ mkdir -p openspec/changes/<变更名>/specs
42
+ ```
43
+
44
+ 将确认的需求描述写入 `openspec/changes/<变更名>/proposal.md`。
45
+
46
+ ### 4. 提示下一步
47
+
48
+ > "需求已记录。接下来可以用 `/openflow spec` 生成完整规格,或继续补充细节。"
49
+
50
+ ## 注意
51
+
52
+ - 不要做技术设计,那是 spec 和 brainstorming 的事
53
+ - 不要写代码
54
+ - 问题要具体,不要泛泛而谈
55
+ - 如果用户的需求很大(跨多个独立子系统),建议拆分
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: openflow/spec
3
+ description: 调用 OpenSpec 生成规格文档,确认后自动翻译为 plan-ready.md
4
+ ---
5
+
6
+ # Spec: 生成规格并翻译
7
+
8
+ ## 目标
9
+
10
+ 调用 OpenSpec 生成完整的规格文档(proposal.md, design.md, specs/, tasks.md),用户确认后自动翻译成 Superpowers 可执行的 plan-ready.md。
11
+
12
+ ## 前置条件
13
+
14
+ - `openspec/changes/` 下存在活跃变更目录(由 proposal 或 brainstorming 阶段创建)
15
+ - 变更目录下至少有 `proposal.md`
16
+
17
+ ## 流程
18
+
19
+ ### 1. 确认活跃变更
20
+
21
+ 检查 `openspec/changes/` 下是否有活跃变更(非 archive 子目录)。
22
+
23
+ 如果没有,提示用户:
24
+ > "还没有活跃变更。请先用 /openflow proposal 或 /openflow brainstorming 创建需求。"
25
+
26
+ 如果有多个,列出并让用户选择:
27
+ > "检测到多个活跃变更:[列表]。要对哪个生成规格?"
28
+
29
+ ### 2. 调用 OpenSpec 生成规格
30
+
31
+ 调用 OpenSpec 的 propose 命令(或 OPSX 工作流)生成完整规格:
32
+
33
+ ```bash
34
+ openspec propose <变更名>
35
+ ```
36
+
37
+ 如果 OpenSpec CLI 不可用,手动根据 proposal.md 的内容生成以下文件:
38
+
39
+ - `openspec/changes/<变更名>/proposal.md` — 已存在,可补充
40
+ - `openspec/changes/<变更名>/design.md` — 技术方案
41
+ - `openspec/changes/<变更名>/specs/` — 具体规格变更(标记新增/修改/删除)
42
+ - `openspec/changes/<变更名>/tasks.md` — 实现任务清单
43
+
44
+ ### 3. 与用户确认规格
45
+
46
+ 展示规格摘要,逐项确认:
47
+
48
+ > "以下是规格摘要:
49
+ > - **提案**:[proposal.md 核心内容]
50
+ > - **设计**:[design.md 核心决策]
51
+ > - **任务**:[tasks.md 任务列表]
52
+ >
53
+ > 有需要调整的地方吗?"
54
+
55
+ 用户确认后才进入翻译步骤。
56
+
57
+ ### 4. 自动生成 plan-ready.md(翻译层)
58
+
59
+ 用户确认后,自动将 OpenSpec 四文件翻译为 Superpowers 可执行的格式。
60
+
61
+ **翻译规则:**
62
+ 1. 每个 OpenSpec Task 拆成 2-5 个细粒度步骤(对应 2-5 分钟工作量)
63
+ 2. 每个步骤必须指明改哪个文件
64
+ 3. 每个步骤必须有验证方式
65
+ 4. **按执行依赖排序,不是按功能模块排序**
66
+ 5. 记录来源路径,方便回溯
67
+
68
+ 读取以下文件作为翻译输入:
69
+ - `openspec/changes/<变更名>/proposal.md`
70
+ - `openspec/changes/<变更名>/design.md`
71
+ - `openspec/changes/<变更名>/specs/` 目录下所有文件
72
+ - `openspec/changes/<变更名>/tasks.md`
73
+
74
+ 生成 `openspec/changes/<变更名>/plan-ready.md`,格式如下:
75
+
76
+ ```markdown
77
+ # 实现计划:<变更名>
78
+
79
+ ## 来源
80
+ - 提案:openspec/changes/<变更名>/proposal.md
81
+ - 设计:openspec/changes/<变更名>/design.md
82
+ - 规格:openspec/changes/<变更名>/specs/
83
+ - 任务:openspec/changes/<变更名>/tasks.md
84
+
85
+ ## 实现步骤
86
+
87
+ ### Task 1: <任务名>
88
+ - 目标:<做什么>
89
+ - 改动文件:<哪些文件>
90
+ - 验证方式:<怎么验证>
91
+
92
+ ### Task 2: ...
93
+ ```
94
+
95
+ ### 5. 提示下一步
96
+
97
+ > "规格已确认,plan-ready.md 已生成。接下来可以用 `/openflow build` 开始实现。"
98
+
99
+ ## 关键原则
100
+
101
+ - **一条代码都不许写** — spec 阶段只产出文档
102
+ - 翻译必须在用户确认后自动生成,不需要用户手动触发
103
+ - plan-ready.md 的 ## 来源 部分必须写明路径,方便 Superpowers 执行时回溯
104
+ - 按执行依赖排序是翻译的关键步骤:先依赖后依赖方