dsh-task-ledger 0.0.74 → 0.0.76

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 (90) hide show
  1. package/dist/client.js +128 -128
  2. package/dist/client.js.map +1 -1
  3. package/dist/host/board.d.ts +4 -4
  4. package/dist/host/board.js +6 -6
  5. package/dist/host/errors.d.ts +3 -3
  6. package/dist/host/errors.js +13 -7
  7. package/dist/host/repositories/cache.d.ts +1 -1
  8. package/dist/host/repositories/cache.js +4 -2
  9. package/dist/host/repositories/identity.repository.d.ts +1 -1
  10. package/dist/host/repositories/identity.repository.js +14 -7
  11. package/dist/host/repositories/ledger.repository.d.ts +1 -1
  12. package/dist/host/repositories/ledger.repository.js +34 -23
  13. package/dist/host/repositories/records.d.ts +1 -1
  14. package/dist/host/repositories/records.js +3 -3
  15. package/dist/host/repositories/runner.d.ts +5 -4
  16. package/dist/host/repositories/runner.js +41 -19
  17. package/dist/host/routes.d.ts +4 -4
  18. package/dist/host/routes.js +145 -96
  19. package/dist/host/services/dates.js +4 -4
  20. package/dist/host/services/history.service.d.ts +5 -2
  21. package/dist/host/services/history.service.js +24 -13
  22. package/dist/host/services/home.service.d.ts +8 -2
  23. package/dist/host/services/home.service.js +29 -14
  24. package/dist/host/services/ledger-write.service.d.ts +8 -3
  25. package/dist/host/services/ledger-write.service.js +14 -9
  26. package/dist/host/services/session.service.d.ts +6 -6
  27. package/dist/host/services/session.service.js +47 -37
  28. package/dist/host/services/trend.service.d.ts +9 -2
  29. package/dist/host/services/trend.service.js +42 -21
  30. package/dist/host/skill/registration.d.ts +1 -1
  31. package/dist/host/skill/registration.js +16 -11
  32. package/dist/host/workspace.d.ts +1 -1
  33. package/dist/host/workspace.js +6 -2
  34. package/dist/index.js +12 -12
  35. package/package.json +57 -58
  36. package/skill/README.md +29 -247
  37. package/skill/SKILL.md +173 -88
  38. package/skill/package.json +22 -22
  39. package/skill/references/log-note.md +3 -1
  40. package/skill/references/state-model.md +29 -27
  41. package/skill/references/troubleshooting.md +97 -32
  42. package/skill/scripts/ledgerkit/__init__.py +0 -0
  43. package/skill/scripts/ledgerkit/actions/__init__.py +0 -0
  44. package/skill/scripts/ledgerkit/actions/atom.py +154 -0
  45. package/skill/scripts/ledgerkit/actions/bootstrap.py +26 -0
  46. package/skill/scripts/ledgerkit/actions/claim.py +82 -0
  47. package/skill/scripts/ledgerkit/actions/close_task.py +180 -0
  48. package/skill/scripts/ledgerkit/actions/guard.py +136 -0
  49. package/skill/scripts/ledgerkit/actions/handoff.py +74 -0
  50. package/skill/scripts/ledgerkit/actions/list_tasks.py +53 -0
  51. package/skill/scripts/ledgerkit/actions/log_progress.py +60 -0
  52. package/skill/scripts/ledgerkit/actions/new_task.py +120 -0
  53. package/skill/scripts/ledgerkit/actions/query.py +33 -0
  54. package/skill/scripts/ledgerkit/actions/query_history.py +172 -0
  55. package/skill/scripts/ledgerkit/actions/registry.py +82 -0
  56. package/skill/scripts/ledgerkit/actions/release.py +51 -0
  57. package/skill/scripts/ledgerkit/actions/result.py +88 -0
  58. package/skill/scripts/ledgerkit/actions/set_task.py +43 -0
  59. package/skill/scripts/ledgerkit/actions/show_task.py +27 -0
  60. package/skill/scripts/ledgerkit/actions/validate.py +224 -0
  61. package/skill/scripts/ledgerkit/core/__init__.py +0 -0
  62. package/skill/scripts/ledgerkit/core/history_md.py +350 -0
  63. package/skill/scripts/ledgerkit/core/identity.py +27 -0
  64. package/skill/scripts/ledgerkit/core/ids.py +58 -0
  65. package/skill/scripts/ledgerkit/core/labels.py +79 -0
  66. package/skill/scripts/ledgerkit/core/paths.py +93 -0
  67. package/skill/scripts/ledgerkit/core/time.py +102 -0
  68. package/skill/scripts/ledgerkit/core/worklog_md.py +232 -0
  69. package/skill/scripts/ledgerkit/env.py +93 -0
  70. package/skill/scripts/ledgerkit/errors.py +15 -0
  71. package/skill/scripts/ledgerkit/notices.py +28 -0
  72. package/skill/scripts/ledgerkit/shell/__init__.py +0 -0
  73. package/skill/scripts/ledgerkit/shell/cli.py +259 -0
  74. package/skill/scripts/ledgerkit/shell/gitops.py +655 -0
  75. package/skill/scripts/ledgerkit/shell/merge.py +266 -0
  76. package/skill/scripts/ledgerkit/shell/presync.py +23 -0
  77. package/skill/scripts/ledgerkit/shell/render.py +165 -0
  78. package/skill/scripts/ledgerkit/shell/runner.py +225 -0
  79. package/skill/scripts/ledgerkit/store/__init__.py +0 -0
  80. package/skill/scripts/ledgerkit/store/atomic.py +130 -0
  81. package/skill/scripts/ledgerkit/store/dirty.py +108 -0
  82. package/skill/scripts/ledgerkit/store/history_store.py +189 -0
  83. package/skill/scripts/ledgerkit/store/json_store.py +166 -0
  84. package/skill/scripts/ledgerkit/store/locks.py +64 -0
  85. package/skill/scripts/ledgerkit/store/owners_index.py +114 -0
  86. package/skill/scripts/ledgerkit/store/task_store.py +28 -0
  87. package/skill/scripts/ledgerkit/store/worklog_store.py +168 -0
  88. package/skill/scripts/progress.py +8 -3109
  89. package/skill/references/taxonomy.md +0 -26
  90. package/skill/references/worklog.md +0 -39
package/dist/client.js CHANGED
@@ -6,6 +6,132 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ //#region src/client/rows.ts
10
+ /** 关键字命中(标题 / 任务号 / 人 / 分类 / 标签;已结案另含「完成了什么」)。 */
11
+ function hitKeyword(keyword, parts) {
12
+ const needle = keyword.trim().toLowerCase();
13
+ if (needle === "") return true;
14
+ return parts.some((part) => part?.toLowerCase().includes(needle) === true);
15
+ }
16
+ /** 「我的」判定:owner = 该工作区的当前 git 身份,且状态是进行中(blocked 是旧数据,照收;与 host 同口径)。 */
17
+ function isMine(task, board) {
18
+ if (board === null) return false;
19
+ const identity = board.identities[task.workspace.id] ?? null;
20
+ if (identity === null || identity === "") return false;
21
+ return task.owner === identity && (task.status === "doing" || task.status === "blocked");
22
+ }
23
+ function resolveActions(task, board) {
24
+ const mine = isMine(task, board);
25
+ return {
26
+ view: true,
27
+ claim: !mine && task.status === "open",
28
+ resume: mine,
29
+ finish: mine,
30
+ release: mine
31
+ };
32
+ }
33
+ /** 已结案行(统计 tab):只有查看,没有在办动作。 */
34
+ const CLOSED_ACTIONS = {
35
+ view: true,
36
+ claim: false,
37
+ resume: false,
38
+ finish: false,
39
+ release: false
40
+ };
41
+ /** 状态档命中(积压 / 逾期 / 停滞由 host 标记,前端不判阈值)——只作用于第二区。 */
42
+ function statusHit(task, status) {
43
+ if (status === "all") return true;
44
+ if (status === "active") return task.status === "doing" || task.status === "blocked";
45
+ if (status === "open") return task.status === "open";
46
+ if (status === "backlog") return task.backlog === true;
47
+ if (status === "overdue") return task.overdue === true;
48
+ return task.stale === true;
49
+ }
50
+ /** 组内排序:别人的进行中在前、待认领在后;各自按创建 / 开始时间倒序。 */
51
+ function inGroupOrder(a, b) {
52
+ if (a.kind !== "task" || b.kind !== "task") return 0;
53
+ const rank = (task) => task.status === "open" ? 1 : 0;
54
+ if (rank(a.task) !== rank(b.task)) return rank(a.task) - rank(b.task);
55
+ return String(b.task.created ?? b.task.started ?? "").localeCompare(String(a.task.created ?? a.task.started ?? ""));
56
+ }
57
+ /**
58
+ * 当前筛选下的两区视图(用户 2026-10-18 调整):
59
+ * - 第一区「我的待办」= 我名下 doing/blocked,吃工作区下拉 + 标题搜索,按创建时间倒序;
60
+ * - 第二区「全部待办」= **所有**在办任务按工作区分组(含我的——用户明确不要过滤掉自己),
61
+ * 吃**状态档**(segmented)+ **标题 / 执行人搜索**(othersKeyword);一个整表(外框连着)。
62
+ * ⇒ 我的任务会同时出现在两个区,这是预期(顶部快看 / 底部全量)。
63
+ */
64
+ function selectView(current) {
65
+ const { board, query } = current;
66
+ if (board === null) return {
67
+ mine: [],
68
+ groups: [],
69
+ total: 0,
70
+ allCounts: {
71
+ total: 0,
72
+ doing: 0,
73
+ open: 0,
74
+ overdue: 0,
75
+ stale: 0,
76
+ backlog: 0
77
+ }
78
+ };
79
+ const allTasks = board.tasks;
80
+ const allCounts = {
81
+ total: allTasks.length,
82
+ doing: allTasks.filter((task) => task.status === "doing").length,
83
+ open: allTasks.filter((task) => task.status === "open").length,
84
+ overdue: allTasks.filter((task) => task.overdue === true).length,
85
+ stale: allTasks.filter((task) => task.stale === true).length,
86
+ backlog: allTasks.filter((task) => task.backlog === true).length
87
+ };
88
+ const mineRows = board.tasks.filter((task) => isMine(task, board)).filter((task) => query.workspace === "" || task.workspace.id === query.workspace).filter((task) => hitKeyword(query.keyword, [
89
+ task.title,
90
+ task.id,
91
+ task.owner,
92
+ task.creator,
93
+ task.category,
94
+ ...task.tags ?? []
95
+ ])).sort((a, b) => String(b.created ?? b.started ?? "").localeCompare(String(a.created ?? a.started ?? ""))).map((task) => ({
96
+ kind: "task",
97
+ key: `${task.workspace.id}:${task.id}`,
98
+ task,
99
+ mine: true
100
+ }));
101
+ const rest = query.scope === "mine" ? [] : board.tasks.filter((task) => statusHit(task, query.status)).filter((task) => hitKeyword(query.othersKeyword, [task.title, task.owner]));
102
+ const byWorkspace = /* @__PURE__ */ new Map();
103
+ for (const task of rest) {
104
+ const entry = byWorkspace.get(task.workspace.id) ?? {
105
+ title: task.workspace.title,
106
+ rows: []
107
+ };
108
+ entry.rows.push({
109
+ kind: "task",
110
+ key: `${task.workspace.id}:${task.id}`,
111
+ task,
112
+ mine: isMine(task, board)
113
+ });
114
+ byWorkspace.set(task.workspace.id, entry);
115
+ }
116
+ const groups = [...byWorkspace.entries()].map(([workspaceId, entry]) => ({
117
+ workspaceId,
118
+ title: entry.title,
119
+ doingCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "doing").length,
120
+ overdueCount: entry.rows.filter((row) => row.kind === "task" && row.task.overdue === true).length,
121
+ staleCount: entry.rows.filter((row) => row.kind === "task" && row.task.stale === true).length,
122
+ backlogCount: entry.rows.filter((row) => row.kind === "task" && row.task.backlog === true).length,
123
+ openCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "open").length,
124
+ total: entry.rows.length,
125
+ rows: entry.rows.sort(inGroupOrder)
126
+ })).sort((a, b) => a.title.localeCompare(b.title, "zh-Hans-CN"));
127
+ return {
128
+ mine: mineRows,
129
+ groups,
130
+ total: groups.reduce((sum, group) => sum + group.rows.length, 0),
131
+ allCounts
132
+ };
133
+ }
134
+ //#endregion
9
135
  //#region src/client/api.ts
