@guanlin4924/devflow-pi 0.1.50 → 0.1.51
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/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import { appendFile, mkdir, rename, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
|
|
7
|
+
import { Box, Text } from "@oh-my-pi/pi-tui";
|
|
7
8
|
import {
|
|
8
9
|
TASK_SUBAGENT_LIFECYCLE_CHANNEL,
|
|
9
10
|
type SubagentLifecyclePayload,
|
|
@@ -27,6 +28,96 @@ type ProjectInitializationRequest = {
|
|
|
27
28
|
mode: ProjectInitializationMode;
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
const workflowStepNames: Record<string, string> = {
|
|
32
|
+
"requirement-triage": "需求梳理",
|
|
33
|
+
"requirement-wayfinder": "需求路径规划",
|
|
34
|
+
"domain-modeling": "领域建模",
|
|
35
|
+
"requirement-feasibility-review": "需求可行性审查",
|
|
36
|
+
"requirement-baseline": "需求基线确认",
|
|
37
|
+
"wireframe-proposal": "原型方案",
|
|
38
|
+
"wireframe-validate": "原型校验",
|
|
39
|
+
"wireframe-render": "原型预览生成",
|
|
40
|
+
"wireframe-review": "原型审查",
|
|
41
|
+
"api-contract": "接口契约",
|
|
42
|
+
"architecture-feasibility": "架构可行性评估",
|
|
43
|
+
"ui-spec": "界面规格",
|
|
44
|
+
"feature-breakdown": "功能拆分",
|
|
45
|
+
"frontend-tech-spec": "前端技术方案",
|
|
46
|
+
"backend-tech-spec": "后端技术方案",
|
|
47
|
+
"api-call-guide": "接口调用说明",
|
|
48
|
+
"api-supplement": "接口补充设计",
|
|
49
|
+
"integration-verify": "集成验证",
|
|
50
|
+
"change-plan": "变更计划",
|
|
51
|
+
"acceptance-test-design": "验收测试设计",
|
|
52
|
+
"phase-structure": "阶段结构确认",
|
|
53
|
+
"execute": "实现执行",
|
|
54
|
+
"code-index-refresh": "代码索引刷新",
|
|
55
|
+
"code-review": "代码审查",
|
|
56
|
+
"generate-mainflow-cases": "主流程用例生成",
|
|
57
|
+
"run-acceptance-tests": "冻结验收测试执行",
|
|
58
|
+
"release-closeout": "发布收尾与里程碑归档",
|
|
59
|
+
"dual-track-design": "前后端并行设计",
|
|
60
|
+
"acceptance-failed-remediation": "验收失败修复",
|
|
61
|
+
"diagnose": "验收失败诊断",
|
|
62
|
+
"execute-fix": "修复执行",
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const workflowStatusNames: Record<string, string> = {
|
|
66
|
+
completed: "已完成",
|
|
67
|
+
current: "进行中",
|
|
68
|
+
pending: "未开始",
|
|
69
|
+
waiting_review: "等待审查",
|
|
70
|
+
blocked: "已阻塞",
|
|
71
|
+
failed: "执行失败",
|
|
72
|
+
cancelled: "已取消",
|
|
73
|
+
unknown: "未知",
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const workflowActionNames: Record<string, string> = {
|
|
77
|
+
run_next_step: "执行下一步骤",
|
|
78
|
+
run_parallel_window: "执行并行步骤",
|
|
79
|
+
route_failure_branch: "进入失败处理分支",
|
|
80
|
+
await_user_confirmation: "等待人工确认",
|
|
81
|
+
recover_artifact_registry: "恢复工件登记",
|
|
82
|
+
workflow_complete: "功能交付已完成",
|
|
83
|
+
workflow_cancelled: "功能交付已取消",
|
|
84
|
+
blocked: "处理阻塞项",
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function workflowSteps(snapshot: Record<string, unknown>): Array<{ id: string; label: string; status: string }> {
|
|
88
|
+
if (!Array.isArray(snapshot.workflow_steps)) return [];
|
|
89
|
+
return snapshot.workflow_steps.flatMap(step => {
|
|
90
|
+
if (!step || typeof step !== "object") return [];
|
|
91
|
+
const record = step as Record<string, unknown>;
|
|
92
|
+
const id = typeof record.id === "string" ? record.id : "";
|
|
93
|
+
if (!id) return [];
|
|
94
|
+
return [{
|
|
95
|
+
id,
|
|
96
|
+
label: typeof record.label === "string" && record.label ? record.label : id,
|
|
97
|
+
status: typeof record.status === "string" ? record.status : "unknown",
|
|
98
|
+
}];
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function workflowStepName(id: string | undefined, fallback?: string): string {
|
|
103
|
+
if (id && workflowStepNames[id]) return workflowStepNames[id];
|
|
104
|
+
return fallback || id || "未开始";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function workflowStatusName(status: unknown): string {
|
|
108
|
+
return typeof status === "string" ? workflowStatusNames[status] ?? status : "未知";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function workflowActionName(action: unknown): string {
|
|
112
|
+
return typeof action === "string" ? workflowActionNames[action] ?? action : "正在确认";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function workflowPhaseName(phase: unknown): string {
|
|
116
|
+
if (typeof phase !== "string" || !phase) return "未开始";
|
|
117
|
+
if (phase.startsWith("feature-")) return "功能交付";
|
|
118
|
+
return phase;
|
|
119
|
+
}
|
|
120
|
+
|
|
30
121
|
type ActiveExecutionContract = {
|
|
31
122
|
contractId: string;
|
|
32
123
|
feature: string;
|
|
@@ -897,20 +988,21 @@ function projectInitializationDirective(summary: string): string {
|
|
|
897
988
|
}
|
|
898
989
|
|
|
899
990
|
function explainWorkflowSnapshot(snapshot: Record<string, unknown> | null): string {
|
|
900
|
-
if (!snapshot) return "Devflow 当前没有可解释的工作流状态。可使用 /devflow start
|
|
991
|
+
if (!snapshot) return "Devflow 当前没有可解释的工作流状态。可使用 /devflow start <功能名> 创建新功能。";
|
|
901
992
|
const step = compactStep(snapshot.next_step) ?? compactStep(snapshot.current_step);
|
|
902
993
|
const lines = [
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
994
|
+
`功能:${String(snapshot.current_feature ?? "未开始")}`,
|
|
995
|
+
`阶段:${workflowPhaseName(snapshot.current_phase)}`,
|
|
996
|
+
`状态:${workflowStatusName(snapshot.status)}`,
|
|
997
|
+
`动作:${workflowActionName(snapshot.action)}`,
|
|
998
|
+
`版本:${String(snapshot.revision ?? "未知")}`,
|
|
907
999
|
];
|
|
908
|
-
if (step?.id) lines.push(
|
|
909
|
-
if (snapshot.checkpoint) lines.push(
|
|
1000
|
+
if (step?.id) lines.push(`当前/下一节点:${workflowStepName(step.id, step.label)}`);
|
|
1001
|
+
if (snapshot.checkpoint) lines.push(`检查点:${String(snapshot.checkpoint)}`);
|
|
910
1002
|
if (snapshot.action === "workflow_complete") {
|
|
911
|
-
lines.push("
|
|
1003
|
+
lines.push("结论:上一功能已完成;可以使用 /devflow start <功能名> 创建下一功能。");
|
|
912
1004
|
} else {
|
|
913
|
-
lines.push("
|
|
1005
|
+
lines.push("结论:这是只读说明;未执行任何状态迁移或文件写入。");
|
|
914
1006
|
}
|
|
915
1007
|
return lines.join("\n");
|
|
916
1008
|
}
|
|
@@ -1129,33 +1221,13 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1129
1221
|
|
|
1130
1222
|
type ProjectControlUi = {
|
|
1131
1223
|
setStatus(key: string, text: string | undefined): void;
|
|
1132
|
-
|
|
1224
|
+
// OMP widgets accept either plain lines or a component factory. Keep this
|
|
1225
|
+
// interface structural because command contexts are supplied by OMP.
|
|
1226
|
+
setWidget?(key: string, content: unknown, options?: { placement?: "aboveEditor" | "belowEditor" }): void;
|
|
1133
1227
|
};
|
|
1134
1228
|
|
|
1135
1229
|
let projectControlRefreshRevision = 0;
|
|
1136
1230
|
|
|
1137
|
-
function workflowSteps(snapshot: Record<string, unknown>): Array<{ id: string; label: string; status: string }> {
|
|
1138
|
-
if (!Array.isArray(snapshot.workflow_steps)) return [];
|
|
1139
|
-
return snapshot.workflow_steps.flatMap(step => {
|
|
1140
|
-
if (!step || typeof step !== "object") return [];
|
|
1141
|
-
const record = step as Record<string, unknown>;
|
|
1142
|
-
const id = typeof record.id === "string" ? record.id : "";
|
|
1143
|
-
if (!id) return [];
|
|
1144
|
-
return [{
|
|
1145
|
-
id,
|
|
1146
|
-
label: typeof record.label === "string" && record.label ? record.label : id,
|
|
1147
|
-
status: typeof record.status === "string" ? record.status : "unknown",
|
|
1148
|
-
}];
|
|
1149
|
-
});
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
function workflowStepIcon(status: string): string {
|
|
1153
|
-
if (status === "completed") return "✓";
|
|
1154
|
-
if (status === "current") return "●";
|
|
1155
|
-
if (status === "waiting_review") return "◆";
|
|
1156
|
-
if (status === "blocked") return "!";
|
|
1157
|
-
return "○";
|
|
1158
|
-
}
|
|
1159
1231
|
|
|
1160
1232
|
function projectActionLabel(snapshot: Record<string, unknown>): string {
|
|
1161
1233
|
switch (snapshot.action) {
|
|
@@ -1166,11 +1238,11 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1166
1238
|
case "await_user_confirmation":
|
|
1167
1239
|
return "审查产物后批准推进";
|
|
1168
1240
|
case "recover_artifact_registry":
|
|
1169
|
-
return "
|
|
1241
|
+
return "可执行安全恢复(仅工件登记)";
|
|
1170
1242
|
case "workflow_complete":
|
|
1171
|
-
return "
|
|
1243
|
+
return "当前功能已完成";
|
|
1172
1244
|
case "workflow_cancelled":
|
|
1173
|
-
return "
|
|
1245
|
+
return "当前功能已取消";
|
|
1174
1246
|
case "blocked":
|
|
1175
1247
|
return "先处理阻塞证据";
|
|
1176
1248
|
default:
|
|
@@ -1184,59 +1256,78 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1184
1256
|
if (state.phase === "blocked") return `已阻塞:${state.label}`;
|
|
1185
1257
|
if (snapshot.action === "await_user_confirmation") return "等待人工审查";
|
|
1186
1258
|
if (snapshot.action === "blocked") return "已阻塞";
|
|
1187
|
-
return "
|
|
1259
|
+
return "当前会话未运行智能体";
|
|
1188
1260
|
}
|
|
1189
1261
|
|
|
1190
1262
|
function projectControlPanel(snapshot: Record<string, unknown> | null, state: SessionControlState): string[] {
|
|
1191
1263
|
if (!snapshot) {
|
|
1192
|
-
return ["Devflow
|
|
1264
|
+
return ["Devflow 当前功能交付", "── 项目状态 ──", "尚未读取到有效控制面状态。"];
|
|
1193
1265
|
}
|
|
1194
1266
|
const steps = workflowSteps(snapshot);
|
|
1195
1267
|
const completedCount = steps.filter(step => step.status === "completed").length;
|
|
1196
|
-
const
|
|
1268
|
+
const selectedStep = compactStep(snapshot.current_step) ?? compactStep(snapshot.next_step);
|
|
1269
|
+
const currentStep = selectedStep
|
|
1270
|
+
? workflowStepName(selectedStep.id, selectedStep.label)
|
|
1271
|
+
: "未开始";
|
|
1272
|
+
const nextStep = steps.find(step => step.status !== "completed" && step.id !== selectedStep?.id)
|
|
1273
|
+
?? steps.find(step => step.status !== "completed");
|
|
1197
1274
|
const recovery = snapshot.recovery_plan && typeof snapshot.recovery_plan === "object"
|
|
1198
1275
|
? snapshot.recovery_plan as Record<string, unknown>
|
|
1199
1276
|
: undefined;
|
|
1200
1277
|
const recoveryLabel = recovery?.classification === "safe_repairable"
|
|
1201
|
-
? "
|
|
1278
|
+
? "可执行安全恢复(不会推进节点)"
|
|
1202
1279
|
: snapshot.action === "blocked"
|
|
1203
|
-
? "
|
|
1204
|
-
: "
|
|
1205
|
-
const chain = steps.length > 0
|
|
1206
|
-
? steps.map(step => `${workflowStepIcon(step.status)} ${step.label}`).join(" → ")
|
|
1207
|
-
: "尚无可展示的已知工作流步骤。";
|
|
1280
|
+
? "暂无自动重置方案,请先查看阻塞证据"
|
|
1281
|
+
: "当前无需恢复或重置";
|
|
1208
1282
|
return [
|
|
1209
|
-
`Devflow
|
|
1210
|
-
`阶段:${
|
|
1211
|
-
|
|
1212
|
-
`进度:已完成 ${completedCount} / 当前已知 ${steps.length}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1283
|
+
`Devflow 当前功能交付 · ${String(snapshot.current_feature ?? "未开始")}`,
|
|
1284
|
+
`阶段:${workflowPhaseName(snapshot.current_phase)} 版本:${String(snapshot.revision ?? "未知")}`,
|
|
1285
|
+
"── 交付链路 ──",
|
|
1286
|
+
`进度:已完成 ${completedCount} / 当前已知 ${steps.length} 步 工作流:${workflowStatusName(snapshot.status)}`,
|
|
1287
|
+
`当前:${currentStep}${nextStep ? ` 后续:${workflowStepName(nextStep.id, nextStep.label)}` : ""}`,
|
|
1288
|
+
"── 当前节点 ──",
|
|
1289
|
+
`节点状态:${workflowStatusName(selectedStep?.status)} 执行状态:${projectExecutionLabel(state, snapshot)}`,
|
|
1290
|
+
`节点动作:${workflowActionName(snapshot.action)}`,
|
|
1291
|
+
"── 下一步执行 ──",
|
|
1292
|
+
`可执行操作:${projectActionLabel(snapshot)}`,
|
|
1293
|
+
"── 恢复与重置 ──",
|
|
1294
|
+
`处理建议:${recoveryLabel}`,
|
|
1217
1295
|
];
|
|
1218
1296
|
}
|
|
1219
1297
|
|
|
1298
|
+
function projectControlWidget(lines: string[]) {
|
|
1299
|
+
return (_tui: unknown, theme: { fg(color: "accent" | "muted" | "dim", text: string): string; bold(text: string): string }) => {
|
|
1300
|
+
const panel = new Box(1, 0, undefined, {
|
|
1301
|
+
chars: { topLeft: "╭", topRight: "╮", bottomLeft: "╰", bottomRight: "╯", horizontal: "─", vertical: "│" },
|
|
1302
|
+
color: text => theme.fg("dim", text),
|
|
1303
|
+
});
|
|
1304
|
+
const content = lines.map((line, index) => {
|
|
1305
|
+
if (index === 0) return theme.bold(theme.fg("accent", line));
|
|
1306
|
+
if (line.startsWith("──")) return theme.fg("accent", line);
|
|
1307
|
+
return theme.fg("muted", line);
|
|
1308
|
+
}).join("\n");
|
|
1309
|
+
panel.addChild(new Text(content, 0, 0));
|
|
1310
|
+
return panel;
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1220
1314
|
function refreshProjectControlPanel(ctx: { cwd: string; ui: ProjectControlUi }): void {
|
|
1221
1315
|
const request = ++projectControlRefreshRevision;
|
|
1222
1316
|
void inspectWorkflow(ctx.cwd).then(({ result, snapshot }) => {
|
|
1223
1317
|
if (request !== projectControlRefreshRevision) return;
|
|
1224
1318
|
const content = result.ok
|
|
1225
1319
|
? projectControlPanel(snapshot, controlState)
|
|
1226
|
-
: ["Devflow
|
|
1227
|
-
ctx.ui.setWidget?.("devflow-project-control", content, { placement: "aboveEditor" });
|
|
1320
|
+
: ["Devflow 当前功能交付", "── 项目状态 ──", "状态读取失败;未执行任何恢复或状态迁移。"];
|
|
1321
|
+
ctx.ui.setWidget?.("devflow-project-control", projectControlWidget(content), { placement: "aboveEditor" });
|
|
1228
1322
|
}).catch(() => {
|
|
1229
1323
|
if (request !== projectControlRefreshRevision) return;
|
|
1230
|
-
ctx.ui.setWidget?.("devflow-project-control", ["Devflow
|
|
1324
|
+
ctx.ui.setWidget?.("devflow-project-control", projectControlWidget(["Devflow 当前功能交付", "── 项目状态 ──", "状态读取失败;未执行任何恢复或状态迁移。"]), { placement: "aboveEditor" });
|
|
1231
1325
|
});
|
|
1232
1326
|
}
|
|
1233
1327
|
|
|
1234
1328
|
function setControlState(ctx: {
|
|
1235
1329
|
cwd: string;
|
|
1236
|
-
ui:
|
|
1237
|
-
setStatus(key: string, text: string | undefined): void;
|
|
1238
|
-
setWidget?(key: string, content: string[] | undefined, options?: { placement?: "aboveEditor" | "belowEditor" }): void;
|
|
1239
|
-
};
|
|
1330
|
+
ui: ProjectControlUi;
|
|
1240
1331
|
}, state: SessionControlState) {
|
|
1241
1332
|
controlState = state;
|
|
1242
1333
|
ctx.ui.setStatus("devflow-control", statusText(state));
|
|
@@ -1914,27 +2005,27 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1914
2005
|
const feature = typeof snapshot?.current_feature === "string" ? snapshot.current_feature : undefined;
|
|
1915
2006
|
const status = typeof snapshot?.status === "string" ? snapshot.status : undefined;
|
|
1916
2007
|
if (!inspected.result.ok || revision === undefined || !feature || !status || ["completed", "cancelled", "failed"].includes(status)) {
|
|
1917
|
-
ctx.ui.notify("
|
|
2008
|
+
ctx.ui.notify("当前没有可取消的进行中功能。", "error");
|
|
1918
2009
|
return;
|
|
1919
2010
|
}
|
|
1920
|
-
const reason = await ctx.ui.input("
|
|
2011
|
+
const reason = await ctx.ui.input("取消当前功能", "请输入取消原因");
|
|
1921
2012
|
if (!reason?.trim()) return;
|
|
1922
2013
|
const confirmed = await ctx.ui.confirm(
|
|
1923
|
-
"
|
|
1924
|
-
|
|
2014
|
+
"确认取消当前功能",
|
|
2015
|
+
`功能:${feature}\n原因:${reason.trim()}\n\n将结束当前功能、释放项目占用并保留现有工件;未完成步骤不会被标记为完成。继续吗?`,
|
|
1925
2016
|
);
|
|
1926
2017
|
if (!confirmed) return;
|
|
1927
2018
|
const result = await runRuby(ctx.cwd, "workflow-orchestrator/scripts/advance_state.rb", [
|
|
1928
2019
|
"--write", "--json", "--cancel", "--cancel-reason", reason.trim(), "--expected-revision", String(revision),
|
|
1929
2020
|
]);
|
|
1930
2021
|
if (!controlPlaneSucceeded(result)) {
|
|
1931
|
-
ctx.ui.notify(
|
|
2022
|
+
ctx.ui.notify(`取消当前功能失败:${result.stderr || result.stdout}`, "error");
|
|
1932
2023
|
return;
|
|
1933
2024
|
}
|
|
1934
2025
|
activeContract = undefined;
|
|
1935
2026
|
contractAgentCalls = 0;
|
|
1936
2027
|
setControlState(ctx, initialControlState());
|
|
1937
|
-
ctx.ui.notify(
|
|
2028
|
+
ctx.ui.notify(`功能“${feature}”已取消;现有工件已保留。`, "info");
|
|
1938
2029
|
}
|
|
1939
2030
|
|
|
1940
2031
|
async function openProjectControlPanel(ctx: ProjectControlCommandContext): Promise<void> {
|
|
@@ -1951,10 +2042,10 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1951
2042
|
if (["run_next_step", "run_parallel_window", "route_failure_branch"].includes(String(snapshot.action))) options.push("启动当前可执行步骤");
|
|
1952
2043
|
if (snapshot.action === "await_user_confirmation") options.push("审查并批准推进…");
|
|
1953
2044
|
if (hasSafeRecoveryPlan(snapshot)) options.push("执行安全恢复…");
|
|
1954
|
-
if (typeof snapshot.current_feature === "string" && !["completed", "cancelled", "failed"].includes(String(snapshot.status))) options.push("
|
|
2045
|
+
if (typeof snapshot.current_feature === "string" && !["completed", "cancelled", "failed"].includes(String(snapshot.status))) options.push("取消当前功能…");
|
|
1955
2046
|
options.push("关闭");
|
|
1956
2047
|
|
|
1957
|
-
const selected = await ctx.ui.select(
|
|
2048
|
+
const selected = await ctx.ui.select("项目控制操作", options);
|
|
1958
2049
|
if (!selected || selected === "关闭") return;
|
|
1959
2050
|
if (selected === "刷新状态") continue;
|
|
1960
2051
|
if (selected === "查看状态说明") {
|
|
@@ -1978,7 +2069,7 @@ export default function devflowCore(pi: ExtensionAPI) {
|
|
|
1978
2069
|
await executeSafeRecoveryFromPanel(ctx);
|
|
1979
2070
|
return;
|
|
1980
2071
|
}
|
|
1981
|
-
if (selected === "
|
|
2072
|
+
if (selected === "取消当前功能…") {
|
|
1982
2073
|
await cancelFeatureFromPanel(ctx);
|
|
1983
2074
|
return;
|
|
1984
2075
|
}
|
package/src/constants.ts
CHANGED