@unifan/pi-unifan-zh 1.0.37 → 1.0.38
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
|
|
|
@@ -10,15 +10,13 @@ export function isExplicit03WorkTrigger(prompt: string): boolean {
|
|
|
10
10
|
// 1. Direct skill invocation or slash command
|
|
11
11
|
if (
|
|
12
12
|
lower.startsWith("/skill:03-work") ||
|
|
13
|
-
lower === "/skill:03"
|
|
14
|
-
lower.startsWith("03-work")
|
|
13
|
+
lower === "/skill:03"
|
|
15
14
|
) {
|
|
16
15
|
return true;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
// 2. Strict keywords and natural language commands to start/resume 03 work
|
|
20
19
|
const strictPatterns = [
|
|
21
|
-
/^(\/)?(03|03-work)$/i,
|
|
22
20
|
/^开始(03|干活|编码|实现|写代码)/,
|
|
23
21
|
/^执行(03|03-work|干活)/,
|
|
24
22
|
/^自主(干活|工作|编码)/,
|
|
@@ -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" });
|
|
@@ -480,10 +474,6 @@ test("isExplicit03WorkTrigger: accurately identifies 03-work invocations vs harm
|
|
|
480
474
|
// Should match explicit 03 triggers
|
|
481
475
|
assert.equal(isExplicit03WorkTrigger("/skill:03-work"), true);
|
|
482
476
|
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
477
|
assert.equal(isExplicit03WorkTrigger("开始干活"), true);
|
|
488
478
|
assert.equal(isExplicit03WorkTrigger("开始03"), true);
|
|
489
479
|
assert.equal(isExplicit03WorkTrigger("开始实现"), true);
|
|
@@ -493,6 +483,10 @@ test("isExplicit03WorkTrigger: accurately identifies 03-work invocations vs harm
|
|
|
493
483
|
assert.equal(isExplicit03WorkTrigger("resume work"), true);
|
|
494
484
|
assert.equal(isExplicit03WorkTrigger("【03-work 自主循环驱动引擎 · 自动化续跑指令】"), true);
|
|
495
485
|
|
|
486
|
+
// Plain numbers must NEVER trigger (must require /skill: or explicit phrase)
|
|
487
|
+
assert.equal(isExplicit03WorkTrigger("03"), false);
|
|
488
|
+
assert.equal(isExplicit03WorkTrigger("/03"), false);
|
|
489
|
+
|
|
496
490
|
// Must NEVER match other skills or normal dialogue (prevents false auto-starts!)
|
|
497
491
|
assert.equal(isExplicit03WorkTrigger("00"), false);
|
|
498
492
|
assert.equal(isExplicit03WorkTrigger("/skill:00-next"), false);
|
|
@@ -683,37 +677,22 @@ test("E2E Lifecycle: 01 brainstorm -> 02 plan -> 01 rollback -> 03 work -> compa
|
|
|
683
677
|
assert.equal(state.recommendedSkill, "03-work");
|
|
684
678
|
assert.equal(state.totalUnitsCount, 3);
|
|
685
679
|
|
|
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
|
|
680
|
+
// 4. User is in 02, realizes requirements need changes -> Rolls back to 01 via /skill:01-brainstorm
|
|
681
|
+
// In standard Pi, user runs /skill:01-brainstorm to revisit requirements
|
|
707
682
|
await writeFile(bsFile, "# Auth Requirements\nScope: login, register, oauth2", "utf-8");
|
|
708
683
|
|
|
709
|
-
// 5. User returns to 02
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
684
|
+
// 5. User returns to 02 via /skill:02-plan and updates the plan
|
|
685
|
+
await writeFile(
|
|
686
|
+
planFile,
|
|
687
|
+
"# Auth Plan\n### Unit 0 — DB Schema\n### Unit 1 — API Route & OAuth2\n### Unit 2 — Tests",
|
|
688
|
+
"utf-8",
|
|
689
|
+
);
|
|
715
690
|
|
|
716
|
-
|
|
691
|
+
state = await detectWorkflowState(testRoot);
|
|
692
|
+
assert.equal(state.recommendedSkill, "03-work");
|
|
693
|
+
|
|
694
|
+
// 6. User enters 03 -> starts autonomous work via /skill:03-work
|
|
695
|
+
assert.equal(isExplicit03WorkTrigger("/skill:03-work"), true);
|
|
717
696
|
const driver = new WorkLoopDriver(testRoot);
|
|
718
697
|
const startRes = await driver.start(planFile);
|
|
719
698
|
assert.equal(startRes.success, true);
|