dsh-receipts
Mine local DSH session logs (JSONL) into personal usage & impact receipts: Markdown day/week/month reports plus a self-contained HTML receipt
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 24, 2026
- Updated
- Aug 24, 2026
Introduction
dsh-receipts
[!IMPORTANT] 依赖前置:相邻
dsh-src检出(link:依赖) 本项目在开发形态下使用link:依赖指向相邻的 DeepSeek Harness 源码检出(dsh-src), 与当前仓库保持同一父目录布局(<parent>/dsh-src)。克隆本仓库后:
- 先把官方
deepseek-ai/deepseek-harness检出到与本仓库同级的dsh-src/目录,并执行其pnpm install && pnpm run build;- 再按下方「安装」一节执行本仓库的
pnpm install --offline && pnpm build与测试。 发布到 npm 的版本会尽量把link:依赖替换为 registry 真实版本;无法替换的内部包保持link:,见各包 README 说明。
从 DSH 本地会话日志挖取「使用与影响报告」(Receipts)——个人「工作流水账」。
纯本地数据构建:挖取本机 DSH 会话日志(JSONL),交叉本机 git log,输出
Markdown 日报/周报/月报 + 自包含 HTML receipt(单文件、无外链,可直接
离线打开或发给别人)。
项目定位(对齐上游 receipts 思路,实现自写,MIT): 向主管 justify 使用/花费、自省「我到底用它干了啥」。 2026-08 调研:
dsh receipts worklog 工作日志实时搜索 0 结果,无人做。
特性
- 零配置:数据源是本机
$DSH_HOME/sessions下已有的会话日志(session.jsonl/session.jsonl.zstd),无需联网、无需注册。 - 零网络挖掘:挖掘阶段是纯本地 Node 脚本(文件 IO + 正则 + JSON 解析), 零 API 调用;唯一一次模型调用(可选)是「摘要 → 书面报告」润色,摘要强制 ≤ 20KB(超出先截断再写)。
- 交叉验证:每个仓库本地
git log(无网络),把 commit 活动与会话活动互相校验; 仓库缺失时报告标注「无法交叉验证」。 - 隐私:只读自己的 dshHome 日志;
--redact开关对报告中的绝对路径脱敏; 报告绝不外发。
目录结构
dsh-receipts/
├── cordis.yml # DSH bundle patch:挂载 receipts 插件行
├── package.json # dsh.bundle.patch 声明 + 构建/测试脚本
├── src/index.ts # 装配(tools/fsp 注入 + systemPrompt 指引 + 注册工具)
├── src/tools/receipts.ts # defineTool:周期/项目过滤/输出路径/脱敏
├── scripts/mine-transcripts.mjs # 日志 → 摘要 JSON(零网络;none + zstd 两种模式)
├── scripts/report.mjs # 摘要 JSON → Markdown + 自包含 HTML(模板内嵌)
├── assets/template.html # 自包含 HTML 模板(无外部资源)
├── tests/
│ ├── fixtures/logs/ # 三份 fixture 会话日志(明文 + zstd)
│ └── smoke.e2e.ts # 离线冒烟/E2E 测试(14 项,覆盖全部验收标准)
├── README.md
└── LICENSE # MIT
安装与使用(DSH 插件)
构建并安装到 profile:
pnpm install # link 到 dsh-src 的依赖
pnpm build # tsc 编译 lib/
dsh plugin --profile <name> add /path/to/dsh-receipts
装载后 agent 获得 receipts 工具:
| 参数 | 默认 | 说明 |
|---|---|---|
period | month | week=7 / month=30 / quarter=90 / year=365 或正整数天数(如 "14") |
repo | 无 | 项目名子串,过滤整个报告(匹配会话 cwd) |
outDir | dshHome | 报告输出目录 |
redactPaths | false | 绝对路径脱敏(<redacted>/<basename>) |
sessionsRoot | $DSH_HOME/sessions | 覆盖会话日志根目录 |
git | true | git log 交叉验证开关(关闭时标注「无法交叉验证(被禁用)」) |
输出两个文件:receipts-<日期>.md 与 receipts-<日期>.html(自包含);
<日期> 为最近活动日/生成日的 UTC 日期,格式 YYYY-MM-DD(如 receipts-2026-08-20.md)。
命令行直接使用
# 挖掘(摘要 JSON 打到 stdout)
node scripts/mine-transcripts.mjs --root ~/.dsh/sessions --period month --repo my-project --redact
# 报告(摘要文件 → Markdown + HTML)
node scripts/report.mjs --summary /tmp/summary.json --out-dir ~/receipts --base-name receipts-2026-08-20
压缩模式(注意事项:DSH 日志默认 Zstd)
compression: 'none':日志为明文 UTF-8 JSONL,逐行解析,最省事。- 默认 Zstd:DSH 的
session-persistence-jsonl把日志写成多帧拼接的session.jsonl.zstd(每 append 批一帧)。本插件自动解压:- 优先 Node 内建
node:zlib的 Zstd——先按帧边界(RFC 8878 块头布局以 DSH 自身扫描器为准)切出每一帧,再逐帧解码拼接内容; - 内建不可用时回退
zstd -dcCLI; - 两者皆无时跳过该文件并在摘要中记录——
skipped清单(路径 + 原因)与totals.skippedSessions计数进入 Markdown/HTML 报告(概览「跳过文件数」+ 告警块),提示改用compression: 'none',不会产出「假空」报告。
- 优先 Node 内建
成本策略
- 挖掘阶段:零网络、零模型调用(文件 IO + 正则)。
- 报告阶段:模板渲染,同样零模型调用(比上游更省;需要叙事润色时,可用摘要 数据让模型做一次调用)。
- 摘要上限:
SUMMARY_BYTES_CAP = 20KB(紧凑 JSON 计字节),超出按 「会话文件线索 → 文件改动 → 按天 → 工具频次 → 主题 → 会话条目 → 项目条目」 的顺序逐步截断,绝不丢弃核心统计。
隐私
- 只读
dshHome/sessions(或显式sessionsRoot),只写outDir(默认 dshHome)。 - 报告默认展示绝对路径;
--redact/redactPaths: true折叠为<redacted>/<basename>。 - 「零外发」是硬约束:挖掘脚本无任何网络模块(测试含网络白名单断言)。
开发
pnpm test # 离线冒烟/E2E:14 项断言,覆盖验收标准 1-5
pnpm typecheck # tsc --noEmit(src + tests)
pnpm build # tsc → lib/
测试覆盖:
- fixture JSONL → 会话数/工具调用数与日志实际一致(抽查断言);
- week/month/数字周期过滤 + repo 子串过滤;
- HTML 单文件自包含(无外部资源、占位符替换、离线可开);
- 挖掘脚本网络白名单(源码不含网络模块/fetch,模板无外部 URL);
- git 交叉仅在有仓库时进行,缺失时报告标注「无法交叉验证」(含临时 git 仓库 验证 commit 数);另有多帧 zstd 拼接回归测试。
已知限制
- 周期判定以会话活动与时间窗的重叠为准(创建于窗外但窗内有活动的会话计入)。
- zstd 文件较大时逐帧解码会占用较多内存;单帧压缩数据超过 64MB 上限时按解码
失败处理并跳过该文件(
scripts/mine-transcripts.mjs的ZSTD_MAX_FRAME_BYTES帧大小护栏,跳过原因进入摘要与报告)。 receipts工具以子进程方式调用挖掘/报告脚本;脚本路径随包发布(必须保留scripts/与assets/两个目录)。- 主题分类为关键词聚类的确定性实现,非语义分析;可按需扩展
TOPIC_KEYWORDS。
License
MIT。上游 anthropics/claude-plugins-official#receipts 仅作流程与 参数语义参考,实现全部自写。