dsh-session-eval 0.2.2 → 0.2.4

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 CHANGED
@@ -1,5 +1,21 @@
1
+ ## 0.2.4 — 2026-10-11
2
+
3
+ Feature release: eval-history report files.
4
+
5
+ - New: `/eval-history --out PATH[.md|.json]` writes the history report to a Markdown or JSON file instead of only echoing it (contributed by @varshith84 in #4, merged as 117f918 after a maintainer rebase).
6
+
1
7
  # Changelog
2
8
 
9
+
10
+ ## 0.2.3 — 2026-10-03
11
+
12
+ Docs polish round (no runtime changes).
13
+
14
+ - Install detail completed: requirements, GitHub install route, the `cordis.patch.yml` row.
15
+ - Contributor block links the open-gap issues directly; sample outputs aligned with the shipped version.
16
+ - Community section; bilingual issue templates (`.github/ISSUE_TEMPLATE/`).
17
+ - English/Chinese README sections kept in sync.
18
+
3
19
  ## 0.2.2 — 2026-10-02
4
20
 
5
21
  Contributor-first docs release.
@@ -9,6 +25,8 @@ Contributor-first docs release.
9
25
  - Skeleton: one-liner → install → try-once → toolchain table → 6-line contributor block.
10
26
  - CONTRIBUTING.md (with source map), SECURITY.md, CODE_OF_CONDUCT.md added; GitHub topics set.
11
27
  - Docs no longer hardcode test counts.
28
+ - Install details (GitHub route + `cordis.patch.yml`), Development/Community footers, and issue links #1–#3 added for parity with the sibling plugins.
29
+ - GitHub issue templates (`bug_report`, `feature_request`) added.
12
30
 
13
31
 
14
32
  ## 0.2.1 — 2026-09-26
package/README.md CHANGED
@@ -13,10 +13,26 @@ No benchmark suites to author. No LLM judges. No new runs. Every grade is a fixe
13
13
 
14
14
  ## Install
15
15
 
16
+ **Requirements**: Node.js 20 or 22 · a DeepSeek Harness profile that mounts the `commands` and `sessionQuery` services (the shipped `web` / `agent` profiles qualify).
17
+
16
18
  ```bash
17
19
  dsh plugin --profile web add dsh-session-eval
18
20
  ```
19
21
 
22
+ Or from GitHub:
23
+
24
+ ```bash
25
+ dsh plugin --profile web add github:kittimzhe/dsh-session-eval
26
+ ```
27
+
28
+ The bundle overlay mounts `/eval`, `/eval-diff`, and `/eval-history`. If you maintain your own `cordis.patch.yml`, keep this row:
29
+
30
+ ```yaml
31
+ - insert:
32
+ - id: session-eval
33
+ name: 'dsh-session-eval'
34
+ ```
35
+
20
36
  ## Try it once
21
37
 
22
38
  In DeepSeek Harness:
@@ -26,7 +42,7 @@ In DeepSeek Harness:
26
42
  ```
27
43
 
28
44
  ```text
29
- Session eval — a1b2c3d4… (dsh-session-eval v0.2.1)
45
+ Session eval — a1b2c3d4… (dsh-session-eval v0.2.2)
30
46
  Overall: A
31
47
  Reliability A 2/87 tool results errored (2.3%)
32
48
  Re-ask A 1 re-ask signal(s) over 23 turns (0.4/10 turns)
@@ -49,7 +65,7 @@ All three read through the same trusted `ctx.sessionQuery` seam, so any persiste
49
65
 
50
66
  - **Local dev**: `npm ci && npm run typecheck && npm test && npm run bundle` (Node 20 or 22).
51
67
  - **Start in the source**: [`src/grade.ts`](src/grade.ts) (thresholds, grade cards), [`src/metrics.ts`](src/metrics.ts) (metric extraction), [`src/trend.ts`](src/trend.ts) (`/eval-history` trend logic). See [CONTRIBUTING.md](CONTRIBUTING.md) for the source map and first-PR suggestions.
52
- - **Open gaps**: threshold configurability, wider `/eval-history` filters, trend export — tracked in [issues labeled `good first issue`](https://github.com/kittimzhe/dsh-session-eval/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
68
+ - **Open gaps**: [#1](https://github.com/kittimzhe/dsh-session-eval/issues/1) (configurable thresholds), [#2](https://github.com/kittimzhe/dsh-session-eval/issues/2) (`/eval-history --since`), [#3](https://github.com/kittimzhe/dsh-session-eval/issues/3) (`/eval-history --out`) — or browse [issues labeled `good first issue`](https://github.com/kittimzhe/dsh-session-eval/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
53
69
  - **Rules**: behavior changes need tests; doc changes must update `README.md` and `README.zh.md` in sync; version releases belong to the maintainer. Details: [CONTRIBUTING.md](CONTRIBUTING.md).
54
70
 
55
71
  ## How it differs from benchmark-style evaluation
@@ -75,6 +91,8 @@ Both are useful; they don't compete. Benchmarks test what you *predicted*; retro
75
91
  | `/eval-history [N]` | Grade the last N sessions in this workspace and show the trend |
76
92
  | `/eval-history … --json` | Machine-readable history report |
77
93
 
94
+ Use `/eval-history --out report.md` to save the terminal card as Markdown, or add `--json` to save the JSON payload. Relative paths resolve against the current session workspace (the process working directory when unavailable). Parent directories must already exist; file errors are reported without creating directories. Existing output files are overwritten.
95
+
78
96
  `/eval-history` finds the sessions for you via the same `sessionQuery` seam `/archive` uses (scoped to the current workspace's `cwd`), grades each, and diffs the first against the last. `/eval-diff` remains the tool when you already know the two ids that matter.
79
97
 
80
98
  Typical use: grade the ten sessions before and after a plugin or prompt change; a persistent `Reliability` regression is an early warning that the change hurt real work, not just benchmarks.
@@ -99,6 +117,19 @@ Diff verdicts include a noise band (±2pp error rate, ±0.5 re-ask, ±1 tool loa
99
117
 
100
118
  Same session logs in → same grade card out, every time, forever. There is no model in the loop, no sampling, no clock dependence in the metrics (wall time is reported, never graded). This is what makes `/eval-diff` trustworthy as a regression signal.
101
119
 
120
+ ## Development
121
+
122
+ ```bash
123
+ npm ci
124
+ npm run typecheck # tsc --noEmit
125
+ npm test # vitest run
126
+ npm run bundle # tsdown → lib/
127
+ ```
128
+
102
129
  ## License
103
130
 
104
131
  [MIT](LICENSE)
132
+
133
+ ## Community
134
+
135
+ - [Contributing](CONTRIBUTING.md) · [Security policy](SECURITY.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
package/README.zh.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [English](README.md) | 中文
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/dsh-session-eval)](https://www.npmjs.com/package/dsh-session-eval)
6
+ [![npm downloads](https://img.shields.io/npm/dw/dsh-session-eval)](https://www.npmjs.com/package/dsh-session-eval)
6
7
  [![tests](https://github.com/kittimzhe/dsh-session-eval/actions/workflows/test.yml/badge.svg)](https://github.com/kittimzhe/dsh-session-eval/actions/workflows/test.yml)
7
8
  [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
9
 
@@ -12,10 +13,26 @@
12
13
 
13
14
  ## 安装
14
15
 
16
+ **环境要求**:Node.js 20 或 22 · 挂载了 `commands` 与 `sessionQuery` 服务的 DeepSeek Harness profile(官方 `web` / `agent` profile 均满足)。
17
+
15
18
  ```bash
16
19
  dsh plugin --profile web add dsh-session-eval
17
20
  ```
18
21
 
22
+ 或从 GitHub:
23
+
24
+ ```bash
25
+ dsh plugin --profile web add github:kittimzhe/dsh-session-eval
26
+ ```
27
+
28
+ bundle 覆盖层会挂上 `/eval`、`/eval-diff`、`/eval-history`。若你维护自己的 `cordis.patch.yml`,请保留这一行:
29
+
30
+ ```yaml
31
+ - insert:
32
+ - id: session-eval
33
+ name: 'dsh-session-eval'
34
+ ```
35
+
19
36
  ## 试一次
20
37
 
21
38
  在 DeepSeek Harness 里:
@@ -25,7 +42,7 @@ dsh plugin --profile web add dsh-session-eval
25
42
  ```
26
43
 
27
44
  ```text
28
- Session eval — a1b2c3d4… (dsh-session-eval v0.2.1)
45
+ Session eval — a1b2c3d4… (dsh-session-eval v0.2.2)
29
46
  Overall: A
30
47
  Reliability A 2/87 tool results errored (2.3%)
31
48
  Re-ask A 1 re-ask signal(s) over 23 turns (0.4/10 turns)
@@ -48,7 +65,7 @@ Notes:
48
65
 
49
66
  - **本地开发**:`npm ci && npm run typecheck && npm test && npm run bundle`(Node 20 或 22)。
50
67
  - **源码入口**:[`src/grade.ts`](src/grade.ts)(阈值与等级卡)、[`src/metrics.ts`](src/metrics.ts)(指标提取)、[`src/trend.ts`](src/trend.ts)(`/eval-history` 趋势逻辑)。源码地图与第一次 PR 建议见 [CONTRIBUTING.md](CONTRIBUTING.md)。
51
- - **当前缺口**:阈值可配置、`/eval-history` 过滤条件扩展、趋势导出——见 [`good first issue` 标签的 issue](https://github.com/kittimzhe/dsh-session-eval/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。
68
+ - **当前缺口**:[#1](https://github.com/kittimzhe/dsh-session-eval/issues/1)(阈值可配置)、[#2](https://github.com/kittimzhe/dsh-session-eval/issues/2)(`/eval-history --since`)、[#3](https://github.com/kittimzhe/dsh-session-eval/issues/3)(`/eval-history --out`)——或浏览 [`good first issue` 标签](https://github.com/kittimzhe/dsh-session-eval/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)。
52
69
  - **规矩**:行为变更必须带测试;文档必须 `README.md` 与 `README.zh.md` 同步改;版本发布由维护者执行。详见 [CONTRIBUTING.md](CONTRIBUTING.md)。
53
70
 
54
71
  ## 与基准式评测(dsh-eval)的区别
@@ -74,6 +91,8 @@ Notes:
74
91
  | `/eval-history [N]` | 本工作区最近 N 个会话成绩单 + 首末趋势 |
75
92
  | `/eval-history … --json` | 机器可读历史报告 |
76
93
 
94
+ 使用 `/eval-history --out report.md` 将终端卡片保存为 Markdown,添加 `--json` 则保存 JSON。相对路径基于当前会话工作目录(缺失时使用进程工作目录)。父目录必须已存在;写入失败会返回错误,不会创建目录。已存在的输出文件会被覆盖。
95
+
77
96
  `/eval-history` 通过与 `/archive` 相同的 `sessionQuery` 接缝自动找到会话(按当前工作区 `cwd` 过滤),逐个评级,并把首尾两个会话做趋势对比——不用再手动挑 id。已经知道两个关键 id 时用 `/eval-diff`。
78
97
 
79
98
  典型用法:换插件或 prompt 前后各评十个会话;`Reliability` 持续回归 = 改动伤害了真实工作的早期预警。
@@ -96,6 +115,19 @@ Diff 判定带噪声带(错误率 ±2pp、Re-ask ±0.5、工具负载 ±1)
96
115
 
97
116
  同样的日志进 → 同样的等级出。没有模型在环、没有采样、指标不含时钟依赖(wall time 只报告不评级)。这是 `/eval-diff` 能作为回归信号被信任的原因。
98
117
 
118
+ ## 开发
119
+
120
+ ```bash
121
+ npm ci
122
+ npm run typecheck # tsc --noEmit
123
+ npm test # vitest run
124
+ npm run bundle # tsdown → lib/
125
+ ```
126
+
99
127
  ## 许可
100
128
 
101
129
  [MIT](LICENSE)
130
+
131
+ ## 社区
132
+
133
+ - [贡献指南](CONTRIBUTING.md) · [安全策略](SECURITY.md) · [行为准则](CODE_OF_CONDUCT.md)
package/lib/index.d.ts CHANGED
@@ -133,11 +133,12 @@ declare function executeEval(ctx: Context, invocation: CommandInvocation, seam?:
133
133
  declare function executeEvalDiff(ctx: Context, invocation: CommandInvocation, seam?: QuerySeam): Promise<CommandResult>;
134
134
  //#endregion
135
135
  //#region src/historyCommand.d.ts
136
- declare const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] (N = last N sessions, 1-20, default 5)";
136
+ declare const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] [--out PATH] (N = last N sessions, 1-20, default 5)";
137
137
  /** Args for /eval-history. */
138
138
  interface EvalHistoryArgs {
139
139
  readonly count: number;
140
140
  readonly json: boolean;
141
+ readonly out?: string;
141
142
  }
142
143
  /** Parse /eval-history input; returns args or a usage-error string. */
143
144
  declare function parseEvalHistoryArgs(rawInput: string): EvalHistoryArgs | string;
@@ -177,7 +178,7 @@ declare function executeEvalHistory(ctx: Context, invocation: CommandInvocation,
177
178
  *
178
179
  * @module dsh-session-eval/version
179
180
  */
180
- declare const VERSION = "0.2.2";
181
+ declare const VERSION = "0.2.4";
181
182
  //#endregion
182
183
  //#region src/index.d.ts
183
184
  declare const name = "session-eval";
package/lib/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { SessionId } from "@deepseek-ai/dsh-session";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
2
4
  //#region src/metrics.ts
3
5
  /** Count a correction signal for every extra user message inside a consecutive-user run. */
4
6
  function countCorrections(events) {
@@ -227,7 +229,7 @@ function compareSessions(before, after) {
227
229
  *
228
230
  * @module dsh-session-eval/version
229
231
  */
230
- const VERSION = "0.2.2";
232
+ const VERSION = "0.2.4";
231
233
  //#endregion
232
234
  //#region src/evalCommand.ts
233
235
  const EVAL_USAGE = "Usage: /eval [--id <sessionId>] [--json]";
@@ -327,14 +329,14 @@ async function executeEval(ctx, invocation, seam = ctx.sessionQuery) {
327
329
  if (parsed.json === true) return {
328
330
  kind: "success",
329
331
  text: JSON.stringify({
330
- generator: "dsh-session-eval v0.2.2",
332
+ generator: "dsh-session-eval v0.2.4",
331
333
  metrics,
332
334
  card
333
335
  }, null, 2)
334
336
  };
335
337
  return {
336
338
  kind: "success",
337
- text: renderCard(card, "dsh-session-eval v0.2.2")
339
+ text: renderCard(card, "dsh-session-eval v0.2.4")
338
340
  };
339
341
  }
340
342
  /** Execute /eval-diff against the session-query seam. */
@@ -358,25 +360,44 @@ async function executeEvalDiff(ctx, invocation, seam = ctx.sessionQuery) {
358
360
  if (parsed.json === true) return {
359
361
  kind: "success",
360
362
  text: JSON.stringify({
361
- generator: "dsh-session-eval v0.2.2",
363
+ generator: "dsh-session-eval v0.2.4",
362
364
  report
363
365
  }, null, 2)
364
366
  };
365
367
  return {
366
368
  kind: "success",
367
- text: renderTrend(report, "dsh-session-eval v0.2.2")
369
+ text: renderTrend(report, "dsh-session-eval v0.2.4")
368
370
  };
369
371
  }
370
372
  //#endregion
371
373
  //#region src/historyCommand.ts
372
- const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] (N = last N sessions, 1-20, default 5)";
374
+ /**
375
+ * `/eval-history` — grade the last N sessions in this workspace and show the trend.
376
+ *
377
+ * Completes the regression-tracking story: `/eval-diff` compares two sessions
378
+ * you already know about; this command finds them for you (via
379
+ * `ctx.sessionQuery.listSessions()`, same seam as `/archive` in
380
+ * dsh-session-export) and answers "is the trend improving?" out of the box.
381
+ *
382
+ * @module dsh-session-eval/historyCommand
383
+ */
384
+ const EVAL_HISTORY_USAGE = "Usage: /eval-history [N] [--json] [--out PATH] (N = last N sessions, 1-20, default 5)";
373
385
  /** Parse /eval-history input; returns args or a usage-error string. */
374
386
  function parseEvalHistoryArgs(rawInput) {
375
387
  const tokens = rawInput.trim().split(/\s+/).filter((t) => t.length > 0);
376
388
  let count = 5;
377
389
  let json = false;
378
390
  let sawCount = false;
379
- for (const token of tokens) {
391
+ let out;
392
+ for (let i = 0; i < tokens.length; i++) {
393
+ const token = tokens[i];
394
+ if (token === "--out") {
395
+ if (out !== void 0) return `--out may be given only once.\n${EVAL_HISTORY_USAGE}`;
396
+ const path = tokens[++i];
397
+ if (!path || path.startsWith("--")) return `--out requires a path.\n${EVAL_HISTORY_USAGE}`;
398
+ out = path;
399
+ continue;
400
+ }
380
401
  if (token === "--json") {
381
402
  if (json) return `--json may be given only once.\n${EVAL_HISTORY_USAGE}`;
382
403
  json = true;
@@ -392,7 +413,8 @@ function parseEvalHistoryArgs(rawInput) {
392
413
  }
393
414
  return {
394
415
  count,
395
- json
416
+ json,
417
+ ...out !== void 0 ? { out } : {}
396
418
  };
397
419
  }
398
420
  function toMillis(createdAt) {
@@ -441,10 +463,6 @@ async function executeEvalHistory(ctx, invocation, seam = ctx.sessionQuery) {
441
463
  id: String(header.id),
442
464
  createdAt: toMillis(header.createdAt)
443
465
  })).sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0)).slice(-parsed.count);
444
- if (recent.length === 0) return {
445
- kind: "success",
446
- text: "No sessions found in this workspace; nothing to grade."
447
- };
448
466
  const entries = [];
449
467
  for (const header of recent) {
450
468
  let events;
@@ -468,16 +486,25 @@ async function executeEvalHistory(ctx, invocation, seam = ctx.sessionQuery) {
468
486
  entries,
469
487
  trend: entries.length >= 2 ? compareSessions(entries[0].metrics, entries[entries.length - 1].metrics) : null
470
488
  };
471
- if (parsed.json === true) return {
472
- kind: "success",
473
- text: JSON.stringify({
474
- generator: `dsh-session-eval v${VERSION}`,
475
- report
476
- }, null, 2)
477
- };
489
+ const text = parsed.json ? JSON.stringify({
490
+ generator: `dsh-session-eval v${VERSION}`,
491
+ report
492
+ }, null, 2) : renderHistory(report);
493
+ if (parsed.out !== void 0) {
494
+ const path = resolve(typeof ownCwd === "string" ? ownCwd : process.cwd(), parsed.out);
495
+ try {
496
+ const output = parsed.json ? text : `\`\`\`text\n${text}\n\`\`\``;
497
+ await writeFile(path, `${output}\n`, "utf8");
498
+ } catch (error) {
499
+ return {
500
+ kind: "error",
501
+ text: `Could not write history report to ${path}: ${error instanceof Error ? error.message : String(error)}`
502
+ };
503
+ }
504
+ }
478
505
  return {
479
506
  kind: "success",
480
- text: renderHistory(report)
507
+ text
481
508
  };
482
509
  }
483
510
  //#endregion
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-session-eval",
3
3
  "description": "Retrospective agent-session evaluation for DeepSeek Harness: deterministic grade cards from persisted session logs, cross-session regression diffs, era comparison — no benchmark runs, no LLM judges.",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },