dsh-tree-task-flow 0.3.12 → 0.3.14

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/lib/tools.js CHANGED
@@ -1,5 +1,13 @@
1
1
  /**
2
- * 六个 `tree_task_*` 工具。
2
+ * 七个 `tree_task_*` 工具。
3
+ *
4
+ * **写树分成两个工具,不是可以合并的重复。** `tree_task_create` 与 `tree_task_done`
5
+ * 是折叠区间的两个边界(见 `lib/region.js`):`create` 是"开工"那一下,`done` 是
6
+ * "收工"那一下。`create` 只在新建目标时调用一次,`plan` 却在任务中途反复调用——
7
+ * 把两者并成一个工具,折叠起点就只能靠实参里有没有 `parentId` 去猜,
8
+ * 模型一次写漏或多写就会把起点挪走,那段过程就再也收不走了。
9
+ * 代价是两个工具的实参形状有点像(都是一串 `{title, detail}`),
10
+ * 所以 `plan` 的说明里专门写了一句"建新目标不归它管"。
3
11
  *
4
12
  * 工具本身**只做状态读写**——不注入消息、不动 surface,也不拦任何别的工具。
5
13
  * 「完成时把这个节点的执行过程从上下文里折起来、换成一条携带它结果的汇总」
@@ -22,9 +30,13 @@ import {
22
30
  addGoal,
23
31
  complete,
24
32
  createPlan,
33
+ deleteNode,
25
34
  doneReply,
26
35
  dropNode,
36
+ findNode,
37
+ goalsOf,
27
38
  renderContinue,
39
+ renderGoal,
28
40
  renderTree,
29
41
  updateNode,
30
42
  } from "./plan.js";
@@ -65,21 +77,35 @@ export function registerTools(ctx, { store, cfg }) {
65
77
  ctx.tools.register({
66
78
  name: "tree_task_status",
67
79
  description:
68
- "查看当前计划树:目标、任务、子任务各自的状态与已提交的结果,以及现在该推进哪个节点。" +
69
- "任务开头、上下文被压缩之后、或者不确定下一步做什么时,调用它。",
70
- parameters: { type: "object", additionalProperties: false, properties: {} },
80
+ "查看计划树:目标、任务、子任务各自的状态与已提交的结果,以及现在该推进哪个节点。" +
81
+ "任务开头、上下文被压缩之后、或者不确定下一步做什么时,调用它。" +
82
+ "默认只展开还没结束的目标——已经做完的目标只留目标那一行,要看它的完整树就把 id 填上。",
83
+ parameters: {
84
+ type: "object",
85
+ additionalProperties: false,
86
+ properties: {
87
+ id: {
88
+ type: "string",
89
+ description:
90
+ "可选。填目标 id 就返回那个目标的完整树(已完成的目标默认不展开);" +
91
+ "填任务或子任务 id,就按它所在的那个目标展开。",
92
+ },
93
+ },
94
+ },
71
95
  output: OUTPUT,
72
- async execute(_args, exec) {
96
+ async execute(args, exec) {
73
97
  const plan = store.readPlan(sessionOf(exec));
74
- return ok(renderTree(plan));
98
+ return ok(renderTree(plan, args?.id ?? null));
75
99
  },
76
100
  });
77
101
 
78
102
  ctx.tools.register({
79
103
  name: "tree_task_create",
80
104
  description:
81
- "新增一个目标:这个目标 + 它下面的若干任务。一个会话可以有多个目标," +
82
- "按调用顺序排在计划里——已经建过的目标和它们的历史都留着,不会被这次调用替换掉。" +
105
+ "新建一个目标:这个目标 + 它下面的若干任务。**它是「开工」那一下**——" +
106
+ "折叠上下文时,插件就认这次调用作为「这一轮从哪儿开始」。" +
107
+ "一个会话可以有多个目标,按调用顺序排在计划里——已经建过的目标和它们的历史都留着," +
108
+ "不会被这次调用替换掉。" +
83
109
  "这里的任务是交付节点——一件能独立验收的交付内容,可以是需要完成或验证的某件事," +
84
110
  "也可以是一个检查点,不是步骤本身。" +
85
111
  "建完记得用 tree_task_plan 给这个目标下的第一个任务拆子任务。",
@@ -124,8 +150,14 @@ export function registerTools(ctx, { store, cfg }) {
124
150
  }
125
151
  store.writePlan(sessionId, plan);
126
152
  return ok(
127
- `${existing === null ? "计划已建立。" : `已新增目标「${goal.title}」。`}\n\n${renderTree(plan)}\n\n` +
128
- "下一步:调用 tree_task_plan 给这个目标下的第一个任务拆子任务。",
153
+ [
154
+ existing === null ? `计划已建立,目标「${goal.title}」。` : `已新增目标「${goal.title}」。`,
155
+ "",
156
+ renderGoal(goal, plan, true),
157
+ "",
158
+ "下一步:给这个目标下的第一个任务拆子任务——用 tree_task_plan," +
159
+ "parentId 填那个任务的 id。",
160
+ ].join("\n"),
129
161
  );
130
162
  },
131
163
  });
@@ -135,7 +167,8 @@ export function registerTools(ctx, { store, cfg }) {
135
167
  description:
136
168
  "给一个节点追加子节点:给目标追加任务,给任务追加子任务。" +
137
169
  "给任务追加的是步骤(需要一次性做完的事情)。" +
138
- "子任务是最低一级,不能再往下拆。",
170
+ "子任务是最低一级,不能再往下拆。" +
171
+ "**建新目标不归它管,那是 tree_task_create**——它只往已有的节点上接子节点。",
139
172
  parameters: {
140
173
  type: "object",
141
174
  additionalProperties: false,
@@ -164,12 +197,13 @@ export function registerTools(ctx, { store, cfg }) {
164
197
  const result = addChildren(plan, args?.parentId, args?.children);
165
198
  if (!result.ok) return ok(`没有添加:${result.reason}`);
166
199
  store.writePlan(sessionOf(exec), plan);
200
+ const found = findNode(plan, args?.parentId);
167
201
  return ok(
168
202
  [
169
203
  `已添加 ${result.added.length} 个${childLabel(result.level)}:`,
170
- ...result.added.map((node) => ` ${node.id} ${node.title}`),
204
+ ...result.added.map((node) => `- \`${node.id}\` ${node.title}`),
171
205
  "",
172
- renderTree(plan),
206
+ renderGoal(found.goal, plan, true),
173
207
  ].join("\n"),
174
208
  );
175
209
  },
@@ -230,7 +264,8 @@ export function registerTools(ctx, { store, cfg }) {
230
264
  const result = updateNode(plan, args?.id, args ?? {});
231
265
  if (!result.ok) return ok(`没有修改:${result.reason}`);
232
266
  store.writePlan(sessionOf(exec), plan);
233
- return ok(`已更新 ${result.changed.join(" / ")}。\n\n${renderTree(plan)}`);
267
+ const found = findNode(plan, args?.id);
268
+ return ok(`已更新 ${result.changed.join(" / ")}。\n\n${renderGoal(found.goal, plan, true)}`);
234
269
  },
235
270
  });
236
271
 
@@ -255,13 +290,63 @@ export function registerTools(ctx, { store, cfg }) {
255
290
  const result = dropNode(plan, args?.id);
256
291
  if (!result.ok) return ok(`没有丢弃:${result.reason}`);
257
292
  store.writePlan(sessionOf(exec), plan);
293
+ const found = findNode(plan, args?.id);
258
294
  return ok(
259
- [`已丢弃:${result.dropped.join("、")}`, "", renderTree(plan), "", renderContinue(plan)].join(
260
- "\n",
261
- ),
295
+ [
296
+ `已丢弃:${result.dropped.join("、")}`,
297
+ "",
298
+ renderGoal(found.goal, plan, true),
299
+ "",
300
+ renderContinue(plan),
301
+ ].join("\n"),
262
302
  );
263
303
  },
264
304
  });
265
305
 
306
+ ctx.tools.register({
307
+ name: "tree_task_delete",
308
+ description:
309
+ "从树里删掉一个节点——**清理**用的,不是「这件事不做了」。" +
310
+ "丢弃(tree_task_drop)是留个记号:这件事我不做,但树里还查得到它曾经有过;" +
311
+ "删除是把内容凭空摘掉,连同它下面的任务或子任务一起," +
312
+ "**不看状态**(已完成、已丢弃的都删得掉)。" +
313
+ "内容写错了、建重了、结果记在了错的地方,就用这个。",
314
+ parameters: {
315
+ type: "object",
316
+ additionalProperties: false,
317
+ properties: {
318
+ id: { type: "string", description: "要删除的节点 id" },
319
+ },
320
+ required: ["id"],
321
+ },
322
+ output: OUTPUT,
323
+ async execute(args, exec) {
324
+ const plan = store.readPlan(sessionOf(exec));
325
+ if (!plan) return ok("还没有计划。");
326
+ // 删掉之后就查不到"这次动的是哪个目标"了,先记一份。
327
+ const touched = findNode(plan, args?.id);
328
+ const result = deleteNode(plan, args?.id);
329
+ if (!result.ok) return ok(`没有删除:${result.reason}`);
330
+ store.writePlan(sessionOf(exec), plan);
331
+
332
+ const lines = [`已删除:${result.removed.join("、")}`, ""];
333
+ if (touched !== null && touched.level !== "goal") {
334
+ // 删的是任务或子任务:它所在的那个目标还在,照旧只给它一个目标的树。
335
+ lines.push(renderGoal(touched.goal, plan, true));
336
+ } else {
337
+ // 删的是目标:那个目标已经不存在了,没有"它所在的树"可给。
338
+ // 剩下的目标数说一句就够——要看细节再调 tree_task_status。
339
+ const rest = goalsOf(plan).length;
340
+ lines.push(
341
+ rest === 0
342
+ ? "计划树现在空了。(要用就用 tree_task_create 建新目标。)"
343
+ : `这个会话还剩 ${rest} 个目标,要看细节用 tree_task_status。`,
344
+ );
345
+ }
346
+ lines.push("", renderContinue(plan));
347
+ return ok(lines.join("\n"));
348
+ },
349
+ });
350
+
266
351
  void cfg;
267
352
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-tree-task-flow",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "树形任务流:把多步任务组织成目标→任务→子任务三级树,节点完成时由 AI 提交结果,插件随即把该节点的执行过程从上下文里折叠掉,只留下结果。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",