@unifan/pi-unifan-zh 1.0.37 → 1.0.39
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.
|
@@ -387,33 +387,15 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
387
387
|
}
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
-
|
|
391
|
-
"00": "00-next",
|
|
392
|
-
"/00": "00-next",
|
|
393
|
-
"01": "01-brainstorm",
|
|
394
|
-
"/01": "01-brainstorm",
|
|
395
|
-
"02": "02-plan",
|
|
396
|
-
"/02": "02-plan",
|
|
397
|
-
"03": "03-work",
|
|
398
|
-
"/03": "03-work",
|
|
399
|
-
"04": "04-review",
|
|
400
|
-
"/04": "04-review",
|
|
401
|
-
"05": "05-learn",
|
|
402
|
-
"/05": "05-learn",
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
// 监听用户打断指令与极简快捷指令(如输入 01 自动转为 /skill:01-brainstorm)
|
|
390
|
+
// 监听用户打断指令:用户若输入“暂停”或“停止”,自动安全挂起 03 自主循环
|
|
406
391
|
pi.on("input", async (event, ctx) => {
|
|
407
392
|
if (event.source === "extension") return { action: "continue" };
|
|
408
393
|
|
|
409
|
-
const
|
|
410
|
-
const lower = raw.toLowerCase();
|
|
411
|
-
|
|
412
|
-
// 1. 暂停/打断指令:用户若输入“暂停”或“停止”,自动安全挂起 03 自主循环
|
|
394
|
+
const text = event.text.trim().toLowerCase();
|
|
413
395
|
if (
|
|
414
396
|
workDriverInstance &&
|
|
415
397
|
workDriverInstance.getStatus().isActive &&
|
|
416
|
-
(
|
|
398
|
+
(text === "暂停" || text === "停止" || text === "pause" || text === "stop")
|
|
417
399
|
) {
|
|
418
400
|
detachEscapeListener();
|
|
419
401
|
await workDriverInstance.pause("用户手动输入暂停");
|
|
@@ -425,23 +407,6 @@ export default function workflowExtension(pi: ExtensionAPI) {
|
|
|
425
407
|
return { action: "handled" };
|
|
426
408
|
}
|
|
427
409
|
|
|
428
|
-
// 2. 阶段编号快捷直达:输入 00~05 自动转换为 /skill:0x-xxx
|
|
429
|
-
const match = raw.match(/^(\/?0[0-5])(?:\s+(.*))?$/);
|
|
430
|
-
if (match) {
|
|
431
|
-
const shortcutKey = match[1];
|
|
432
|
-
const skillName = WORKFLOW_SHORTCUTS[shortcutKey];
|
|
433
|
-
if (skillName) {
|
|
434
|
-
// 若当前正在 03 自主干活中,切换阶段时先优雅暂停 03
|
|
435
|
-
if (skillName !== "03-work" && workDriverInstance && workDriverInstance.getStatus().isActive) {
|
|
436
|
-
detachEscapeListener();
|
|
437
|
-
await workDriverInstance.pause(`用户通过快捷键切换至 ${skillName}`);
|
|
438
|
-
ctx.ui?.setStatus?.("workflow", undefined);
|
|
439
|
-
}
|
|
440
|
-
const rest = match[2] ? ` ${match[2]}` : "";
|
|
441
|
-
return { action: "transform", text: `/skill:${skillName}${rest}` };
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
410
|
return { action: "continue" };
|
|
446
411
|
});
|
|
447
412
|
|
|
@@ -8,22 +8,17 @@ export function isExplicit03WorkTrigger(prompt: string): boolean {
|
|
|
8
8
|
const lower = trimmed.toLowerCase();
|
|
9
9
|
|
|
10
10
|
// 1. Direct skill invocation or slash command
|
|
11
|
-
if (
|
|
12
|
-
lower.startsWith("/skill:03-work") ||
|
|
13
|
-
lower === "/skill:03" ||
|
|
14
|
-
lower.startsWith("03-work")
|
|
15
|
-
) {
|
|
11
|
+
if (lower.startsWith("/skill:03-work")) {
|
|
16
12
|
return true;
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
// 2. Strict keywords and natural language commands to start/resume 03 work
|
|
20
16
|
const strictPatterns = [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/^执行(03|03-work|干活)/,
|
|
17
|
+
/^开始(干活|编码|实现|写代码)/,
|
|
18
|
+
/^执行03-work/,
|
|
24
19
|
/^自主(干活|工作|编码)/,
|
|
25
|
-
/^(继续|恢复)(
|
|
26
|
-
/^resume\s+
|
|
20
|
+
/^(继续|恢复)(干活|工作|work)/i,
|
|
21
|
+
/^resume\s+work/i,
|
|
27
22
|
];
|
|
28
23
|
|
|
29
24
|
if (strictPatterns.some((p) => p.test(trimmed))) {
|
|
@@ -290,16 +290,10 @@ test("workflowExtension: registers tools and command cleanly", async () => {
|
|
|
290
290
|
assert.ok(listeners.has("input"));
|
|
291
291
|
assert.ok(listeners.has("agent_settled"));
|
|
292
292
|
|
|
293
|
-
// Verify
|
|
293
|
+
// Verify input handler passes through standard inputs and handles pause
|
|
294
294
|
const inputHandler = listeners.get("input");
|
|
295
|
-
const
|
|
296
|
-
assert.deepEqual(
|
|
297
|
-
|
|
298
|
-
const res01WithText = await inputHandler({ text: "01 补充需求边界", source: "interactive" }, {});
|
|
299
|
-
assert.deepEqual(res01WithText, { action: "transform", text: "/skill:01-brainstorm 补充需求边界" });
|
|
300
|
-
|
|
301
|
-
const res00 = await inputHandler({ text: "00", source: "interactive" }, {});
|
|
302
|
-
assert.deepEqual(res00, { action: "transform", text: "/skill:00-next" });
|
|
295
|
+
const resPassThrough = await inputHandler({ text: "/skill:01-brainstorm", source: "interactive" }, {});
|
|
296
|
+
assert.deepEqual(resPassThrough, { action: "continue" });
|
|
303
297
|
|
|
304
298
|
const resNormal = await inputHandler({ text: "1. 选第一个方案", source: "interactive" }, {});
|
|
305
299
|
assert.deepEqual(resNormal, { action: "continue" });
|
|
@@ -479,20 +473,19 @@ test("WorkLoopDriver: records failure history and triggers Stop-The-Line valve o
|
|
|
479
473
|
test("isExplicit03WorkTrigger: accurately identifies 03-work invocations vs harmless inputs", async () => {
|
|
480
474
|
// Should match explicit 03 triggers
|
|
481
475
|
assert.equal(isExplicit03WorkTrigger("/skill:03-work"), true);
|
|
482
|
-
assert.equal(isExplicit03WorkTrigger("/skill:03"), true);
|
|
483
|
-
assert.equal(isExplicit03WorkTrigger("03-work"), true);
|
|
484
|
-
assert.equal(isExplicit03WorkTrigger("03-work docs/plans/plan.md"), true);
|
|
485
|
-
assert.equal(isExplicit03WorkTrigger("03"), true);
|
|
486
|
-
assert.equal(isExplicit03WorkTrigger("/03"), true);
|
|
487
476
|
assert.equal(isExplicit03WorkTrigger("开始干活"), true);
|
|
488
|
-
assert.equal(isExplicit03WorkTrigger("开始03"), true);
|
|
489
477
|
assert.equal(isExplicit03WorkTrigger("开始实现"), true);
|
|
490
|
-
assert.equal(isExplicit03WorkTrigger("执行03"), true);
|
|
478
|
+
assert.equal(isExplicit03WorkTrigger("执行03-work"), true);
|
|
491
479
|
assert.equal(isExplicit03WorkTrigger("继续干活"), true);
|
|
492
480
|
assert.equal(isExplicit03WorkTrigger("恢复干活"), true);
|
|
493
481
|
assert.equal(isExplicit03WorkTrigger("resume work"), true);
|
|
494
482
|
assert.equal(isExplicit03WorkTrigger("【03-work 自主循环驱动引擎 · 自动化续跑指令】"), true);
|
|
495
483
|
|
|
484
|
+
// Alias /skill:03 or plain numbers must NEVER trigger (strictly require full skill name /skill:03-work)
|
|
485
|
+
assert.equal(isExplicit03WorkTrigger("/skill:03"), false);
|
|
486
|
+
assert.equal(isExplicit03WorkTrigger("03"), false);
|
|
487
|
+
assert.equal(isExplicit03WorkTrigger("/03"), false);
|
|
488
|
+
|
|
496
489
|
// Must NEVER match other skills or normal dialogue (prevents false auto-starts!)
|
|
497
490
|
assert.equal(isExplicit03WorkTrigger("00"), false);
|
|
498
491
|
assert.equal(isExplicit03WorkTrigger("/skill:00-next"), false);
|
|
@@ -683,37 +676,22 @@ test("E2E Lifecycle: 01 brainstorm -> 02 plan -> 01 rollback -> 03 work -> compa
|
|
|
683
676
|
assert.equal(state.recommendedSkill, "03-work");
|
|
684
677
|
assert.equal(state.totalUnitsCount, 3);
|
|
685
678
|
|
|
686
|
-
// 4. User is in 02, realizes requirements need changes -> Rolls back to 01
|
|
687
|
-
//
|
|
688
|
-
const listeners = new Map();
|
|
689
|
-
const mockPi = {
|
|
690
|
-
registerTool() {},
|
|
691
|
-
registerCommand() {},
|
|
692
|
-
on(event, handler) {
|
|
693
|
-
listeners.set(event, handler);
|
|
694
|
-
},
|
|
695
|
-
};
|
|
696
|
-
workflowExtension(mockPi);
|
|
697
|
-
const inputHandler = listeners.get("input");
|
|
698
|
-
|
|
699
|
-
// User types "01 增加第三方登录"
|
|
700
|
-
const transformRes = await inputHandler({ text: "01 增加第三方登录", source: "interactive" }, {});
|
|
701
|
-
assert.deepEqual(transformRes, {
|
|
702
|
-
action: "transform",
|
|
703
|
-
text: "/skill:01-brainstorm 增加第三方登录",
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
// User updates brainstorm file
|
|
679
|
+
// 4. User is in 02, realizes requirements need changes -> Rolls back to 01 via /skill:01-brainstorm
|
|
680
|
+
// In standard Pi, user runs /skill:01-brainstorm to revisit requirements
|
|
707
681
|
await writeFile(bsFile, "# Auth Requirements\nScope: login, register, oauth2", "utf-8");
|
|
708
682
|
|
|
709
|
-
// 5. User returns to 02
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
683
|
+
// 5. User returns to 02 via /skill:02-plan and updates the plan
|
|
684
|
+
await writeFile(
|
|
685
|
+
planFile,
|
|
686
|
+
"# Auth Plan\n### Unit 0 — DB Schema\n### Unit 1 — API Route & OAuth2\n### Unit 2 — Tests",
|
|
687
|
+
"utf-8",
|
|
688
|
+
);
|
|
715
689
|
|
|
716
|
-
|
|
690
|
+
state = await detectWorkflowState(testRoot);
|
|
691
|
+
assert.equal(state.recommendedSkill, "03-work");
|
|
692
|
+
|
|
693
|
+
// 6. User enters 03 -> starts autonomous work via /skill:03-work
|
|
694
|
+
assert.equal(isExplicit03WorkTrigger("/skill:03-work"), true);
|
|
717
695
|
const driver = new WorkLoopDriver(testRoot);
|
|
718
696
|
const startRes = await driver.start(planFile);
|
|
719
697
|
assert.equal(startRes.success, true);
|