10
136
  /** host 路由前缀(全仓唯一;与 host/routes.ts 的 API_PREFIX 对应)。 */
11
137
  const API_PREFIX = "api/task-ledger";
@@ -170,132 +296,6 @@ window.__ModuleLoader__.load({
170
296
  } });
171
297
  }
172
298
  //#endregion
173
- //#region src/client/rows.ts
174
- /** 关键字命中(标题 / 任务号 / 人 / 分类 / 标签;已结案另含「完成了什么」)。 */
175
- function hitKeyword(keyword, parts) {
176
- const needle = keyword.trim().toLowerCase();
177
- if (needle === "") return true;
178
- return parts.some((part) => part?.toLowerCase().includes(needle) === true);
179
- }
180
- /** 「我的」判定:owner = 该工作区的当前 git 身份,且状态是进行中(blocked 是旧数据,照收;与 host 同口径)。 */
181
- function isMine(task, board) {
182
- if (board === null) return false;
183
- const identity = board.identities[task.workspace.id] ?? null;
184
- if (identity === null || identity === "") return false;
185
- return task.owner === identity && (task.status === "doing" || task.status === "blocked");
186
- }
187
- function resolveActions(task, board) {
188
- const mine = isMine(task, board);
189
- return {
190
- view: true,
191
- claim: !mine && task.status === "open",
192
- resume: mine,
193
- finish: mine,
194
- release: mine
195
- };
196
- }
197
- /** 已结案行(统计 tab):只有查看,没有在办动作。 */
198
- const CLOSED_ACTIONS = {
199
- view: true,
200
- claim: false,
201
- resume: false,
202
- finish: false,
203
- release: false
204
- };
205
- /** 状态档命中(积压 / 逾期 / 停滞由 host 标记,前端不判阈值)——只作用于第二区。 */
206
- function statusHit(task, status) {
207
- if (status === "all") return true;
208
- if (status === "active") return task.status === "doing" || task.status === "blocked";
209
- if (status === "open") return task.status === "open";
210
- if (status === "backlog") return task.backlog === true;
211
- if (status === "overdue") return task.overdue === true;
212
- return task.stale === true;
213
- }
214
- /** 组内排序:别人的进行中在前、待认领在后;各自按创建 / 开始时间倒序。 */
215
- function inGroupOrder(a, b) {
216
- if (a.kind !== "task" || b.kind !== "task") return 0;
217
- const rank = (task) => task.status === "open" ? 1 : 0;
218
- if (rank(a.task) !== rank(b.task)) return rank(a.task) - rank(b.task);
219
- return String(b.task.created ?? b.task.started ?? "").localeCompare(String(a.task.created ?? a.task.started ?? ""));
220
- }
221
- /**
222
- * 当前筛选下的两区视图(用户 2026-10-18 调整):
223
- * - 第一区「我的待办」= 我名下 doing/blocked,吃工作区下拉 + 标题搜索,按创建时间倒序;
224
- * - 第二区「全部待办」= **所有**在办任务按工作区分组(含我的——用户明确不要过滤掉自己),
225
- * 吃**状态档**(segmented)+ **标题 / 执行人搜索**(othersKeyword);一个整表(外框连着)。
226
- * ⇒ 我的任务会同时出现在两个区,这是预期(顶部快看 / 底部全量)。
227
- */
228
- function selectView(current) {
229
- const { board, query } = current;
230
- if (board === null) return {
231
- mine: [],
232
- groups: [],
233
- total: 0,
234
- allCounts: {
235
- total: 0,
236
- doing: 0,
237
- open: 0,
238
- overdue: 0,
239
- stale: 0,
240
- backlog: 0
241
- }
242
- };
243
- const allTasks = board.tasks;
244
- const allCounts = {
245
- total: allTasks.length,
246
- doing: allTasks.filter((task) => task.status === "doing").length,
247
- open: allTasks.filter((task) => task.status === "open").length,
248
- overdue: allTasks.filter((task) => task.overdue === true).length,
249
- stale: allTasks.filter((task) => task.stale === true).length,
250
- backlog: allTasks.filter((task) => task.backlog === true).length
251
- };
252
- const mineRows = board.tasks.filter((task) => isMine(task, board)).filter((task) => query.workspace === "" || task.workspace.id === query.workspace).filter((task) => hitKeyword(query.keyword, [
253
- task.title,
254
- task.id,
255
- task.owner,
256
- task.creator,
257
- task.category,
258
- ...task.tags ?? []
259
- ])).sort((a, b) => String(b.created ?? b.started ?? "").localeCompare(String(a.created ?? a.started ?? ""))).map((task) => ({
260
- kind: "task",
261
- key: `${task.workspace.id}:${task.id}`,
262
- task,
263
- mine: true
264
- }));
265
- const rest = query.scope === "mine" ? [] : board.tasks.filter((task) => statusHit(task, query.status)).filter((task) => hitKeyword(query.othersKeyword, [task.title, task.owner]));
266
- const byWorkspace = /* @__PURE__ */ new Map();
267
- for (const task of rest) {
268
- const entry = byWorkspace.get(task.workspace.id) ?? {
269
- title: task.workspace.title,
270
- rows: []
271
- };
272
- entry.rows.push({
273
- kind: "task",
274
- key: `${task.workspace.id}:${task.id}`,
275
- task,
276
- mine: isMine(task, board)
277
- });
278
- byWorkspace.set(task.workspace.id, entry);
279
- }
280
- const groups = [...byWorkspace.entries()].map(([workspaceId, entry]) => ({
281
- workspaceId,
282
- title: entry.title,
283
- doingCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "doing").length,
284
- overdueCount: entry.rows.filter((row) => row.kind === "task" && row.task.overdue === true).length,
285
- staleCount: entry.rows.filter((row) => row.kind === "task" && row.task.stale === true).length,
286
- backlogCount: entry.rows.filter((row) => row.kind === "task" && row.task.backlog === true).length,
287
- openCount: entry.rows.filter((row) => row.kind === "task" && row.task.status === "open").length,
288
- total: entry.rows.length,
289
- rows: entry.rows.sort(inGroupOrder)
290
- })).sort((a, b) => a.title.localeCompare(b.title, "zh-Hans-CN"));
291
- return {
292
- mine: mineRows,
293
- groups,
294
- total: groups.reduce((sum, group) => sum + group.rows.length, 0),
295
- allCounts
296
- };
297
- }
298
- //#endregion
299
299
  //#region src/client/format.ts
