dsh-task-ledger 0.0.125 → 0.0.127

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.
Files changed (51) hide show
  1. package/dist/client.js +21192 -11429
  2. package/dist/client.js.map +1 -1
  3. package/dist/host/errors.d.ts +9 -3
  4. package/dist/host/errors.js +10 -0
  5. package/dist/host/repositories/cache.d.ts +2 -2
  6. package/dist/host/repositories/dirs.repository.d.ts +37 -0
  7. package/dist/host/repositories/dirs.repository.js +97 -0
  8. package/dist/host/repositories/history.repository.d.ts +20 -0
  9. package/dist/host/repositories/history.repository.js +63 -0
  10. package/dist/host/repositories/identity.repository.d.ts +53 -3
  11. package/dist/host/repositories/identity.repository.js +105 -10
  12. package/dist/host/repositories/ledger.repository.d.ts +0 -8
  13. package/dist/host/repositories/ledger.repository.js +3 -20
  14. package/dist/host/repositories/runner.d.ts +29 -0
  15. package/dist/host/repositories/runner.js +66 -4
  16. package/dist/host/repositories/stats.repository.js +2 -2
  17. package/dist/host/repositories/task.repository.d.ts +12 -0
  18. package/dist/host/repositories/task.repository.js +63 -0
  19. package/dist/host/repositories/trend.repository.d.ts +15 -5
  20. package/dist/host/repositories/trend.repository.js +38 -11
  21. package/dist/host/routes.d.ts +13 -1
  22. package/dist/host/routes.js +379 -12
  23. package/dist/host/services/continue.service.d.ts +126 -0
  24. package/dist/host/services/continue.service.js +138 -0
  25. package/dist/host/services/dirs.service.d.ts +34 -0
  26. package/dist/host/services/dirs.service.js +66 -0
  27. package/dist/host/services/history.service.d.ts +14 -0
  28. package/dist/host/services/history.service.js +172 -0
  29. package/dist/host/services/identity.service.d.ts +53 -0
  30. package/dist/host/services/identity.service.js +89 -0
  31. package/dist/host/services/stats.service.d.ts +2 -2
  32. package/dist/host/services/stats.service.js +23 -6
  33. package/dist/host/services/task.service.d.ts +15 -0
  34. package/dist/host/services/task.service.js +15 -0
  35. package/dist/host/services/trend.service.d.ts +2 -2
  36. package/dist/host/services/trend.service.js +5 -5
  37. package/dist/host/services/workspaces.service.d.ts +10 -0
  38. package/dist/host/services/workspaces.service.js +28 -0
  39. package/dist/host/types.d.ts +108 -6
  40. package/dist/index.d.ts +2 -0
  41. package/dist/index.js +31 -3
  42. package/package.json +2 -2
  43. package/skill/SKILL.md +13 -13
  44. package/skill/package.json +1 -1
  45. package/skill/references/init-identity.md +1 -1
  46. package/skill/references/troubleshooting.md +1 -1
  47. package/skill/scripts/bin/ledger-darwin-arm64 +0 -0
  48. package/skill/scripts/bin/ledger-linux-amd64 +0 -0
  49. package/skill/scripts/bin/ledger-windows-amd64.exe +0 -0
  50. package/dist/host/workspace.d.ts +0 -20
  51. package/dist/host/workspace.js +0 -17
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 任务动作(界面行内按钮 → skill 写命令):认领、释放。
3
+ *
4
+ * 写完必须清读缓存 —— 下一屏快照要看到刚改过的归属,不能吃 1.5s 的旧值。
5
+ */
6
+ import { invalidate } from "../repositories/cache.js";
7
+ import { runTaskAction, } from "../repositories/task.repository.js";
8
+ /**
9
+ * @param onStep - 过程步骤回调:路由把它写成 NDJSON 一行一行推给界面(进度翻牌)。
10
+ * 命令跑完 / 失败前都会先收到前面那几个阶段,界面据此换文案。
11
+ */
12
+ export async function actOnTask(input, onStep) {
13
+ await runTaskAction(input.repo, input.id, input.action, input.summary ?? "", onStep);
14
+ invalidate();
15
+ }
@@ -1,5 +1,5 @@
1
1
  import { type TrendBucket } from "../repositories/trend.repository.js";
2
- import type { LedgerWorkspace, TrendPayload, TrendView } from "../types.js";
2
+ import type { BoardTrendGran, LedgerWorkspace, TrendPayload, TrendView } from "../types.js";
3
3
  /**
4
4
  * 跨工作区求和:同一天起止的周桶相加。
5
5
  * 工作区之间周界一致(同机同时区、都由 skill 的周一起算),所以直接按 `from` 归并。
@@ -10,4 +10,4 @@ export declare function mergeTrend(rows: readonly {
10
10
  coverageTo: string;
11
11
  }[]): TrendView;
12
12
  /** 趋势图数据:逐工作区读、坏的进 degraded,全挂给空桶(不抛)。 */
