pi-skill-evolve 0.1.8 → 0.1.9

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.
@@ -29,21 +29,6 @@ export const RANGE_LABEL: Record<RangeKey, string> = {
29
29
  /** 状态下拉的可选值:既有 run 终态,也有待处理页自己的两种(running / stale) */
30
30
  export const STATUS_OPTIONS = ["running", "stale", "reviewed", "applied", "rejected", "failed", "analyzed"] as const;
31
31
 
32
- /**
33
- * 状态下拉的中文标签(用户 2026-11-08:「这里的下拉应该是中文」)。
34
- * 值仍然是状态机原文(URL 与过滤逻辑不变),只改给人看的那一侧;
35
- * 与徽章文案保持同一套词——同一个概念全站只能有一个中文名。
36
- */
37
- export const STATUS_LABEL: Record<(typeof STATUS_OPTIONS)[number], string> = {
38
- running: "复盘中",
39
- stale: "卡死",
40
- reviewed: "待入队",
41
- applied: "已采纳",
42
- rejected: "已拒绝",
43
- failed: "失败",
44
- analyzed: "待评审",
45
- };
46
-
47
32
  export interface ListFilter {
48
33
  skill: string;
49
34
  /** 空串 = 全部 */
@@ -21,7 +21,7 @@ import {
21
21
  telemetryOffWhileEnabled,
22
22
  } from "../flags.ts";
23
23
  import type { WebView } from "./data.ts";
24
- import { RANGE_KEYS, RANGE_LABEL, STATUS_LABEL, STATUS_OPTIONS, type ListFilter, type ViewPage } from "./filter.ts";
24
+ import { RANGE_KEYS, RANGE_LABEL, type ListFilter, type ViewPage } from "./filter.ts";
25
25
  import { listHref, type ListExtras } from "./list-query.ts";
26
26
  import { escapeHtml, formatStamp, formatTimeSeconds } from "./text.ts";
27
27
  import type { WebControls } from "./activity.ts";
@@ -106,16 +106,7 @@ ${run}</div>
106
106
  ${warn}${backlog}`;
107
107
  }
108
108
 
109
- /** 状态下拉:先「全部」,再固定七个状态(顺序与 STATUS_OPTIONS 一致,别在渲染层重排) */
110
- function statusSelect(current: string): string {
111
- const options = [`<option value="">全部状态</option>`];
112
- for (const status of STATUS_OPTIONS) {
113
- // value 仍是状态机原文(过滤逻辑不变),只把展示换中文
114
- options.push(`<option value="${status}"${status === current ? " selected" : ""}>${STATUS_LABEL[status]}</option>`);
115
- }
116
- return options.join("");
117
- }
118
-
109
+ /** 时间下拉:只有「看多久内」需要人手动换,状态与技能已在 tab 与技能页面各自解决了 */
119
110
  function rangeSelect(current: string): string {
120
111
  return RANGE_KEYS.map(
121
112
  (key) => `<option value="${key}"${key === current ? " selected" : ""}>${RANGE_LABEL[key]}</option>`,
@@ -123,16 +114,16 @@ function rangeSelect(current: string): string {
123
114
  }
124
115
 
125
116
  /**
126
- * 筛选表单:hidden view 保证 GET 提交后还在同一页;「重置」链回不带筛选的本页。
127
- * `open` / `live` 也一并带上:筛选不该顺手把「展开分组」「自动刷新」这两个开关清掉。
117
+ * 筛选表单:只留「时间」一个条件。
118
+ * 技能:每个列表页的行里已经有技能列,技能页可以按技能点进来,再摆一个模糊搜索框没人知道该填什么;
119
+ * 状态:顶部 tab 本身就是按状态分的(待处理 / 待审批 / 已采纳 / 已处理 / 复盘报告),重复选毫无意义。
120
+ * 两个参数(skill / status)仍由 `parseListQuery` 支持,旧链接与代码内部跳转不会断,只是不再出输入框。
128
121
  */
129
122
  export function renderFilterForm(view: WebView, filter: ListFilter, extras: ListExtras): string {
130
123
  const keep = extraInputs(extras);
131
124
  return `<form class="filter" method="get">
132
125
  <input type="hidden" name="view" value="${view}">
133
- ${keep}<label>技能 <input type="text" name="skill" value="${escapeHtml(filter.skill)}" placeholder="技能名包含…" size="16"></label>
134
- <label>状态 <select name="status">${statusSelect(filter.status)}</select></label>
135
- <label>时间 <select name="range">${rangeSelect(filter.range)}</select></label>
126
+ ${keep}<label>时间 <select name="range">${rangeSelect(filter.range)}</select></label>
136
127
  <button type="submit">筛选</button>
137
128
  <a class="btn" href="${escapeHtml(listHref(view, filter, 1, extras))}">重置</a>
138
129
  </form>`;
@@ -1,18 +1,19 @@
1
1
  /**
2
- * 筛选栏文案的验收测试(用户 2026-11-08:「这里的下拉应该是中文, 但是这个搜索框是干嘛的?」)。
2
+ * 筛选栏的验收测试(用户 2026-11-08:「这个搜索框的技能和状态不用了吧, 技能不知道用来搜索什么,
3
+ * 状态应该在顶部 tab 包含了吧」)。
3
4
  *
4
- * 两条口径:
5
- * 1. 状态下拉的 **value 仍是状态机原文**(URL / 过滤逻辑一个字符都不改),只把给人看的一侧换中文;
6
- * 中文名与列表徽章必须同一套词,同一个概念全站只能有一个名字。
7
- * 2. 技能那个输入框是**筛选条件**,不是搜索框也不是下拉:placeholder 必须写成「技能名包含…」,
8
- * 让人一眼知道「填一部分就够」。
5
+ * 口径:
6
+ * 1. 表单**只剩「时间」**一个条件——技能看不出该填什么、状态与顶部 tab 重复,两个都撤掉;
7
+ * 2. 撤掉的是**输入控件**,不是参数:`?skill=` / `?status=` 仍由 `parseListQuery` 解析,
8
+ * 技能页点进来、旧书签、代码内部跳转都不会断;
9
+ * 3. 表单仍是 GET + 保留 `view`,否则一提交就跳出当前页。
9
10
  *
10
11
  * 纪律:纯字符串断言,不碰文件系统、不起服务。
11
12
  */
12
13
  import { test } from "node:test";
13
14
  import assert from "node:assert/strict";
14
15
 
15
- import { parseListQuery, STATUS_LABEL, STATUS_OPTIONS } from "../lib/web/filter.ts";
16
+ import { parseListQuery } from "../lib/web/filter.ts";
16
17
  import { renderFilterForm } from "../lib/web/render-controls.ts";
17
18
 
18
19
  const FILTER = parseListQuery(new URLSearchParams(""));
@@ -22,36 +23,38 @@ function form(): string {
22
23
  return renderFilterForm("pending", FILTER, EXTRAS);
23
24
  }
24
25
 
25
- test("状态下拉每一项都显示中文,value 保持状态机原文", () => {
26
+ test("筛选栏不再有技能输入框与状态下拉", () => {
26
27
  const html = form();
27
- for (const status of STATUS_OPTIONS) {
28
- assert.ok(html.includes(`value="${status}"`), `value 必须是 ${status}`);
29
- assert.ok(html.includes(`>${STATUS_LABEL[status]}<`), `${status} 要显示成 ${STATUS_LABEL[status]}`);
30
- }
28
+ assert.ok(!html.includes('name="skill"'), "技能搜索框已撤掉");
29
+ assert.ok(!html.includes('name="status"'), "状态下拉已撤掉(顶部 tab 就是状态)");
30
+ assert.ok(!html.includes("全部状态"));
31
+ assert.ok(!html.includes("全部技能"));
31
32
  });
32
33
 
33
- test("状态下拉里不出现英文状态词", () => {
34
+ test("时间筛选还在,且三个档位齐全", () => {
34
35
  const html = form();
35
- const select = html.slice(html.indexOf('name="status"'), html.indexOf("</select>", html.indexOf('name="status"')));
36
- for (const status of STATUS_OPTIONS) {
37
- assert.ok(!select.includes(`>${status}<`), `不应再出现英文项 >${status}<`);
36
+ assert.ok(html.includes('name="range"'));
37
+ for (const label of ["最近 7 天", "最近 30 天", "全部时间"]) {
38
+ assert.ok(html.includes(`>${label}<`), `缺时间档位:${label}`);
38
39
  }
39
40
  });
40
41
 
41
- test("中文标签覆盖七个可选状态,且没有空值", () => {
42
- assert.equal(Object.keys(STATUS_LABEL).length, STATUS_OPTIONS.length);
43
- for (const label of Object.values(STATUS_LABEL)) assert.ok(label.trim().length > 0);
42
+ test("表单仍是 GET,且保留 view", () => {
43
+ const html = form();
44
+ assert.ok(html.includes('method="get"'));
45
+ assert.ok(html.includes('<input type="hidden" name="view" value="pending">'));
44
46
  });
45
47
 
46
- test("技能输入框的 placeholder 说明它是筛选条件", () => {
47
- const html = form();
48
- assert.ok(html.includes('name="skill"'));
49
- assert.ok(html.includes('placeholder="技能名包含…"'), "要告诉人这里填的是「技能名的一部分」");
50
- assert.ok(!html.includes('placeholder="全部技能"'), "旧 placeholder 让人误以为是静态字段");
48
+ test("参数没被删:URL 里的 skill / status 仍然生效", () => {
49
+ const parsed = parseListQuery(new URLSearchParams("skill=deploy-test&status=stale&range=30d"));
50
+ assert.equal(parsed.skill, "deploy-test");
51
+ assert.equal(parsed.status, "stale");
52
+ assert.equal(parsed.range, "30d");
51
53
  });
52
54
 
53
- test("筛选表单仍然是 GET + 保留 view", () => {
54
- const html = form();
55
- assert.ok(html.includes('method="get"'));
56
- assert.ok(html.includes('<input type="hidden" name="view" value="pending">'));
55
+ test("技能页跳过来的链接照旧能带 skill 参数(只是不再有输入框)", () => {
56
+ const parsed = parseListQuery(new URLSearchParams("skill=ui-verify"));
57
+ const html = renderFilterForm("reports", parsed, EXTRAS);
58
+ assert.ok(!html.includes('name="skill"'));
59
+ assert.equal(parsed.skill, "ui-verify", "筛选逻辑要能吃到链接里的技能");
57
60
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-skill-evolve",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "技能进化:采集会话中的技能曝光/加载,异步复盘、生成改进提案并独立评审,附本机 Web 管理面板",
5
5
  "keywords": [
6
6
  "pi-package",