feat: CLI 入口和 init 命令

This commit is contained in:
2026-06-08 17:28:37 +08:00
parent 7530a5a743
commit 8e126a95c6
3 changed files with 210 additions and 0 deletions

103
src/cli.ts Normal file
View File

@@ -0,0 +1,103 @@
#!/usr/bin/env bun
import { cac } from "cac";
import { join } from "node:path";
import { mkdir, rename } from "node:fs/promises";
import { runInit } from "./commands/init.ts";
import { findProjectRoot, loadConfig, getChangeDir, getArchiveDir } from "./core/config.ts";
import {
assembleDiscussPrompt,
assemblePlanPrompt,
assembleBuildPrompt,
assembleArchivePrompt,
} from "./core/assembler.ts";
import { scanChanges } from "./core/scanner.ts";
const cli = cac("rune");
cli.command("init [...tools]", "初始化 Rune 并注入工具配置").action(
async (tools: string[]) => {
if (!tools || tools.length === 0) {
console.error("请指定至少一个工具rune init opencode");
process.exit(1);
}
await runInit(process.cwd(), tools);
console.log(`Rune 初始化完成,已注入工具:${tools.join(", ")}`);
},
);
cli.command("discuss", "讨论阶段").action(async () => {
const root = findProjectRoot();
if (!root) {
console.error("未找到 .rune 目录,请先运行 rune init");
process.exit(1);
}
const config = await loadConfig(root);
const prompt = assembleDiscussPrompt(config);
console.log(prompt);
});
cli
.command("plan <change-name>", "规划阶段")
.action(async (changeName: string) => {
const root = findProjectRoot();
if (!root) {
console.error("未找到 .rune 目录,请先运行 rune init");
process.exit(1);
}
await mkdir(getChangeDir(root, changeName), { recursive: true });
const config = await loadConfig(root);
const prompt = await assemblePlanPrompt(config, root, changeName);
console.log(prompt);
});
cli
.command("build <change-name>", "构建阶段")
.action(async (changeName: string) => {
const root = findProjectRoot();
if (!root) {
console.error("未找到 .rune 目录,请先运行 rune init");
process.exit(1);
}
const config = await loadConfig(root);
const prompt = await assembleBuildPrompt(config, root, changeName);
console.log(prompt);
});
cli
.command("archive <change-name>", "归档阶段")
.action(async (changeName: string) => {
const root = findProjectRoot();
if (!root) {
console.error("未找到 .rune 目录,请先运行 rune init");
process.exit(1);
}
const config = await loadConfig(root);
const prompt = assembleArchivePrompt(config, changeName);
const today = new Date().toISOString().slice(0, 10);
const src = getChangeDir(root, changeName);
const dest = join(getArchiveDir(root), `${today}-${changeName}`);
await rename(src, dest);
console.log(prompt);
});
cli.command("status", "查看变更状态").action(async () => {
const root = findProjectRoot();
if (!root) {
console.error("未找到 .rune 目录,请先运行 rune init");
process.exit(1);
}
const changes = await scanChanges(root);
if (changes.length === 0) {
console.log("当前无进行中的变更。");
return;
}
for (const change of changes) {
const progress = change.taskProgress
? ` (${change.taskProgress.completed}/${change.taskProgress.total} 任务)`
: "";
console.log(`- ${change.name}${progress} [${change.documents.join(", ")}]`);
}
});
cli.help();
cli.parse();