13
- export declare function getBoardTrend(workspaces: readonly LedgerWorkspace[]): Promise<TrendPayload>;
13
+ export declare function getBoardTrendGran(workspaces: readonly LedgerWorkspace[], gran: BoardTrendGran): Promise<TrendPayload>;
@@ -1,14 +1,14 @@
1
1
  /**
2
- * 首页趋势图的用例:跨工作区把「近 8 周」的桶**按周求和**。
2
+ * 首页趋势图的用例:跨工作区把「近 26 周」的桶**按周求和**。
3
3
  *
4
- * 数字真源全在 skill(每工作区 `stats --trend week --last 8w`),本文件只做两件事:
4
+ * 数字真源全在 skill(每工作区 `stats --trend week --last 26w`),本文件只做两件事:
5
5
  * 按 `from` 把各工作区同周的数加在一起、把读挂的工作区归进 `degraded`。
6
6
  *
7
7
  * ⚠️ 这里**不抛**「全部失败」:趋势只是首页的一块图,不能因为它把 8 张卡和列表一起打成 500。
8
8
  * 全挂时返回空桶 + inherited degraded,界面显示空态(真源口径见 errors.ts 的 settleWorkspaces 注释)。
9
9
  */
10
10
  import { codeOf, messageOf } from "../errors.js";
11
- import { readTrend } from "../repositories/trend.repository.js";
11
+ import { readBoardTrend } from "../repositories/trend.repository.js";
12
12
  const byFrom = (a, b) => a.from < b.from ? -1 : a.from > b.from ? 1 : 0;
13
13
  /**
14
14
  * 跨工作区求和:同一天起止的周桶相加。
@@ -63,8 +63,8 @@ export function mergeTrend(rows) {
63
63
  };
64
64
  }
65
65
  /** 趋势图数据:逐工作区读、坏的进 degraded,全挂给空桶(不抛)。 */