300
300
  /**
301
301
  * 客户端时间口径**单源**:补零 / 时刻串(本机时区)。
@@ -1760,7 +1760,7 @@ body[data-ds-dark-theme]{
1760
1760
  mdCopied: "已复制",
1761
1761
  mdFootnotes: "脚注",
1762
1762
  emptyNoWorkspace: "还没有台账工作区",
1763
- emptyNoWorkspaceHint: "在某个项目里跑 task-ledger skill 的 init 建档(生成 ledger/progress.json),这里就会出现它。",
1763
+ emptyNoWorkspaceHint: "在某个项目里跑一条 task-ledger skill 的写命令(第一条会自动建档 ledger/pool.json),这里就会出现它。",
1764
1764
  emptyNoTask: "一条任务都没有",
1765
1765
  emptyNoTaskHint: "用 task-ledger skill 的 new 开一个任务,或让别人开在这里等你认领。",
1766
1766
  emptyFiltered: "没有符合当前筛选的任务",
@@ -1885,7 +1885,7 @@ body[data-ds-dark-theme]{
1885
1885
  mdCopied: "Copied",
1886
1886
  mdFootnotes: "Footnotes",
1887
1887
  emptyNoWorkspace: "No ledger workspace yet",
1888
- emptyNoWorkspaceHint: "Run the task-ledger skill init in a project (creates ledger/progress.json) and it shows up here.",
1888
+ emptyNoWorkspaceHint: "Run any task-ledger skill write command in a project (the first one creates ledger/pool.json) and it shows up here.",
1889
1889
  emptyNoTask: "No tasks yet",
1890
1890
  emptyNoTaskHint: "Create one with the task-ledger skill new, or leave one here for others to claim.",
1891
1891
  emptyFiltered: "No tasks match the current filters",