pi-skill-evolve 0.1.11 → 0.1.12
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/CHANGELOG.md +12 -0
- package/README.md +16 -0
- package/extensions/skill-evolve/index.ts +10 -0
- package/extensions/skill-evolve/lib/evolution.ts +36 -0
- package/extensions/skill-evolve/lib/pipeline.ts +206 -0
- package/extensions/skill-evolve/lib/recover.ts +182 -0
- package/extensions/skill-evolve/lib/review-spawn.ts +18 -1
- package/extensions/skill-evolve/lib/review-supervisor.ts +18 -2
- package/extensions/skill-evolve/lib/supervisor-runtime.ts +127 -0
- package/extensions/skill-evolve/lib/web/pending.ts +13 -0
- package/extensions/skill-evolve/lib/web/render-run.ts +37 -0
- package/extensions/skill-evolve/lib/web/run-detail.ts +20 -0
- package/extensions/skill-evolve/lib/web/style.ts +18 -0
- package/extensions/skill-evolve/test/pipeline.test.ts +141 -0
- package/extensions/skill-evolve/test/recover.test.ts +153 -0
- package/extensions/skill-evolve/test/review-spawn.test.ts +30 -2
- package/extensions/skill-evolve/test/supervisor-runtime.test.ts +107 -0
- package/extensions/skill-evolve/test/web-data.test.ts +2 -0
- package/extensions/skill-evolve/test/web-render-run.test.ts +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 督导运行目录单测:npm 包装形态下督导脚本在 node_modules 里,Node 的 --experimental-strip-types
|
|
3
|
+
* 会拒绝它(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING)。这一层唯一的职责就是「先搬家再跑」,
|
|
4
|
+
* 所以测试只盯三件事:**该搬的搬**、**搬家后内容完整**、**搬不了就退回原路径**。
|
|
5
|
+
*/
|
|
6
|
+
import { test } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as os from "node:os";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
ensureSupervisorRuntime,
|
|
13
|
+
needsCopy,
|
|
14
|
+
packageVersionOf,
|
|
15
|
+
RUNTIME_DIRNAME,
|
|
16
|
+
runtimeDirOf,
|
|
17
|
+
runtimeKey,
|
|
18
|
+
} from "../lib/supervisor-runtime.ts";
|
|
19
|
+
|
|
20
|
+
function tmpRoot(): string {
|
|
21
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), "skill-evolve-runtime-"));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 造一份「装在 node_modules 里」的包:lib/ 两个文件 + 一个测试文件(不该被拷) */
|
|
25
|
+
function fakePackage(): { root: string; script: string } {
|
|
26
|
+
const root = path.join(tmpRoot(), "node_modules", "pi-skill-evolve");
|
|
27
|
+
const lib = path.join(root, "extensions", "skill-evolve", "lib");
|
|
28
|
+
fs.mkdirSync(lib, { recursive: true });
|
|
29
|
+
fs.writeFileSync(path.join(lib, "review-supervisor.ts"), "export const a = 1;\n");
|
|
30
|
+
fs.writeFileSync(path.join(lib, "runs.ts"), "export const b = 2;\n");
|
|
31
|
+
fs.writeFileSync(path.join(lib, "runs.test.ts"), "test('x', () => {});\n");
|
|
32
|
+
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "pi-skill-evolve", version: "0.1.12" }));
|
|
33
|
+
return { root, script: path.join(lib, "review-supervisor.ts") };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test("只有 node_modules 里的脚本需要搬家", () => {
|
|
37
|
+
assert.equal(needsCopy("/repo/.pi/npm/node_modules/pi-skill-evolve/extensions/skill-evolve/lib/review-supervisor.ts"), true);
|
|
38
|
+
assert.equal(needsCopy("C:\\repo\\node_modules\\pkg\\lib\\review-supervisor.ts"), true);
|
|
39
|
+
assert.equal(needsCopy("/repo/.pi/extensions/skill-evolve/lib/review-supervisor.ts"), false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("本地扩展原路径直接用,一个文件都不拷", () => {
|
|
43
|
+
const evolutionDir = tmpRoot();
|
|
44
|
+
const script = "/repo/.pi/extensions/skill-evolve/lib/review-supervisor.ts";
|
|
45
|
+
assert.equal(ensureSupervisorRuntime({ evolutionDir, scriptPath: script }), script);
|
|
46
|
+
assert.equal(fs.existsSync(path.join(evolutionDir, "runtime")), false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("node_modules 里的督导搬到 runtime 下,内容齐全且跳过测试文件", () => {
|
|
50
|
+
const { script } = fakePackage();
|
|
51
|
+
const evolutionDir = tmpRoot();
|
|
52
|
+
const entry = ensureSupervisorRuntime({ evolutionDir, scriptPath: script });
|
|
53
|
+
assert.notEqual(entry, script, "必须换成副本路径");
|
|
54
|
+
const key = runtimeKey(script, "0.1.12");
|
|
55
|
+
assert.equal(entry, path.join(runtimeDirOf(evolutionDir, key), "review-supervisor.ts"));
|
|
56
|
+
assert.equal(fs.readFileSync(entry, "utf8"), "export const a = 1;\n");
|
|
57
|
+
assert.equal(fs.existsSync(path.join(path.dirname(entry), "runs.ts")), true, "同目录的相对 import 必须一起搬");
|
|
58
|
+
assert.equal(fs.existsSync(path.join(path.dirname(entry), "runs.test.ts")), false);
|
|
59
|
+
assert.equal(fs.existsSync(path.join(path.dirname(entry), ".ready")), true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("重复调用复用同一份副本,不重复拷贝", () => {
|
|
63
|
+
const { script } = fakePackage();
|
|
64
|
+
const evolutionDir = tmpRoot();
|
|
65
|
+
const first = ensureSupervisorRuntime({ evolutionDir, scriptPath: script });
|
|
66
|
+
const stamp = fs.statSync(first).mtimeMs;
|
|
67
|
+
const second = ensureSupervisorRuntime({ evolutionDir, scriptPath: script });
|
|
68
|
+
assert.equal(second, first);
|
|
69
|
+
assert.equal(fs.statSync(second).mtimeMs, stamp, "第二次不该重写文件");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("内容变了(size/mtime 变)就换新目录,旧目录被清掉", () => {
|
|
73
|
+
const { script } = fakePackage();
|
|
74
|
+
const evolutionDir = tmpRoot();
|
|
75
|
+
const first = ensureSupervisorRuntime({ evolutionDir, scriptPath: script });
|
|
76
|
+
fs.writeFileSync(script, "export const a = 123456;\n");
|
|
77
|
+
const future = new Date(Date.now() + 5000);
|
|
78
|
+
fs.utimesSync(script, future, future);
|
|
79
|
+
const second = ensureSupervisorRuntime({ evolutionDir, scriptPath: script });
|
|
80
|
+
assert.notEqual(second, first);
|
|
81
|
+
assert.equal(fs.existsSync(first), false, "旧版本副本要清掉");
|
|
82
|
+
assert.equal(fs.readFileSync(second, "utf8"), "export const a = 123456;\n");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("搬不了(目标目录建不出来)就退回原路径,只记日志不抛", () => {
|
|
86
|
+
const { script } = fakePackage();
|
|
87
|
+
const blocked = path.join(tmpRoot(), "file-not-dir");
|
|
88
|
+
fs.writeFileSync(blocked, "x");
|
|
89
|
+
const logs: string[] = [];
|
|
90
|
+
const entry = ensureSupervisorRuntime({
|
|
91
|
+
evolutionDir: path.join(blocked, "sub"),
|
|
92
|
+
scriptPath: script,
|
|
93
|
+
log: (msg) => logs.push(msg),
|
|
94
|
+
});
|
|
95
|
+
assert.equal(entry, script);
|
|
96
|
+
assert.match(logs.join("\n"), /督导运行目录准备失败/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("缓存键带版本与入口文件指纹,包版本读得到就直接用", () => {
|
|
100
|
+
const { script } = fakePackage();
|
|
101
|
+
assert.equal(packageVersionOf(script), "0.1.12");
|
|
102
|
+
const key = runtimeKey(script, "0.1.12");
|
|
103
|
+
assert.match(key, /^lib-0\.1\.12-\d+-\d+$/);
|
|
104
|
+
assert.equal(runtimeKey(script), key, "不传版本时从最近的 package.json 取");
|
|
105
|
+
assert.equal(RUNTIME_DIRNAME, "supervisor");
|
|
106
|
+
assert.equal(packageVersionOf(path.join(tmpRoot(), "lonely", "x.ts")), "", "找不到 package.json 就是空串");
|
|
107
|
+
});
|
|
@@ -313,6 +313,8 @@ test("pending:正在跑的 run 排最前,阶段 / 进程 / 最近输出 /
|
|
|
313
313
|
assert.match(detail, /进程:pid 1234 · 已跑 1m 30s(上限 20 分钟)/);
|
|
314
314
|
assert.match(detail, /最近输出:(还没有输出)/, "child.log 还没生成时明确说没有输出");
|
|
315
315
|
assert.match(detail, /日志:tail -f \.pi\/evolution\/runs\/live\/child\.log/);
|
|
316
|
+
// 五段进度:分析在跑 ⇒ 分析/提案都该是「…」,评审判「等提案」而不是含糊的「在进行」
|
|
317
|
+
assert.match(detail, /流水线:派发✓ 分析… 提案… 评审· 采纳·/);
|
|
316
318
|
assert.equal(state.pending[1]!.kind, "approval", "待批排在运行中之后");
|
|
317
319
|
});
|
|
318
320
|
|
|
@@ -69,6 +69,17 @@ function detail(parts: Partial<RunDetail> = {}): RunDetail {
|
|
|
69
69
|
findings: [{ skill: "demo", reason: "G", confidence: "high", lines: [578, 622] }],
|
|
70
70
|
findingsTotal: 1,
|
|
71
71
|
notes: "",
|
|
72
|
+
// 流水线固定在详情页顶部:夹具给一条「已驳回」的完整链路,断言单独在 test/pipeline.test.ts
|
|
73
|
+
pipeline: {
|
|
74
|
+
stages: [
|
|
75
|
+
{ key: "dispatch", label: "派发", state: "done", detail: "复盘子进程 pid 65757 已退出", at: 1794133942865 },
|
|
76
|
+
{ key: "analyze", label: "分析", state: "done", detail: "01-findings.json 已落盘", at: 1794134000000 },
|
|
77
|
+
{ key: "propose", label: "提案", state: "done", detail: "improve → demo", at: 1794134050000 },
|
|
78
|
+
{ key: "review", label: "评审", state: "done", detail: "rejected / revise", at: 1794134158913 },
|
|
79
|
+
{ key: "apply", label: "采纳", state: "skipped", detail: "评审驳回,不会采纳", at: null },
|
|
80
|
+
],
|
|
81
|
+
hint: "流程结束:卡在「采纳」— 评审驳回,不会采纳",
|
|
82
|
+
},
|
|
72
83
|
...parts,
|
|
73
84
|
};
|
|
74
85
|
}
|