66
- export async function getBoardTrend(workspaces) {
67
- const settled = await Promise.allSettled(workspaces.map((workspace) => readTrend(workspace)));
66
+ export async function getBoardTrendGran(workspaces, gran) {
67
+ const settled = await Promise.allSettled(workspaces.map((workspace) => readBoardTrend(workspace, gran)));
68
68
  const rows = [];
69
69
  const degraded = [];
70
70
  settled.forEach((result, i) => {
@@ -0,0 +1,10 @@
1
+ import type { LedgerWorkspace, RegistryWorkspace } from "../types.js";
2
+ /**
3
+ * 台账目录发现(用户 2026-11-01 改口径):**按机器级清单读**,不再按宿主工作区读。
4
+ *
5
+ * 宿主 registry 只剩一件事:替清单**补登记**(扫到的工作区里清单还没有的,加进去)。
6
+ * 补登记只在 `sync = true` 时做(用户 2026-11-03:进看板 + 手动刷新,一共两处;其余请求
7
+ * 一律只读清单 —— 每请求起一个进程只为对比工作区,纯浪费)。
8
+ * 隐藏 = 全局不看,过滤只在这里做一次 —— 下游(统计卡 / 趋势 / 列表 / 名册)全吃这个输出。
9
+ */
10
+ export declare function resolveWorkspaces(registryList: readonly RegistryWorkspace[], sync?: boolean): Promise<LedgerWorkspace[]>;
@@ -0,0 +1,28 @@
1
+ import { addDir, readDirs } from "../repositories/dirs.repository.js";
2
+ /**
3
+ * 台账目录发现(用户 2026-11-01 改口径):**按机器级清单读**,不再按宿主工作区读。
4
+ *
5
+ * 宿主 registry 只剩一件事:替清单**补登记**(扫到的工作区里清单还没有的,加进去)。
6
+ * 补登记只在 `sync = true` 时做(用户 2026-11-03:进看板 + 手动刷新,一共两处;其余请求
7
+ * 一律只读清单 —— 每请求起一个进程只为对比工作区,纯浪费)。
8
+ * 隐藏 = 全局不看,过滤只在这里做一次 —— 下游(统计卡 / 趋势 / 列表 / 名册)全吃这个输出。
9
+ */
10
+ export async function resolveWorkspaces(registryList, sync = false) {
11
+ let dirs = await readDirs();
12
+ if (sync) {
13
+ const known = new Set(dirs.map((dir) => dir.path));
14
+ for (const workspace of registryList) {
15
+ if (known.has(workspace.path))
16
+ continue;
17
+ // 串行:读改写同一个文件,并发会互相覆盖(skill 侧虽有锁,最后写入者仍可能少几条)
18
+ const added = await addDir(workspace.path, workspace.title);
19
+ known.add(workspace.path);
20
+ if (added.added)
21
+ dirs = added.dirs;
22
+ }
23
+ }
24
+ return dirs.filter((dir) => !dir.hidden).map(toWorkspace);
25
+ }
26
+ function toWorkspace(dir) {
27
+ return { id: dir.path, title: dir.name, path: dir.path };
28
+ }
@@ -5,15 +5,30 @@
5
5
  * 比绑死官方类型更稳)。
6
6
  */
7
7
  import type { DegradedInfo } from "./errors.js";
8
- /** 一个活跃台账工作区:宿主 registry 条目 + 本地探测(含 `ledger/`)。 */
8
+ /** 一个可看的台账:机器级清单里的一条(已去掉隐藏)。 */
9
9
  export interface LedgerWorkspace {
10
- /** 宿主工作区 id。 */
10
+ /** 台账目录绝对路径(清单条目没有别的唯一标识,路径本身就唯一;历史筛选 / 缓存键都用它)。 */
11
11
  readonly id: string;
12
- /** 宿主 title(允许重名)。 */
12
+ /** 显示名:清单里的 name,没写就是目录名(默认名由 skill 算好,插件不重算)。 */
13
13
  readonly title: string;
14
- /** canonical 绝对路径(宿主已 realpath 归一;skill 脚本 --repo 直传它)。 */
14
+ /** 台账目录绝对路径(skill 脚本 --repo 直传它)。 */
15
15
  readonly path: string;
16
16
  }
17
+ /**
18
+ * 宿主 `workspaceRegistry.list()` 条目的最小面(结构声明;事实与坐标见
19
+ * external/dsh-workspace-registry.md)。它现在只承担「替台账清单补登记」一件事。
20
+ */
21
+ export interface RegistryWorkspace {
22
+ readonly id: string;
23
+ readonly title: string;
24
+ readonly path: string;
25
+ /**
26
+ * 把会话登记进本工作区的 `sessionIds`(会话归组的唯一途径)。
27
+ * ⚠️ 只设 `meta.cwd` **不会**自动归组(reference: dsh-task-dispatch-table 决策 22 真机结论);
28
+ * 宿主旧版本可能没有该面 ⇒ optional,调用方按缺失降级并如实回报。
29
+ */
30
+ readonly attachSession?: (sessionId: string) => Promise<void>;
31
+ }
17
32
  /** 任务对象的原始字段(skill 脚本 list --json 的值形状;缺字段按可选处理)。 */
18
33
  export interface TaskRecord {
19
34
  title?: string;
@@ -117,7 +132,9 @@ export interface StatsPayload {
117
132
  readonly stats: BoardStatsView;
118
133
  readonly degraded: readonly DegradedInfo[];
119
134
  }
120
- /** 一个人在一个周桶里的三个数(新增按 creator、完成 / 未结按 owner,都是 skill 的口径)。 */
135
+ /** 首页趋势图的周期粒度(日 / 周;历史页那档 month 这里不要,见 HistoryGran)。 */
136
+ export type BoardTrendGran = "day" | "week";
137
+ /** 一个人在一个周期桶里的三个数(新增按 creator、完成 / 未结按 owner,都是 skill 的口径)。 */
121
138
  export interface TrendOwnerCounts {
122
139
  readonly added: number;
123
140
  readonly done: number;
@@ -145,6 +162,89 @@ export interface TrendView {
145
162
  export interface TrendPayload extends TrendView {
146
163
  readonly degraded: readonly DegradedInfo[];
147
164
  }
165
+ /** 历史趋势的周期粒度(一根柱 = 一个周期;桶的切分与口径全在 skill)。 */
166
+ export type HistoryGran = "day" | "week" | "month";
167
+ /** 历史趋势里某工作区一个周期内的结案数。 */
168
+ export interface HistoryWorkspaceCell {
169
+ readonly done: number;
170
+ readonly cancelled: number;
171
+ }
172
+ /**
173
+ * 历史页顶部的一个周期桶:**数字真源是 skill `stats --trend <gran>`**,
174
+ * host 只做跨工作区求和(用户 2026-10-30 定:不自己写聚合算法)。
175
+ */
176
+ export interface HistoryBucket {
177
+ /** 周期起始日(日 = 当天、周 = 周一、月 = 1 号;skill 口径)。 */
178
+ readonly key: string;
179
+ /** 周期结束日(含边界;skill 口径,浮卡的区间标题用)。 */
180
+ readonly to: string;
181
+ readonly done: number;
182
+ readonly cancelled: number;
183
+ /** 该周期新建(含在办 / 待认领)。 */
184
+ readonly added: number;
185
+ /** 工作区 id → 该区该周期的 done / cancelled(分色用)。 */
186
+ readonly byWorkspace: Readonly<Record<string, HistoryWorkspaceCell>>;
187
+ }
188
+ /** 一个周期的结案拆分(合计 = done + cancelled)—— 小字要分别显示「完成 / 取消」。 */
189
+ export interface PeriodCount {
190
+ readonly done: number;
191
+ readonly cancelled: number;
192
+ }
193
+ /** 历史页顶部数显(host 从 day 桶求和:今日 / 昨日 / 本周 / 上周 / 本月 / 上月 / 本季度 / 上季度)。 */
194
+ export interface HistoryTotals {
195
+ readonly today: PeriodCount;
196
+ readonly yesterday: PeriodCount;
197
+ readonly week: PeriodCount;
198
+ readonly lastWeek: PeriodCount;
199
+ readonly month: PeriodCount;
200
+ readonly lastMonth: PeriodCount;
201
+ readonly quarter: PeriodCount;
202
+ readonly lastQuarter: PeriodCount;
203
+ }
204
+ /** 历史页顶部数据:当前粒度的桶序列 + 4 数显。
205
+ * 桶只覆盖最近 365 天(skill 的 day 粒度上限);更早的窗口由前端按 key 补空槽。 */
206
+ export interface HistoryTrendView {
207
+ readonly workspaces: readonly {
208
+ readonly id: string;
209
+ readonly title: string;
210
+ }[];
211
+ /** 桶序列(升序;空桶 = 该周期无结案)。 */
212
+ readonly buckets: readonly HistoryBucket[];
213
+ readonly totals: HistoryTotals;
214
+ }
215
+ /** 历史日趋势 + 降级。 */
216
+ export interface HistoryTrendPayload extends HistoryTrendView {
217
+ readonly degraded: readonly DegradedInfo[];
218
+ }
219
+ /** 一条已结案记录(skill `history --json` 的 records 项 + 工作区身份)。 */
220
+ export interface HistoryRecord {
221
+ /** 唯一键 `${src}:${line}`:翻页时跨页重复的那几条靠它去重。 */
222
+ readonly key: string;
223
+ readonly workspaceId: string;
224
+ readonly workspaceTitle: string;
225
+ readonly title: string;
226
+ readonly summary: string;
227
+ readonly category: string;
228
+ readonly tags: readonly string[];
229
+ /** done / cancelled(skill 口径)。 */
230
+ readonly result: string;
231
+ readonly owner: string;
232
+ readonly ownerName: string;
233
+ readonly creator: string;
234
+ readonly creatorName: string;
235
+ readonly created: string;
236
+ readonly finished: string;
237
+ /** 过程文档绝对路径;空串 = 这行没有 worklog,不可点开。 */
238
+ readonly filePath: string;
239
+ }
240
+ /** `/history/list` 响应:选中那个月的结案记录(按结案时间正序)。
241
+ * 分页不在这里做 —— 整月一次下发,前端按行号做虚拟滚动。 */
242
+ export interface HistoryListPayload {
243
+ readonly records: readonly HistoryRecord[];
244
+ /** 窗口内记录超过读取上限(一年 5000 条):此时列表底部不再追加。 */
245
+ readonly capped: boolean;
246
+ readonly degraded: readonly DegradedInfo[];
247
+ }
148
248
  /** 一个工作区的花名册(skill `owners --json`:含当前没任务的人)。 */
149
249
  export interface WorkspaceOwners {
150
250
  readonly workspaceId: string;
@@ -161,8 +261,10 @@ export interface WorkspaceOwners {
161
261
  * 前端只筛不判。
162
262
  */
163
263
  export interface BoardHomePayload extends StatsPayload {
164
- /** 首页右侧趋势图(近 8 周)。 */
264
+ /** 首页右侧趋势图(近 26 周)。 */
165
265
  readonly trend: TrendView;
266
+ /** 同一段时间的**日档**桶(近 26 周的天数):两份一起下发 ⇒ 切「日 / 周」零请求。 */
267
+ readonly trendDay: TrendView;
166
268
  readonly workspaces: readonly LedgerWorkspace[];
167
269
  readonly tasks: readonly TaskRow[];
168
270
  readonly identities: Readonly<Record<string, string | null>>;
package/dist/index.d.ts CHANGED
@@ -4,6 +4,8 @@ interface PluginContext {
4
4
  inject(deps: readonly string[], callback: (ctx: Record<string, unknown>) => void): void;
5
5
  /** 组合内副作用:进入时 setup、离开时执行返回的清理函数(cordis 核心面)。 */
6
6
  effect(setup: () => (() => void) | undefined): void;
7
+ /** 按名取服务(取不到给 undefined)。**读属性**在未 inject 的模块上会直接 throw ⇒ 见 serviceOf。 */
8
+ get(name: string): unknown;
7
9
  }
8
10
  export declare function apply(ctx: PluginContext): void;
9
11
  export {};
package/dist/index.js CHANGED
@@ -9,9 +9,27 @@
9
9
  * 满足不了的依赖会令 entry 一直 pending、卡死整个 dsh 启动);两条注入线
10
10
  * 互相独立——skills 缺席不挡数据面,反之亦然。
11
11
  */
12
- import { registerRoutes, } from "./host/routes.js";
12
+ import { serveFsRoute } from "dsh-plugin-core/host";
13
+ import { FS_ROUTE, registerRoutes } from "./host/routes.js";
13
14
  import { buildSkillRegistration, } from "./host/skill/registration.js";
14
15
  export const name = "dsh-task-ledger";
16
+ /**
17
+ * 「继续」用的宿主服务读取器:**现取、不预先注入**。
18
+ *
19
+ * 为什么不写进 `ctx.inject([...])` 的依赖表:cordis 里任一依赖缺席,整个回调永远 pending,
20
+ * 数据面会跟着一起消失(参考插件真机教训)。这些服务缺席只该让「继续」这一个动作报人话。
21
+ * 另外 ctx 的命名空间属性是 getter,未 inject 时读取直接 throw ⇒ 这里归一成 undefined。
22
+ */
23
+ function serviceOf(ctx, name) {
24
+ return () => {
25
+ try {
26
+ return ctx.get(name);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ };
32
+ }
15
33
  export function apply(ctx) {
16
34
  // ── 数据面(任务 3) ──
17
35
  ctx.inject(["workspaceRegistry", "webServer"], (services) => {
@@ -21,9 +39,19 @@ export function apply(ctx) {
21
39
  console.warn("[task-ledger] workspaceRegistry / webServer 未就位,数据面路由不注册");
22
40
  return;
23
41
  }
24
- registerRoutes(webServer, { registry });
25
- console.info("[task-ledger] 数据面路由已注册(读 /api/task-ledger/workspaces、/board/snapshot)");
42
+ registerRoutes(webServer, {
43
+ registry,
44
+ // 「继续」要起会话:agents / agentPresets / agentDefaultModel 三者按需现取
45
+ agents: serviceOf(ctx, "agents"),
46
+ presets: serviceOf(ctx, "agentPresets"),
47
+ defaultModel: serviceOf(ctx, "agentDefaultModel"),
48
+ sessionTitle: serviceOf(ctx, "sessionTitle"),
49
+ });
50
+ console.info("[task-ledger] 数据面路由已注册(读 /api/task-ledger/workspaces、/board/snapshot;写 /task/action、/task/continue)");
26
51
  });
52
+ // ── 只读文件路由(核心 explorer 的数据面:选目录弹窗 + 文件预览都吃它) ──
53
+ // 官方 workspaceFiles 只能在各自工作区内列目录,选台账目录要能走全盘 ⇒ 用核心这条。
54
+ serveFsRoute(ctx, { path: FS_ROUTE });
27
55
  // ── skill 全局注册(任务 2) ──
28
56
  // unscoped 注册落全局层,模型可 load;disposer 随插件 dispose 自动注销。
29
57
  ctx.inject(["skills"], (services) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-task-ledger",
3
- "version": "0.0.125",
3
+ "version": "0.0.127",
4
4
  "description": "Task ledger board for dsh (DeepSeek Harness): read-only board over task-ledger skill workspaces. The task-ledger skill is bundled — install the plugin, get the skill globally.",
5
5
  "keywords": [
6
6
  "deepseek",
@@ -57,6 +57,6 @@
57
57
  }
58
58
  },
59
59
  "dependencies": {
60
- "dsh-plugin-core": "^0.10.0"
60
+ "dsh-plugin-core": "^0.26.0"
61
61
  }
62
62
  }
package/skill/SKILL.md CHANGED
@@ -278,7 +278,7 @@ allowed-tools: Read,Edit,Bash,Grep
278
278
 
279
279
  干什么:本机身份的只读查询,**不依赖 `--repo`、不写任何文件**,回答「这台机器上我是谁」。需要自己的 uuid 时用它(转述给别人、核对 `list --owner` 的查询结果),别去解析 `~/.taskledger/identity.json`。
280
280
 
281
- **它不是执行前的检查项**:跑命令前不用先跑它探一次 —— 未初始化会在你真正要跑的那条命令上当场报「这台机器还没有初始化身份。」,那时再按「身份闸门」那条分支走。
281
+ **它不是执行前的检查项**:跑命令前不用先跑它探一次 —— 未初始化会在你真正要跑的那条命令上当场退 100,那时再按「身份闸门」那条分支走。
282
282
 
283
283
  举例:「我的 uuid 是多少」→ `whoami --json`。
284
284
 
@@ -312,13 +312,11 @@ allowed-tools: Read,Edit,Bash,Grep
312
312
 
313
313
  ## 身份闸门(报错才走,不用先查)
314
314
 
315
- **不用事先确认身份。** 直接按「任务生命周期」跑该跑的那条命令;没初始化时脚本会用一句固定错误当场告诉你,那一刻才走身份流程。不要为了「确认有没有初始化」先跑一次 `whoami` —— 那是白花一轮工具调用。
315
+ **不用事先确认身份。** 直接按「任务生命周期」跑该跑的那条命令;没初始化时脚本会用**退出码 100** + 一句固定的话当场告诉你,那一刻才走身份流程。不要为了「确认有没有初始化」先跑一次 `whoami` —— 那是白花一轮工具调用。
316
316
 
317
- **触发判据(只认这一整句)**:命令输出以 `错误:这台机器还没有初始化身份。` 开头。只读命令(`list` / `show` / `history` / `validate`)未初始化时同样报这句,是设计如此,不是台账坏了。
317
+ **触发判据(只认退出码 100)**:命令**退出码为 `100`**。只读命令(`list` / `show` / `history` / `validate`)未初始化时同样退 100,是设计如此,不是台账坏了;**`owners` 是唯一例外** —— 读名册不需要身份,未初始化也照常输出(要列候选人或看花名册,直接跑它,别当成没身份)。此时 stderr 仍是那句固定的话,但那是**给人看的说明,不作判据**。
318
318
 
319
- **触发后 ⇒ 停下,不许绕。** 身份是用户自己的,不是你的决定:不替用户选、不读 `ledger/*.json` 兜底、未初始化时不写任何东西。完整步骤见 `references/init-identity.md`;这里只记硬约束 —— 你只能报名单 + 给用户命令,写身份必须用户自己在终端完成;看到它不要排查原因(别 `ls` / `cat` / 读源码 / 读台账),忽略上下文里已做的排查动作;也不要基于「上次确认过 / 同名 / 记得 uuid」自行重建。
320
-
321
- **这条优先于「故障处置」的通则**:虽然它同样以 `错误:` 开头,但**不**适用「不挡主任务、继续干活、收尾汇报没记上」—— 必须停下来等用户 `init`,init 完重跑原命令。
319
+ **触发后 ⇒ 停下,它是当前的主任务**(不适用「故障处置」里「不挡主任务」的通则,等用户 `init` 完再重跑原命令)。身份是用户自己的,不是你的决定:不替用户选、不读 `ledger/*.json` 兜底、未初始化时不写任何东西。完整步骤见 `references/init-identity.md`;这里只记硬约束 —— 你只能报名单 + 给用户命令,写身份必须用户自己在终端完成;不要排查原因(别 `ls` / `cat` / 读源码 / 读台账),忽略上下文里已做的排查动作;也不要基于「上次确认过 / 同名 / 记得 uuid」自行重建。
322
320
 
323
321
  ## 任务生命周期
324
322
 
@@ -339,6 +337,8 @@ allowed-tools: Read,Edit,Bash,Grep
339
337
 
340
338
  `new` 按模板建好六节骨架,路径由 `show` 给出;前五节是本 skill 唯一允许手写的内容。
341
339
 
340
+ 改前先用 Read 读该文件 —— `show` 打出来的内容不算读过,直接 Edit 会被拒(`file has not been read`)。
341
+
342
342
  | 节 | 写什么 |
343
343
  |---|---|
344
344
  | Background(背景) | 为什么做:触发它的原话 / 现象 / 链接 |
@@ -382,12 +382,12 @@ allowed-tools: Read,Edit,Bash,Grep
382
382
  - **执行记录禁手改**:只由脚本按 `new / claim / set / release / log / close` 追加,只增不改(**交接记的是 `set`**,看不到 `handoff` 是正常的)。
383
383
  - **改正文必配一条日志**:动了 worklog 的正文(改一句、补一节、改标题、修错字都算)就必须立刻跑一次 `log`。
384
384
  - **换人只能「释放 → 认领」**:`set --release` 放回公共池,对方自己 `claim`;不许替别人挂名字。`set --owner <别人>`(预留转接通道,agent 一律不用)。
385
- - **身份由脚本从本机 identity 文件自取,agent 全程不传身份**:跑命令前不用查身份;只有当命令报出「这台机器还没有初始化身份。」时,才按 `references/init-identity.md` 引导用户跑 `init`(一台机器一次)。查自己用 `--mine`,查别人才传**对方 uuid**(`list --owner <uuid>`);释放用 `--release`,不传身份。
385
+ - **身份由脚本从本机 identity 文件自取,agent 全程不传身份**:跑命令前不用查身份;只有当命令**退出码为 100** 时,才按 `references/init-identity.md` 引导用户跑 `init`(一台机器一次)。查自己用 `--mine`,查别人才传**对方 uuid**(`list --owner <uuid>`);释放用 `--release`,不传身份。
386
386
  - **身份只用 uuid,名字只用于显示与筛选**:认领 / 释放 / 交接 / 查「我的」一律只比 uuid;`--owner` 传名字会当场报错,要按人模糊找就用 `--owner-name <片段>`(子串、不分大小写)。名字是冗余字段(改名不影响归属),别拿它当身份用。
387
387
 
388
388
  ### 人类授权(跑之前问不问)
389
389
 
390
- 项目没有 `ledger/` ⇒ 先问「要不要建个台账记这活?」,不许自建。用户同意 ⇒ 直接跑第一条写命令,且**必须带 `--repo <工作区根>`**(根是直接含有 `ledger/` 的那个目录,`ledger/` 本身不是根、指到它会被拒绝):不带时脚本只从当前目录逐级往上找第一个含 `ledger/` 的目录,一个都没有就退 1 报「找不到台账」,绝不就地建;台账由这条写命令顺带建出来,身份**不用事先确认** —— 没 `init` 过,这条命令会当场报「这台机器还没有初始化身份。」,按「身份闸门」走一遍就好(一台机器一次,写本机身份,与台账无关)。之后可以不带 `--repo`,但查找是**逐级往上**的 —— 在本项目的子目录里跑可能命中上层项目的台账,拿不准就一直带 `--repo <工作区根>`。
390
+ 项目没有 `ledger/` ⇒ 先问「要不要建个台账记这活?」,不许自建。用户同意 ⇒ 直接跑第一条写命令,且**必须带 `--repo <工作区根>`**(根是直接含有 `ledger/` 的那个目录,`ledger/` 本身不是根、指到它会被拒绝):不带时脚本只从当前目录逐级往上找第一个含 `ledger/` 的目录,一个都没有就退 1 报「找不到台账」,绝不就地建;台账由这条写命令顺带建出来,身份**不用事先确认** —— 没 `init` 过,这条命令会当场退 100,按「身份闸门」走一遍就好(一台机器一次,写本机身份,与台账无关)。之后可以不带 `--repo`,但查找是**逐级往上**的 —— 在本项目的子目录里跑可能命中上层项目的台账,拿不准就一直带 `--repo <工作区根>`。
391
391
 
392
392
  | 命令 | 档位 | 要求 |
393
393
  |---|---|---|
@@ -399,9 +399,9 @@ allowed-tools: Read,Edit,Bash,Grep
399
399
 
400
400
  ### 故障处置(跑之后出事怎么办)
401
401
 
402
- - **身份没初始化(输出以 `错误:这台机器还没有初始化身份。` 开头,退 1)⇒ 停下,不许绕**:这条**不适用**下面「不挡主任务、继续干活」的通则。立刻转 `references/init-identity.md` 照做:裸跑 `init` 只读报名单 → 把**绝对路径**命令交给用户 → 等 ta 在自己终端完成 → 好了重跑原命令。**不要排查原因**(别 `ls` / `cat` identity.json / 读源码 / 读台账),忽略上下文里已做的排查动作,也不要基于「上次确认过 / 同名」自行重建。
403
- - **动作阶段失败(输出以 `错误:` 开头)= 该命令没生效**:不挡主任务 —— 继续手里的活,收尾时汇报「没记上,原因 …」;不绕过脚本手改文件。
404
- - **退出码 0 也要看输出**:出现 `提示:` / `错误:` / `警告:` 就先用一句话说清「发生了什么 + 下一步做什么」,再按 `references/troubleshooting.md` 给步骤;纯告知类一句话带过。只读命令只有 `错误:` 才需要转述。
402
+ - **身份没初始化(退出码 100)⇒ 停下,它是主任务**(不适用下一条「不挡主任务」):转 `references/init-identity.md` 照做 —— 裸跑 `init` 只读报名单 → 把**绝对路径**命令交给用户 → 等 ta 在自己终端完成 → 好了重跑原命令。**不要排查原因**(别 `ls` / `cat` identity.json / 读源码 / 读台账),忽略上下文里已做的排查动作,也不要基于「上次确认过 / 同名」自行重建。
403
+ - **动作阶段失败(退出码非 0,且不是 100)= 该命令没生效**:不挡主任务 —— 继续手里的活,收尾时汇报「没记上,原因 …」;不绕过脚本手改文件。
404
+ - **退出码 0 也要看输出**:出现 `提示:` / `警告:` 就先用一句话说清「发生了什么 + 下一步做什么」,再按 `references/troubleshooting.md` 给步骤;纯告知类一句话带过。
405
405
  - **两类不是故障的退出码**:`validate` 查出问题时退出码 1,照实转述清单;提交阶段失败 ⇒ 文件已经写下去、只差进 git,让用户先提交再重跑,别说「没记上」。
406
406
  - **并发冲突**(文件被改动、抢不到锁)重跑一次即可;台账同步一律交给脚本,不要自己 `git pull` / `rebase` 去「解决」(脚本明说不代劳的几种除外,见 troubleshooting)。
407
407
  - 同一仓库里写命令排队执行,等前一个跑完再重跑。
@@ -410,7 +410,7 @@ allowed-tools: Read,Edit,Bash,Grep
410
410
 
411
411
  | 何时读 | 文件 |
412
412
  |---|---|
413
- | 任何非 0 退出、退出码 0 但有 `提示:` / `错误:` / `警告:`、并发冲突、`validate` 报错 | `references/troubleshooting.md` |
413
+ | 任何非 0 退出、退出码 0 但有 `提示:` / `警告:`、并发冲突、`validate` 报错 | `references/troubleshooting.md` |
414
414
  | 传 `--created` / `--finished` 补录时间、看 `--json` 里的时间字段、查别人时 owner 匹配异常、看 history 列义 / 时间窗口口径 | `references/state-model.md` |
415
415
  | 拿不准一条记录够不够格、`--note` 不会写 | `references/log-note.md` |
416
- | 命令返回「这台机器还没有初始化身份。」(退 1,输出以 `错误:` 开头) | `references/init-identity.md` |
416
+ | 命令退出码 100(这台机器还没有身份) | `references/init-identity.md` |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-ledger",
3
- "version": "1.3.46",
3
+ "version": "1.3.48",
4
4
  "description": "Task-ledger skill for coding agents: per-workspace progress tracking (ledger/pool.json + owners/<hash>.json shards + owners.json index, history, worklog) driven by a CLI script. Standalone install for CodeBuddy and other agents; also bundled inside the dsh-task-ledger DSH plugin.",
5
5
  "keywords": [
6
6
  "skill",
@@ -1,6 +1,6 @@
1
1
  # 未初始化身份时怎么走
2
2
 
3
- 触发:任何命令返回「这台机器还没有初始化身份」。身份是用户自己的,**不是你的决定**。
3
+ 触发:任何命令**退出码为 100**。身份是用户自己的,**不是你的决定**。
4
4
 
5
5
  **触发后立刻照做,不要探索。** 除「裸跑 init 只读报名单」外,不要 `ls` / 扫描仓库、不要 `cat identity.json`、不要读任何目录或文件,不要猜身份。
6
6
 
@@ -88,7 +88,7 @@
88
88
 
89
89
  ### 只提示、不拦(退出码 0)
90
90
 
91
- - **(「这台机器还没有初始化身份。」不在此列** —— 它是退 1 的硬拦,见「身份闸门」。)
91
+ - **(「这台机器还没有初始化身份。」不在此列** —— 它是**退出码 100** 的硬拦,见「身份闸门」。)
92
92
  - **「提示:格式版本是 … 当前是 …;不拦,但字段可能有出入」**:数据是旧版本写的,功能照常;想统一就按提示升版本。
93
93
  - **「提示:写前把本地快进到 …(N 个提交,仅 fast-forward)」**:纯告知。
94
94
  - **「提示:写前快进到 … 没成功(工作树有未提交改动),改为只刷新本次要写的台账文件」**:没碍事,照写。
@@ -1,20 +0,0 @@
1
- import type { LedgerWorkspace } from "./types.js";
2
- /** 宿主 workspaceRegistry.list() 条目的最小面(结构声明;事实与坐标见 external/dsh-workspace-registry.md)。 */
3
- export interface RegistryWorkspace {
4
- readonly id: string;
5
- readonly title: string;
6
- readonly path: string;
7
- /**
8
- * 把会话登记进本工作区的 `sessionIds`(会话归组的唯一途径)。
9
- * ⚠️ 只设 `meta.cwd` **不会**自动归组(reference: dsh-task-dispatch-table 决策 22 真机结论);
10
- * 宿主旧版本可能没有该面 ⇒ optional,调用方按缺失降级并如实回报。
11
- */
12
- readonly attachSession?: (sessionId: string) => Promise<void>;
13
- }
14
- /**
15
- * 工作区发现:宿主 registry 全量 → 逐个问 skill `root`(这个目录是不是台账根),
16
- * 只留是的。没有 `ledger/` 的目录后端不可见(2026-10-17 拍板)。
17
- *
18
- * 探测走 repository 的 hasLedger()——本文件不碰 node:fs(IO 唯一出口纪律)。
19
- */
20
- export declare function resolveWorkspaces(registryList: readonly RegistryWorkspace[]): Promise<LedgerWorkspace[]>;
@@ -1,17 +0,0 @@
1
- import { hasLedger } from "./repositories/ledger.repository.js";
2
- /**
3
- * 工作区发现:宿主 registry 全量 → 逐个问 skill `root`(这个目录是不是台账根),
4
- * 只留是的。没有 `ledger/` 的目录后端不可见(2026-10-17 拍板)。
5
- *
6
- * 探测走 repository 的 hasLedger()——本文件不碰 node:fs(IO 唯一出口纪律)。
7
- */
8
- export async function resolveWorkspaces(registryList) {
9
- const flags = await Promise.all(registryList.map((workspace) => hasLedger(workspace.path)));
10
- return registryList
11
- .filter((_, index) => flags[index])
12
- .map((workspace) => ({
13
- id: workspace.id,
14
- title: workspace.title,
15
- path: workspace.path,
16
- }));
17
- }