refactor: create 从 SDD 阶段降级为工具命令,移除阶段配置和提示词

This commit is contained in:
2026-06-10 14:51:29 +08:00
parent 1f6e49e336
commit 8573d2abc8
13 changed files with 190 additions and 80 deletions

View File

@@ -36,6 +36,22 @@ export async function injectClaudeCode(
}
}
// create 是工具命令,不是 SDD 阶段,但仍需生成对应的 command 文件
{
const stage = "create";
const cmd = `${command} ${stage} <变更名>`;
const smartGuide = `\n${buildSmartGuide(command)}\n`;
const commandDir = join(projectRoot, COMMANDS_DIR);
await mkdir(commandDir, { recursive: true });
const commandPath = join(commandDir, `rune-${stage}.md`);
if (!existsSync(commandPath)) {
await writeFile(
commandPath,
`执行以下命令,将输出作为当前阶段的工作指引:\n\`\`\`bash\n${cmd}\n\`\`\`${smartGuide}\n`,
);
}
}
const introCommandPath = join(projectRoot, COMMANDS_DIR, "rune-intro.md");
if (!existsSync(introCommandPath)) {
await mkdir(join(projectRoot, COMMANDS_DIR), { recursive: true });
@@ -59,6 +75,18 @@ export async function updateClaudeCode(
await writeIfChanged(commandPath, newContent);
}
// create 是工具命令,不是 SDD 阶段,但仍需生成对应的 command 文件
{
const stage = "create";
const cmd = `${command} ${stage} <变更名>`;
const smartGuide = `\n${buildSmartGuide(command)}\n`;
const commandDir = join(projectRoot, COMMANDS_DIR);
await mkdir(commandDir, { recursive: true });
const commandPath = join(commandDir, `rune-${stage}.md`);
const newContent = `执行以下命令,将输出作为当前阶段的工作指引:\n\`\`\`bash\n${cmd}\n\`\`\`${smartGuide}\n`;
await writeIfChanged(commandPath, newContent);
}
const introCommandPath = join(projectRoot, COMMANDS_DIR, "rune-intro.md");
await writeIfChanged(introCommandPath, generateIntroCommand(command));
}
@@ -66,11 +94,11 @@ export async function updateClaudeCode(
function generateIntroCommand(command: string): string {
return `Rune 是基于规格驱动开发SDD的 AI 开发辅助工具。SDD 工作流程:
discuss → create → plan → build → archive
discuss → plan → build → archive
可用命令:
- /rune-discuss — 自由讨论需求和方案
- /rune-create — 创建变更目录
- /rune-create — 创建变更目录辅助命令plan 阶段的前置步骤)
- /rune-plan — 生成设计文档和任务清单
- /rune-build — 按任务清单逐步实现
- /rune-archive — 归档已完成的变更

View File

@@ -26,6 +26,24 @@ export async function injectOpenCode(projectRoot: string, command: string = "run
}
}
// create 是工具命令,不是 SDD 阶段,但仍需生成对应的 command 和 skill 文件
{
const stage = "create";
const commandDir = join(projectRoot, COMMANDS_DIR);
await mkdir(commandDir, { recursive: true });
const commandPath = join(commandDir, `rune-${stage}.md`);
if (!existsSync(commandPath)) {
await writeFile(commandPath, generateCommand(stage));
}
const skillStageDir = join(projectRoot, SKILLS_DIR, `rune-${stage}`);
await mkdir(skillStageDir, { recursive: true });
const skillPath = join(skillStageDir, "SKILL.md");
if (!existsSync(skillPath)) {
await writeFile(skillPath, generateSkill(stage, command));
}
}
const introSkillDir = join(projectRoot, SKILLS_DIR, "rune-intro");
await mkdir(introSkillDir, { recursive: true });
const introSkillPath = join(introSkillDir, "SKILL.md");
@@ -47,6 +65,20 @@ export async function updateOpenCode(projectRoot: string, command: string = "run
await writeIfChanged(skillPath, generateSkill(stage, command));
}
// create 是工具命令,不是 SDD 阶段,但仍需生成对应的 command 和 skill 文件
{
const stage = "create";
const commandDir = join(projectRoot, COMMANDS_DIR);
await mkdir(commandDir, { recursive: true });
const commandPath = join(commandDir, `rune-${stage}.md`);
await writeIfChanged(commandPath, generateCommand(stage));
const skillStageDir = join(projectRoot, SKILLS_DIR, `rune-${stage}`);
await mkdir(skillStageDir, { recursive: true });
const skillPath = join(skillStageDir, "SKILL.md");
await writeIfChanged(skillPath, generateSkill(stage, command));
}
const introSkillDir = join(projectRoot, SKILLS_DIR, "rune-intro");
await mkdir(introSkillDir, { recursive: true });
const introSkillPath = join(introSkillDir, "SKILL.md");
@@ -69,7 +101,7 @@ function generateSkill(stage: string, command: string): string {
const descriptionMap: Record<string, string> = {
discuss: "Use when 需要进入 SDD 讨论阶段,自由讨论需求和架构方案",
create: "Use when 需要创建变更目录,为 SDD 流程准备变更工作区",
create: "Use when 需要创建变更目录plan 阶段的辅助命令prepare workspace",
plan: "Use when 需要进入 SDD 规划阶段,生成设计文档和任务清单",
build: "Use when 需要进入 SDD 构建阶段,按任务清单逐步实现变更",
archive: "Use when 需要进入 SDD 归档阶段,确认变更完成并归档",
@@ -115,8 +147,8 @@ Rune 是基于规格驱动开发SDD的 AI 开发辅助工具。它通过
## SDD 工作流程
\`\`\`
discuss → create → plan → build → archive
探索 创建 规划 构建 归档
discuss → plan → build → archive
探索 规划 构建 归档
\`\`\`
## 可用命令
@@ -124,7 +156,7 @@ discuss → create → plan → build → archive
| 阶段 | 编辑器命令 | 说明 |
|------|-----------|------|
| discuss | /rune-discuss | 自由讨论需求和方案 |
| create | /rune-create | 创建变更目录 |
| create | /rune-create | 创建变更目录(辅助命令) |
| plan | /rune-plan | 生成设计文档和任务清单 |
| build | /rune-build | 按任务清单逐步实现 |
| archive | /rune-archive | 归档已完成的变更 |
@@ -138,9 +170,8 @@ ${command} status
## 快速开始
1. 使用 /rune-discuss 进入讨论,自由探索需求
2. 使用 /rune-create 创建变更目录
3. 使用 /rune-plan 生成设计文档和任务清单
4. 使用 /rune-build 按任务顺序实现功能
5. 使用 /rune-archive 归档已完成的变更
2. 使用 /rune-create 创建变更目录,然后用 /rune-plan 生成设计文档和任务清单
3. 使用 /rune-build 按任务顺序实现功能
4. 使用 /rune-archive 归档已完成的变更
`;
}

View File

@@ -7,7 +7,6 @@ import { runInit } from "./commands/init.ts";
import { findProjectRoot, loadConfig, getChangeDir, getArchiveDir } from "./core/config.ts";
import {
assembleDiscussPrompt,
assembleCreatePrompt,
assemblePlanPrompt,
assembleBuildPrompt,
assembleArchivePrompt,
@@ -216,8 +215,10 @@ cli.command("create <change-name>", "创建变更").action(async (changeName: st
});
}
await mkdir(changeDir, { recursive: true });
const prompt = assembleCreatePrompt(config);
console.log(prompt);
const prefix = getPmPrefix(config);
console.log(`变更 "${changeName}" 已创建。
下一步:${prefix} plan ${changeName} <文档名>`);
});
cli

