pi-skill-evolve 0.1.5 → 0.1.7
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/extensions/skill-evolve/lib/runs.ts +4 -1
- package/extensions/skill-evolve/lib/web/list-query.ts +43 -0
- package/extensions/skill-evolve/lib/web/markdown-lite.ts +95 -0
- package/extensions/skill-evolve/lib/web/pending.ts +5 -5
- package/extensions/skill-evolve/lib/web/render-controls.ts +58 -32
- package/extensions/skill-evolve/lib/web/render-rows.ts +178 -235
- package/extensions/skill-evolve/lib/web/render-session.ts +105 -34
- package/extensions/skill-evolve/lib/web/render-skills.ts +0 -1
- package/extensions/skill-evolve/lib/web/render-table.ts +126 -0
- package/extensions/skill-evolve/lib/web/render.ts +116 -54
- package/extensions/skill-evolve/lib/web/server.ts +9 -1
- package/extensions/skill-evolve/lib/web/session-render.ts +189 -0
- package/extensions/skill-evolve/lib/web/session-view.ts +100 -84
- package/extensions/skill-evolve/lib/web/shell.ts +42 -11
- package/extensions/skill-evolve/lib/web/style.ts +171 -106
- package/extensions/skill-evolve/lib/web/text.ts +57 -0
- package/extensions/skill-evolve/test/pending-kinds.test.ts +142 -0
- package/extensions/skill-evolve/test/session-nav.test.ts +162 -0
- package/extensions/skill-evolve/test/session-page.test.ts +11 -5
- package/extensions/skill-evolve/test/session-render.test.ts +203 -0
- package/extensions/skill-evolve/test/session-view.test.ts +75 -87
- package/extensions/skill-evolve/test/web-applied.test.ts +30 -22
- package/extensions/skill-evolve/test/web-redesign.test.ts +264 -0
- package/extensions/skill-evolve/test/web-render.test.ts +57 -41
- package/extensions/skill-evolve/test/web-server.test.ts +4 -1
- package/extensions/skill-evolve/test/web-session-column.test.ts +30 -32
- package/extensions/skill-evolve/test/web-shell.test.ts +7 -5
- package/package.json +1 -1
|
@@ -1,27 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* 且文案一致 ——
|
|
2
|
+
* 列表页的「会话」标识:五个视图(待处理 / 待审批 / 已采纳 / 已处理 / 复盘报告)都必须能看出这条 run 属于哪段对话,
|
|
3
|
+
* 且文案一致 —— 显示 sessionId 前 8 位,完整 id 放 title,点进 `?view=session`。
|
|
4
4
|
*
|
|
5
5
|
* 为什么必须每页都有(用户 2026-11-08):「面板展示的也应该是 pi sessionId 否则我都不知道怎么看对应会话」。
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* 重做列表页后会话不再是一列:同一会话的多条 run 归到组标题(`<details>` 的 summary)里,
|
|
7
|
+
* 单条会话的 run 把会话名写在状态格的小字里 —— 两处都由 render-rows.ts 产出,本文件把这条约定钉住。
|
|
8
8
|
*/
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
10
|
import test from "node:test";
|
|
11
11
|
import type { AppliedItem } from "../lib/web/applied.ts";
|
|
12
12
|
import type { ApprovalItem } from "../lib/web/approvals.ts";
|
|
13
|
-
import type { QueueItem } from "../lib/queue.ts";
|
|
14
13
|
import type { DoneItem, PendingItem, WebState } from "../lib/web/data.ts";
|
|
15
14
|
import type { RunReport } from "../lib/web/reports.ts";
|
|
16
15
|
import { renderPage } from "../lib/web/render.ts";
|
|
17
|
-
import { pendingRows } from "../lib/web/render-rows.ts";
|
|
18
16
|
import type { SessionRef } from "../lib/web/run-session.ts";
|
|
19
17
|
|
|
20
18
|
const NOW = new Date(2026, 10, 8, 12).getTime();
|
|
21
19
|
const SESSION_ID = "01a1b90f-08c3-722c-871d-d024b083770a";
|
|
22
20
|
const SESSION: SessionRef = { sessionId: SESSION_ID, sessionFile: `/tmp/${SESSION_ID}.jsonl` };
|
|
23
21
|
const NO_SESSION: SessionRef = { sessionId: null, sessionFile: null };
|
|
24
|
-
const CTX = { csrf: "c", writable: true, view: "pending" as const };
|
|
25
22
|
|
|
26
23
|
function state(parts: Partial<WebState> = {}): WebState {
|
|
27
24
|
return {
|
|
@@ -89,53 +86,54 @@ function reportItem(session: SessionRef): RunReport {
|
|
|
89
86
|
|
|
90
87
|
/** 五个视图各塞一条带会话的行,返回整页 HTML */
|
|
91
88
|
function pageWithSession(): Record<string, string> {
|
|
92
|
-
const
|
|
89
|
+
const current = state({
|
|
93
90
|
pending: [pendingItem(SESSION)],
|
|
94
91
|
approvals: [approvalItem(SESSION)],
|
|
95
92
|
applied: [appliedItem(SESSION)],
|
|
96
93
|
done: [doneItem(SESSION)],
|
|
97
94
|
reports: [reportItem(SESSION)],
|
|
98
|
-
};
|
|
99
|
-
const current = state(base);
|
|
95
|
+
});
|
|
100
96
|
return {
|
|
101
|
-
pending: renderPage(current, "pending"),
|
|
102
|
-
approvals: renderPage(current, "approvals"),
|
|
103
|
-
applied: renderPage(current, "applied"),
|
|
104
|
-
done: renderPage(current, "done"),
|
|
105
|
-
reports: renderPage(current, "reports"),
|
|
97
|
+
pending: renderPage(current, "pending", { now: NOW }),
|
|
98
|
+
approvals: renderPage(current, "approvals", { now: NOW }),
|
|
99
|
+
applied: renderPage(current, "applied", { now: NOW }),
|
|
100
|
+
done: renderPage(current, "done", { now: NOW }),
|
|
101
|
+
reports: renderPage(current, "reports", { now: NOW }),
|
|
106
102
|
};
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
test("
|
|
105
|
+
test("五个视图都能看到会话:链接指向 ?view=session,文案是 sessionId 前 8 位,title 是完整 id", () => {
|
|
110
106
|
const pages = pageWithSession();
|
|
107
|
+
const href = `/?view=session&sessionId=${SESSION_ID}`;
|
|
111
108
|
for (const [view, html] of Object.entries(pages)) {
|
|
112
|
-
const href = `/?view=session&sessionId=${SESSION_ID}`;
|
|
113
109
|
assert.equal(html.includes(href), true, `${view} 页缺会话链接`);
|
|
114
|
-
assert.match(html, /会话 01a1b90f/, `${view}
|
|
110
|
+
assert.match(html, /会话 01a1b90f/, `${view} 页要显示会话前 8 位`);
|
|
115
111
|
assert.equal(html.includes(`title="${SESSION_ID}"`), true, `${view} 页 title 要放完整 sessionId`);
|
|
116
112
|
}
|
|
117
113
|
});
|
|
118
114
|
|
|
119
|
-
test("
|
|
120
|
-
const html =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
assert.match(html,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test("待审批行:summary 里同样带会话列(它是自己拼的 details,最容易漏)", () => {
|
|
128
|
-
const html = renderPage(state({ approvals: [approvalItem(SESSION)] }), "approvals");
|
|
129
|
-
assert.match(html, /<summary class="head">[^<]*<a class="sess" href="\/\?view=session&sessionId=/);
|
|
115
|
+
test("单条会话的 run 不进组:会话名写在状态格的小字里,runId 只出现在操作列", () => {
|
|
116
|
+
const html = renderPage(state({ pending: [pendingItem(SESSION)] }), "pending", { now: NOW });
|
|
117
|
+
assert.match(html, /<span class="sk-sess"><a class="sess"/, "会话名应在状态格的小字里");
|
|
118
|
+
assert.equal(html.includes('<details class="grp"'), false, "只有一条 run 时不该出现会话分组");
|
|
119
|
+
// runId 不再是主标识:只在操作列,显示末 8 位,title 放完整值
|
|
120
|
+
assert.match(html, /<td class="ops">.*title="run-p-1794133942"/);
|
|
130
121
|
});
|
|
131
122
|
|
|
132
123
|
test("没有 sessionId 的老 run:写「会话 —」占位,不造空链接、不报错", () => {
|
|
133
|
-
const html = renderPage(state({ pending: [pendingItem(NO_SESSION)], done: [doneItem(NO_SESSION)] }), "pending"
|
|
134
|
-
|
|
124
|
+
const html = renderPage(state({ pending: [pendingItem(NO_SESSION)], done: [doneItem(NO_SESSION)] }), "pending", {
|
|
125
|
+
now: NOW,
|
|
126
|
+
});
|
|
127
|
+
assert.match(html, /<span class="sk-sess">会话 —<\/span>/);
|
|
135
128
|
assert.equal(html.includes("view=session"), false, "缺 id 时不许出现会话链接");
|
|
136
129
|
});
|
|
137
130
|
|
|
138
131
|
test("会话链接带 file 参数:页面可直接打开声明过的会话文件,不必再反查", () => {
|
|
139
|
-
const html =
|
|
132
|
+
const html = renderPage(state({ pending: [pendingItem(SESSION)] }), "pending", { now: NOW });
|
|
140
133
|
assert.match(html, new RegExp(`file=%2Ftmp%2F${SESSION_ID}\\.jsonl`));
|
|
141
134
|
});
|
|
135
|
+
|
|
136
|
+
test("同一会话的多条 run 归到组标题:会话名出现在 details 的 summary 里", () => {
|
|
137
|
+
const html = renderPage(state({ pending: [pendingItem(SESSION), pendingItem(SESSION)] }), "pending", { now: NOW });
|
|
138
|
+
assert.match(html, /<details class="grp" open>\n<summary><a class="sess" href="\/\?view=session&sessionId=/);
|
|
139
|
+
});
|
|
@@ -37,17 +37,19 @@ test("高亮只落在当前 tab 上;详情页(active=null)一个都不高
|
|
|
37
37
|
assert.equal(tabs(null).includes('class="active"'), false);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test("
|
|
40
|
+
test("状态栏:四个数字,待批 / 已采纳各自是链接(回滚率不进这一行,它在周报里)", () => {
|
|
41
41
|
const html = statusBar(state());
|
|
42
|
-
assert.match(html, /复盘中 2 · 卡死 1 · 待批 <a href="\/\?view=approvals">3<\/a> · 已采纳 <a href="\/\?view=applied">4<\/a
|
|
42
|
+
assert.match(html, /复盘中 2 · 卡死 1 · 待批 <a href="\/\?view=approvals">3<\/a> · 已采纳 <a href="\/\?view=applied">4<\/a>/);
|
|
43
|
+
assert.equal(html.includes("回滚率"), false, "回滚率是周报口径,不该混进「现在要处理什么」这一行");
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
test("骨架:标题、状态栏、导航、正文、页脚各就各位", () => {
|
|
46
47
|
const html = shellPage({ state: state(), title: "已采纳", active: "applied", body: "<main>正文</main>" });
|
|
47
48
|
assert.match(html, /<title>技能进化 · 已采纳<\/title>/);
|
|
48
|
-
assert.match(html, /<p class="
|
|
49
|
-
|
|
50
|
-
assert.match(html,
|
|
49
|
+
assert.match(html, /<p class="sum">复盘中 2/);
|
|
50
|
+
// tab 上带计数徽章(数量就是列表里的条数,不另算一份)
|
|
51
|
+
assert.match(html, /<nav class="tabs">\n<a href="\/\?view=pending">待处理<span class="cnt">0<\/span><\/a>/);
|
|
52
|
+
assert.equal(html.includes("</nav>\n</header>\n<main>正文</main>\n<footer>"), true, "nav 与正文之间只有粘性区结束标签");
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
test("?view= 白名单:三个新视图认,乱值一律回落待处理", () => {
|