@ouro.bot/cli 0.1.0-alpha.36 → 0.1.0-alpha.38

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.
@@ -33,37 +33,31 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.finalAnswerTool = exports.baseToolHandlers = exports.tools = exports.baseToolDefinitions = void 0;
36
+ exports.finalAnswerTool = exports.baseToolHandlers = exports.tools = exports.baseToolDefinitions = exports.editFileReadTracker = void 0;
37
37
  const fs = __importStar(require("fs"));
38
+ const fg = __importStar(require("fast-glob"));
38
39
  const child_process_1 = require("child_process");
39
40
  const path = __importStar(require("path"));
40
41
  const skills_1 = require("./skills");
41
42
  const config_1 = require("../heart/config");
42
43
  const runtime_1 = require("../nerves/runtime");
43
44
  const identity_1 = require("../heart/identity");
44
- const tasks_1 = require("./tasks");
45
45
  const tools_1 = require("./coding/tools");
46
46
  const memory_1 = require("../mind/memory");
47
47
  const pending_1 = require("../mind/pending");
48
- const postIt = (msg) => `post-it from past you:\n${msg}`;
49
- function normalizeOptionalText(value) {
50
- if (typeof value !== "string")
51
- return null;
52
- const trimmed = value.trim();
53
- return trimmed.length > 0 ? trimmed : null;
54
- }
55
- function buildTaskCreateInput(args) {
56
- return {
57
- title: args.title,
58
- type: args.type,
59
- category: args.category,
60
- body: args.body,
61
- status: normalizeOptionalText(args.status) ?? undefined,
62
- validator: normalizeOptionalText(args.validator),
63
- requester: normalizeOptionalText(args.requester),
64
- cadence: normalizeOptionalText(args.cadence),
65
- scheduledAt: normalizeOptionalText(args.scheduledAt),
66
- };
48
+ // Tracks which file paths have been read via read_file in this session.
49
+ // edit_file requires a file to be read first (must-read-first guard).
50
+ exports.editFileReadTracker = new Set();
51
+ function buildContextDiff(lines, changeStart, changeEnd, contextSize = 3) {
52
+ const start = Math.max(0, changeStart - contextSize);
53
+ const end = Math.min(lines.length, changeEnd + contextSize);
54
+ const result = [];
55
+ for (let i = start; i < end; i++) {
56
+ const lineNum = i + 1;
57
+ const prefix = (i >= changeStart && i < changeEnd) ? ">" : " ";
58
+ result.push(`${prefix} ${lineNum} | ${lines[i]}`);
59
+ }
60
+ return result.join("\n");
67
61
  }
68
62
  exports.baseToolDefinitions = [
69
63
  {
@@ -74,12 +68,27 @@ exports.baseToolDefinitions = [
74
68
  description: "read file contents",
75
69
  parameters: {
76
70
  type: "object",
77
- properties: { path: { type: "string" } },
71
+ properties: {
72
+ path: { type: "string" },
73
+ offset: { type: "number", description: "1-based line number to start reading from" },
74
+ limit: { type: "number", description: "maximum number of lines to return" },
75
+ },
78
76
  required: ["path"],
79
77
  },
80
78
  },
81
79
  },
82
- handler: (a) => fs.readFileSync(a.path, "utf-8"),
80
+ handler: (a) => {
81
+ const content = fs.readFileSync(a.path, "utf-8");
82
+ exports.editFileReadTracker.add(a.path);
83
+ const offset = a.offset ? parseInt(a.offset, 10) : undefined;
84
+ const limit = a.limit ? parseInt(a.limit, 10) : undefined;
85
+ if (offset === undefined && limit === undefined)
86
+ return content;
87
+ const lines = content.split("\n");
88
+ const start = offset ? offset - 1 : 0;
89
+ const end = limit !== undefined ? start + limit : lines.length;
90
+ return lines.slice(start, end).join("\n");
91
+ },
83
92
  },
84
93
  {
85
94
  tool: {
@@ -104,97 +113,203 @@ exports.baseToolDefinitions = [
104
113
  tool: {
105
114
  type: "function",
106
115
  function: {
107
- name: "shell",
108
- description: "run shell command",
116
+ name: "edit_file",
117
+ description: "surgically edit a file by replacing an exact string. the file must have been read via read_file first. old_string must match exactly one location in the file.",
109
118
  parameters: {
110
119
  type: "object",
111
- properties: { command: { type: "string" } },
112
- required: ["command"],
120
+ properties: {
121
+ path: { type: "string" },
122
+ old_string: { type: "string" },
123
+ new_string: { type: "string" },
124
+ },
125
+ required: ["path", "old_string", "new_string"],
113
126
  },
114
127
  },
115
128
  },
116
- handler: (a) => (0, child_process_1.execSync)(a.command, { encoding: "utf-8", timeout: 30000 }),
129
+ handler: (a) => {
130
+ if (!exports.editFileReadTracker.has(a.path)) {
131
+ return `error: you must read the file with read_file before editing it. call read_file on ${a.path} first.`;
132
+ }
133
+ let content;
134
+ try {
135
+ content = fs.readFileSync(a.path, "utf-8");
136
+ }
137
+ catch (e) {
138
+ return `error: could not read file: ${e instanceof Error ? e.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(e)}`;
139
+ }
140
+ // Count occurrences
141
+ const occurrences = [];
142
+ let searchFrom = 0;
143
+ while (true) {
144
+ const idx = content.indexOf(a.old_string, searchFrom);
145
+ if (idx === -1)
146
+ break;
147
+ occurrences.push(idx);
148
+ searchFrom = idx + 1;
149
+ }
150
+ if (occurrences.length === 0) {
151
+ return `error: old_string not found in ${a.path}`;
152
+ }
153
+ if (occurrences.length > 1) {
154
+ return `error: old_string is ambiguous -- found ${occurrences.length} matches in ${a.path}. provide more context to make the match unique.`;
155
+ }
156
+ // Single unique match -- replace
157
+ const idx = occurrences[0];
158
+ const updated = content.slice(0, idx) + a.new_string + content.slice(idx + a.old_string.length);
159
+ fs.writeFileSync(a.path, updated, "utf-8");
160
+ // Build contextual diff
161
+ const lines = updated.split("\n");
162
+ const prefixLines = content.slice(0, idx).split("\n");
163
+ const changeStartLine = prefixLines.length - 1;
164
+ const newStringLines = a.new_string.split("\n");
165
+ const changeEndLine = changeStartLine + newStringLines.length;
166
+ return buildContextDiff(lines, changeStartLine, changeEndLine);
167
+ },
117
168
  },
118
169
  {
119
170
  tool: {
120
171
  type: "function",
121
172
  function: {
122
- name: "list_directory",
123
- description: "list directory contents",
173
+ name: "glob",
174
+ description: "find files matching a glob pattern. returns matching paths sorted alphabetically, one per line.",
124
175
  parameters: {
125
176
  type: "object",
126
- properties: { path: { type: "string" } },
127
- required: ["path"],
177
+ properties: {
178
+ pattern: { type: "string", description: "glob pattern (e.g. **/*.ts)" },
179
+ cwd: { type: "string", description: "directory to search from (defaults to process.cwd())" },
180
+ },
181
+ required: ["pattern"],
128
182
  },
129
183
  },
130
184
  },
131
- handler: (a) => fs
132
- .readdirSync(a.path, { withFileTypes: true })
133
- .map((e) => `${e.isDirectory() ? "d" : "-"} ${e.name}`)
134
- .join("\n"),
185
+ handler: (a) => {
186
+ const cwd = a.cwd || process.cwd();
187
+ const matches = fg.globSync(a.pattern, { cwd, dot: true });
188
+ return matches.sort().join("\n");
189
+ },
135
190
  },
136
191
  {
137
192
  tool: {
138
193
  type: "function",
139
194
  function: {
140
- name: "git_commit",
141
- description: "commit changes to git with explicit paths",
195
+ name: "grep",
196
+ description: "search file contents for lines matching a regex pattern. searches recursively when given a directory. returns matching lines with file path and line numbers.",
142
197
  parameters: {
143
198
  type: "object",
144
199
  properties: {
145
- message: { type: "string" },
146
- paths: { type: "array", items: { type: "string" } },
200
+ pattern: { type: "string", description: "regex pattern to search for" },
201
+ path: { type: "string", description: "file or directory to search" },
202
+ context_lines: { type: "number", description: "number of surrounding context lines (default 0)" },
203
+ include: { type: "string", description: "glob filter to limit searched files (e.g. *.ts)" },
147
204
  },
148
- required: ["message", "paths"],
205
+ required: ["pattern", "path"],
149
206
  },
150
207
  },
151
208
  },
152
209
  handler: (a) => {
153
- try {
154
- if (!a.paths || !Array.isArray(a.paths) || a.paths.length === 0) {
155
- return postIt("paths are required. specify explicit files to commit.");
210
+ const targetPath = a.path;
211
+ const regex = new RegExp(a.pattern);
212
+ const contextLines = parseInt(a.context_lines || "0", 10);
213
+ const includeGlob = a.include || undefined;
214
+ function searchFile(filePath) {
215
+ let content;
216
+ try {
217
+ content = fs.readFileSync(filePath, "utf-8");
218
+ }
219
+ catch {
220
+ return [];
156
221
  }
157
- for (const p of a.paths) {
158
- if (!fs.existsSync(p)) {
159
- return postIt(`path does not exist: ${p}`);
222
+ const lines = content.split("\n");
223
+ const matchIndices = new Set();
224
+ for (let i = 0; i < lines.length; i++) {
225
+ if (regex.test(lines[i])) {
226
+ matchIndices.add(i);
160
227
  }
161
- (0, child_process_1.execSync)(`git add ${p}`, { encoding: "utf-8" });
162
228
  }
163
- const diff = (0, child_process_1.execSync)("git diff --cached --stat", { encoding: "utf-8" });
164
- if (!diff || diff.trim().length === 0) {
165
- return postIt("nothing was staged. check your changes or paths.");
229
+ if (matchIndices.size === 0)
230
+ return [];
231
+ const outputIndices = new Set();
232
+ for (const idx of matchIndices) {
233
+ const start = Math.max(0, idx - contextLines);
234
+ const end = Math.min(lines.length - 1, idx + contextLines);
235
+ for (let i = start; i <= end; i++) {
236
+ outputIndices.add(i);
237
+ }
166
238
  }
167
- (0, child_process_1.execSync)(`git commit -m \"${a.message}\"`, { encoding: "utf-8" });
168
- return `${diff}\ncommitted`;
239
+ const sortedIndices = [...outputIndices].sort((a, b) => a - b);
240
+ const results = [];
241
+ for (const idx of sortedIndices) {
242
+ const lineNum = idx + 1;
243
+ if (matchIndices.has(idx)) {
244
+ results.push(`${filePath}:${lineNum}: ${lines[idx]}`);
245
+ }
246
+ else {
247
+ results.push(`-${filePath}:${lineNum}: ${lines[idx]}`);
248
+ }
249
+ }
250
+ return results;
169
251
  }
170
- catch (e) {
171
- return `failed: ${e}`;
252
+ function collectFiles(dirPath) {
253
+ const files = [];
254
+ function walk(dir) {
255
+ let entries;
256
+ try {
257
+ entries = fs.readdirSync(dir, { withFileTypes: true });
258
+ }
259
+ catch {
260
+ return;
261
+ }
262
+ for (const entry of entries) {
263
+ const fullPath = path.join(dir, entry.name);
264
+ if (entry.isDirectory()) {
265
+ walk(fullPath);
266
+ }
267
+ else if (entry.isFile()) {
268
+ files.push(fullPath);
269
+ }
270
+ }
271
+ }
272
+ walk(dirPath);
273
+ return files.sort();
274
+ }
275
+ function matchesGlob(filePath, glob) {
276
+ const escaped = glob
277
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
278
+ .replace(/\*/g, ".*")
279
+ .replace(/\?/g, ".");
280
+ return new RegExp(`(^|/)${escaped}$`).test(filePath);
281
+ }
282
+ const stat = fs.statSync(targetPath, { throwIfNoEntry: false });
283
+ if (!stat)
284
+ return "";
285
+ if (stat.isFile()) {
286
+ return searchFile(targetPath).join("\n");
287
+ }
288
+ let files = collectFiles(targetPath);
289
+ if (includeGlob) {
290
+ files = files.filter((f) => matchesGlob(f, includeGlob));
172
291
  }
292
+ const allResults = [];
293
+ for (const file of files) {
294
+ allResults.push(...searchFile(file));
295
+ }
296
+ return allResults.join("\n");
173
297
  },
174
298
  },
175
299
  {
176
300
  tool: {
177
301
  type: "function",
178
302
  function: {
179
- name: "gh_cli",
180
- description: "execute a GitHub CLI (gh) command. use carefully.",
303
+ name: "shell",
304
+ description: "run shell command",
181
305
  parameters: {
182
306
  type: "object",
183
- properties: {
184
- command: { type: "string" },
185
- },
307
+ properties: { command: { type: "string" } },
186
308
  required: ["command"],
187
309
  },
188
310
  },
189
311
  },
190
- handler: (a) => {
191
- try {
192
- return (0, child_process_1.execSync)(`gh ${a.command}`, { encoding: "utf-8", timeout: 60000 });
193
- }
194
- catch (e) {
195
- return `error: ${e}`;
196
- }
197
- },
312
+ handler: (a) => (0, child_process_1.execSync)(a.command, { encoding: "utf-8", timeout: 30000 }),
198
313
  },
199
314
  {
200
315
  tool: {
@@ -229,20 +344,6 @@ exports.baseToolDefinitions = [
229
344
  }
230
345
  },
231
346
  },
232
- {
233
- tool: {
234
- type: "function",
235
- function: {
236
- name: "get_current_time",
237
- description: "get the current date and time in America/Los_Angeles (Pacific Time)",
238
- parameters: { type: "object", properties: {} },
239
- },
240
- },
241
- handler: () => new Date().toLocaleString("en-US", {
242
- timeZone: "America/Los_Angeles",
243
- hour12: false,
244
- }),
245
- },
246
347
  {
247
348
  tool: {
248
349
  type: "function",
@@ -398,192 +499,6 @@ exports.baseToolDefinitions = [
398
499
  return JSON.stringify(friend, null, 2);
399
500
  },
400
501
  },
401
- {
402
- tool: {
403
- type: "function",
404
- function: {
405
- name: "task_board",
406
- description: "show the task board grouped by status",
407
- parameters: { type: "object", properties: {} },
408
- },
409
- },
410
- handler: () => {
411
- const board = (0, tasks_1.getTaskModule)().getBoard();
412
- return board.full || board.compact || "no tasks found";
413
- },
414
- },
415
- {
416
- tool: {
417
- type: "function",
418
- function: {
419
- name: "task_create",
420
- description: "create a new task in the bundle task system. optionally set `scheduledAt` for a one-time reminder or `cadence` for recurring daemon-scheduled work.",
421
- parameters: {
422
- type: "object",
423
- properties: {
424
- title: { type: "string" },
425
- type: { type: "string", enum: ["one-shot", "ongoing", "habit"] },
426
- category: { type: "string" },
427
- body: { type: "string" },
428
- status: { type: "string" },
429
- validator: { type: "string" },
430
- requester: { type: "string" },
431
- scheduledAt: { type: "string", description: "ISO timestamp for a one-time scheduled run/reminder" },
432
- cadence: { type: "string", description: "recurrence like 30m, 1h, 1d, or cron" },
433
- },
434
- required: ["title", "type", "category", "body"],
435
- },
436
- },
437
- },
438
- handler: (a) => {
439
- try {
440
- const created = (0, tasks_1.getTaskModule)().createTask(buildTaskCreateInput(a));
441
- return `created: ${created}`;
442
- }
443
- catch (error) {
444
- return `error: ${error instanceof Error ? error.message : String(error)}`;
445
- }
446
- },
447
- },
448
- {
449
- tool: {
450
- type: "function",
451
- function: {
452
- name: "schedule_reminder",
453
- description: "create a scheduled reminder or recurring daemon job. use `scheduledAt` for one-time reminders and `cadence` for recurring reminders. this writes canonical task fields that the daemon reconciles into OS-level jobs.",
454
- parameters: {
455
- type: "object",
456
- properties: {
457
- title: { type: "string" },
458
- body: { type: "string" },
459
- category: { type: "string" },
460
- scheduledAt: { type: "string", description: "ISO timestamp for a one-time reminder" },
461
- cadence: { type: "string", description: "recurrence like 30m, 1h, 1d, or cron" },
462
- },
463
- required: ["title", "body"],
464
- },
465
- },
466
- },
467
- handler: (a) => {
468
- const scheduledAt = normalizeOptionalText(a.scheduledAt);
469
- const cadence = normalizeOptionalText(a.cadence);
470
- if (!scheduledAt && !cadence) {
471
- return "error: provide scheduledAt or cadence";
472
- }
473
- try {
474
- const created = (0, tasks_1.getTaskModule)().createTask({
475
- title: a.title,
476
- type: cadence ? "habit" : "one-shot",
477
- category: normalizeOptionalText(a.category) ?? "reminder",
478
- body: a.body,
479
- scheduledAt,
480
- cadence,
481
- });
482
- return `created: ${created}`;
483
- }
484
- catch (error) {
485
- return `error: ${error instanceof Error ? error.message : String(error)}`;
486
- }
487
- },
488
- },
489
- {
490
- tool: {
491
- type: "function",
492
- function: {
493
- name: "task_update_status",
494
- description: "update a task status using validated transitions",
495
- parameters: {
496
- type: "object",
497
- properties: {
498
- name: { type: "string" },
499
- status: { type: "string" },
500
- },
501
- required: ["name", "status"],
502
- },
503
- },
504
- },
505
- handler: (a) => {
506
- const result = (0, tasks_1.getTaskModule)().updateStatus(a.name, a.status);
507
- if (!result.ok) {
508
- return `error: ${result.reason ?? "status update failed"}`;
509
- }
510
- const archivedSuffix = result.archived && result.archived.length > 0
511
- ? ` | archived: ${result.archived.join(", ")}`
512
- : "";
513
- return `updated: ${a.name} -> ${result.to}${archivedSuffix}`;
514
- },
515
- },
516
- {
517
- tool: {
518
- type: "function",
519
- function: {
520
- name: "task_board_status",
521
- description: "show board detail for a specific status",
522
- parameters: {
523
- type: "object",
524
- properties: {
525
- status: { type: "string" },
526
- },
527
- required: ["status"],
528
- },
529
- },
530
- },
531
- handler: (a) => {
532
- const lines = (0, tasks_1.getTaskModule)().boardStatus(a.status);
533
- return lines.length > 0 ? lines.join("\n") : "no tasks in that status";
534
- },
535
- },
536
- {
537
- tool: {
538
- type: "function",
539
- function: {
540
- name: "task_board_action",
541
- description: "show tasks or validation issues that require action",
542
- parameters: {
543
- type: "object",
544
- properties: {
545
- scope: { type: "string" },
546
- },
547
- },
548
- },
549
- },
550
- handler: (a) => {
551
- const lines = (0, tasks_1.getTaskModule)().boardAction();
552
- if (!a.scope) {
553
- return lines.length > 0 ? lines.join("\n") : "no action required";
554
- }
555
- const filtered = lines.filter((line) => line.includes(a.scope));
556
- return filtered.length > 0 ? filtered.join("\n") : "no matching action items";
557
- },
558
- },
559
- {
560
- tool: {
561
- type: "function",
562
- function: {
563
- name: "task_board_deps",
564
- description: "show unresolved task dependencies",
565
- parameters: { type: "object", properties: {} },
566
- },
567
- },
568
- handler: () => {
569
- const lines = (0, tasks_1.getTaskModule)().boardDeps();
570
- return lines.length > 0 ? lines.join("\n") : "no unresolved dependencies";
571
- },
572
- },
573
- {
574
- tool: {
575
- type: "function",
576
- function: {
577
- name: "task_board_sessions",
578
- description: "show tasks with active coding or sub-agent sessions",
579
- parameters: { type: "object", properties: {} },
580
- },
581
- },
582
- handler: () => {
583
- const lines = (0, tasks_1.getTaskModule)().boardSessions();
584
- return lines.length > 0 ? lines.join("\n") : "no active sessions";
585
- },
586
- },
587
502
  {
588
503
  tool: {
589
504
  type: "function",
@@ -741,12 +656,18 @@ exports.baseToolDefinitions = [
741
656
  const key = args.key || "session";
742
657
  const content = args.content;
743
658
  const now = Date.now();
744
- const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, channel, key);
659
+ const agentName = (0, identity_1.getAgentName)();
660
+ // Self-routing: messages to "self" always go to inner dialog pending dir,
661
+ // regardless of the channel or key the agent specified.
662
+ const isSelf = friendId === "self";
663
+ const pendingDir = isSelf
664
+ ? (0, pending_1.getPendingDir)(agentName, "self", "inner", "dialog")
665
+ : (0, pending_1.getPendingDir)(agentName, friendId, channel, key);
745
666
  fs.mkdirSync(pendingDir, { recursive: true });
746
667
  const fileName = `${now}-${Math.random().toString(36).slice(2, 10)}.json`;
747
668
  const filePath = path.join(pendingDir, fileName);
748
669
  const envelope = {
749
- from: (0, identity_1.getAgentName)(),
670
+ from: agentName,
750
671
  friendId,
751
672
  channel,
752
673
  key,
@@ -755,7 +676,8 @@ exports.baseToolDefinitions = [
755
676
  };
756
677
  fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
757
678
  const preview = content.length > 80 ? content.slice(0, 80) + "…" : content;
758
- return `message queued for delivery to ${friendId} on ${channel}/${key}. preview: "${preview}". it will be delivered when their session is next active.`;
679
+ const target = isSelf ? "inner/dialog" : `${channel}/${key}`;
680
+ return `message queued for delivery to ${friendId} on ${target}. preview: "${preview}". it will be delivered when their session is next active.`;
759
681
  },
760
682
  },
761
683
  ...tools_1.codingToolDefinitions,
@@ -18,14 +18,8 @@ Object.defineProperty(exports, "finalAnswerTool", { enumerable: true, get: funct
18
18
  var tools_teams_2 = require("./tools-teams");
19
19
  Object.defineProperty(exports, "teamsTools", { enumerable: true, get: function () { return tools_teams_2.teamsTools; } });
20
20
  // All tool definitions in a single registry
21
- const allDefinitions = [
22
- ...tools_base_1.baseToolDefinitions,
23
- ...tools_bluebubbles_1.bluebubblesToolDefinitions,
24
- ...tools_teams_1.teamsToolDefinitions,
25
- ...ado_semantic_1.adoSemanticToolDefinitions,
26
- ...tools_github_1.githubToolDefinitions,
27
- ];
28
- const REMOTE_BLOCKED_LOCAL_TOOLS = new Set(["shell", "read_file", "write_file", "git_commit", "gh_cli"]);
21
+ const allDefinitions = [...tools_base_1.baseToolDefinitions, ...tools_bluebubbles_1.bluebubblesToolDefinitions, ...tools_teams_1.teamsToolDefinitions, ...ado_semantic_1.adoSemanticToolDefinitions, ...tools_github_1.githubToolDefinitions];
22
+ const REMOTE_BLOCKED_LOCAL_TOOLS = new Set(["shell", "read_file", "write_file", "edit_file", "glob", "grep"]);
29
23
  function isRemoteChannel(capabilities) {
30
24
  return capabilities?.channel === "teams" || capabilities?.channel === "bluebubbles";
31
25
  }
@@ -183,28 +177,16 @@ function summarizeArgs(name, args) {
183
177
  // Base tools
184
178
  if (name === "read_file" || name === "write_file")
185
179
  return summarizeKeyValues(args, ["path"]);
186
- if (name === "shell")
187
- return summarizeKeyValues(args, ["command"]);
188
- if (name === "list_directory")
180
+ if (name === "edit_file")
189
181
  return summarizeKeyValues(args, ["path"]);
190
- if (name === "git_commit")
191
- return summarizeKeyValues(args, ["message"]);
192
- if (name === "gh_cli")
182
+ if (name === "glob")
183
+ return summarizeKeyValues(args, ["pattern", "cwd"]);
184
+ if (name === "grep")
185
+ return summarizeKeyValues(args, ["pattern", "path", "include"]);
186
+ if (name === "shell")
193
187
  return summarizeKeyValues(args, ["command"]);
194
188
  if (name === "load_skill")
195
189
  return summarizeKeyValues(args, ["name"]);
196
- if (name === "task_create")
197
- return summarizeKeyValues(args, ["title", "type", "category", "scheduledAt", "cadence"]);
198
- if (name === "schedule_reminder")
199
- return summarizeKeyValues(args, ["title", "scheduledAt", "cadence"]);
200
- if (name === "task_update_status")
201
- return summarizeKeyValues(args, ["name", "status"]);
202
- if (name === "task_board_status")
203
- return summarizeKeyValues(args, ["status"]);
204
- if (name === "task_board_action")
205
- return summarizeKeyValues(args, ["scope"]);
206
- if (name === "task_board" || name === "task_board_deps" || name === "task_board_sessions")
207
- return "";
208
190
  if (name === "coding_spawn")
209
191
  return summarizeKeyValues(args, ["runner", "workdir", "taskRef"]);
210
192
  if (name === "coding_status")