View File

@@ -30,10 +30,11 @@ const COMMANDS: Record<string, CommandHelpDef> = {
create: {
name: "create",
alias: "create <变更>",
description: "创建:创建变更目录",
description: "创建变更目录plan 阶段的辅助命令)",
usageTemplate: "create <change-name>",
args: [{ name: "<change-name>", desc: '变更名称,如 "add-login"' }],
detail: "在 .rune/changes/ 下创建以变更名称命名的目录,并生成创建阶段提示词。",
detail:
"在 .rune/changes/ 下创建以变更名称命名的目录。这不是独立的 SDD 阶段,而是为 plan 阶段准备变更工作区的工具命令。使用 plan 前必须先 create 创建变更目录。",
exampleArgs: ["create add-user-auth", "create fix-memory-leak"],
},
plan: {

View File

@@ -11,11 +11,11 @@ import { parse as parseYaml } from "yaml";
const CONFIG_TEMPLATE = `# Rune 配置文件
#
# 未配置的阶段将使用内置默认配置。
# 阶段顺序discuss -> create -> plan -> build -> archive
# SDD 四阶段discuss -> plan -> build -> archive
# 辅助命令create创建变更目录plan 阶段的前置步骤)
#
# 可配置阶段:
# discuss - 探索阶段:深度思考、调查代码库、对比方案
# create - 创建阶段:拟定变更名称并创建变更目录
# plan - 规划阶段:生成设计文档和任务清单
# build - 构建阶段:按任务清单逐步实现
# archive - 归档阶段:确认完成并归档变更

View File

@@ -16,15 +16,6 @@ export function assembleDiscussPrompt(config: RuneConfig): string {
return applyCommandPrefix(discuss.prompt, config);
}
export function assembleCreatePrompt(config: RuneConfig): string {
const create = config.stages.create;
if (!create)
throw new CommandError("创建阶段未配置", {
hint: "请在 .rune/config.yaml 中配置 stages.create",
});
return applyCommandPrefix(create.prompt, config);
}
export async function assemblePlanPrompt(
config: RuneConfig,
projectRoot: string,

View File

@@ -91,7 +91,7 @@ export function validateConfig(config: RuneConfig): void {
function mergeConfig(userConfig: Partial<RuneConfig>): RuneConfig {
const result: RuneConfig = { stages: {} };
const stageKeys = ["discuss", "create", "plan", "build", "archive"] as const;
const stageKeys = ["discuss", "plan", "build", "archive"] as const;
for (const stage of stageKeys) {
if (userConfig.stages?.[stage]) {

View File

@@ -232,14 +232,6 @@ rune status
除非……有同步组件?
\`\`\``,
},
create: {
prompt: `请根据讨论内容,拟定一个简短、有意义的变更名称,然后执行 create 命令创建变更目录。
要求:
- 变更名称应简洁明了,能概括变更的核心内容
- 仅支持中文、英文和短横线(-
- 创建成功后,引导用户使用 /rune-plan <变更名> <文档名> 进入规划阶段`,
},
plan: {
documents: [

View File

@@ -15,10 +15,6 @@ export interface DiscussStage {
prompt: string;
}
export interface CreateStage {
prompt: string;
}
export interface PlanStage {
documents: DocumentConfig[];
}
@@ -33,7 +29,6 @@ export interface ArchiveStage {
export interface StagesConfig {
discuss?: DiscussStage;
create?: CreateStage;
plan?: PlanStage;
build?: BuildStage;
archive?: ArchiveStage;
@@ -60,7 +55,7 @@ export interface ChangeStatus {
taskProgress: { completed: number; total: number } | null;
}
export const STAGES = ["discuss", "create", "plan", "build", "archive"] as const;
export const STAGES = ["discuss", "plan", "build", "archive"] as const;
export type Stage = (typeof STAGES)[number];
export const RUNE_DIR = ".rune";