linkshell-cli 0.3.10 → 0.3.12

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.
@@ -62,6 +62,8 @@ interface CodexIndexEntry {
62
62
  updatedAt?: number;
63
63
  }
64
64
 
65
+ type StoredToolStatus = NonNullable<StoredAgentTimelineItem["toolCall"]>["status"];
66
+
65
67
  function asRecord(value: unknown): Record<string, unknown> | undefined {
66
68
  return value && typeof value === "object" && !Array.isArray(value)
67
69
  ? value as Record<string, unknown>
@@ -366,7 +368,8 @@ function historyToolName(name: string | undefined): string {
366
368
  return name;
367
369
  }
368
370
 
369
- function historyToolKind(name: string | undefined): "tool_activity" | "command_execution" {
371
+ function historyToolKind(name: string | undefined): "tool_activity" | "command_execution" | "file_change" {
372
+ if (name === "apply_patch") return "file_change";
370
373
  return name?.endsWith("exec_command") || name?.endsWith("write_stdin") ? "command_execution" : "tool_activity";
371
374
  }
372
375
 
@@ -390,6 +393,75 @@ function commandFromCodexTool(name: string | undefined, rawInput: unknown, outpu
390
393
  return { command, cwd, output, status: output === undefined ? "running" : "completed" };
391
394
  }
392
395
 
396
+ function patchTextFromCodexTool(name: string | undefined, rawInput: unknown, input: string | undefined): string | undefined {
397
+ if (name !== "apply_patch") return undefined;
398
+ if (typeof rawInput === "string") return rawInput;
399
+ const record = asRecord(rawInput);
400
+ for (const key of ["patch", "input", "text", "content"]) {
401
+ const value = record?.[key];
402
+ if (typeof value === "string" && value.trim()) return value;
403
+ }
404
+ return input;
405
+ }
406
+
407
+ function fileChangeFromApplyPatch(
408
+ patchText: string | undefined,
409
+ status: StoredToolStatus,
410
+ ): StoredAgentTimelineItem["fileChange"] | undefined {
411
+ if (!patchText?.trim()) return undefined;
412
+ const entries: NonNullable<StoredAgentTimelineItem["fileChange"]>["entries"] = [];
413
+ let current: NonNullable<StoredAgentTimelineItem["fileChange"]>["entries"][number] | undefined;
414
+
415
+ const flush = () => {
416
+ if (!current?.path) return;
417
+ const existing = entries.find((entry) => entry.path === current!.path);
418
+ if (existing) {
419
+ existing.added = (existing.added ?? 0) + (current.added ?? 0);
420
+ existing.removed = (existing.removed ?? 0) + (current.removed ?? 0);
421
+ existing.kind ??= current.kind;
422
+ } else {
423
+ entries.push(current);
424
+ }
425
+ };
426
+
427
+ for (const rawLine of patchText.split(/\r?\n/)) {
428
+ const add = rawLine.match(/^\*\*\* Add File:\s+(.+)$/);
429
+ const update = rawLine.match(/^\*\*\* Update File:\s+(.+)$/);
430
+ const del = rawLine.match(/^\*\*\* Delete File:\s+(.+)$/);
431
+ const move = rawLine.match(/^\*\*\* Move to:\s+(.+)$/);
432
+ if (add || update || del) {
433
+ flush();
434
+ current = {
435
+ path: (add?.[1] ?? update?.[1] ?? del?.[1] ?? "").trim(),
436
+ kind: add ? "create" : del ? "delete" : "update",
437
+ added: 0,
438
+ removed: 0,
439
+ };
440
+ continue;
441
+ }
442
+ if (move?.[1] && current) {
443
+ current.path = move[1].trim();
444
+ current.kind = "move";
445
+ continue;
446
+ }
447
+ if (!current) continue;
448
+ if (rawLine.startsWith("+") && !rawLine.startsWith("+++")) {
449
+ current.added = (current.added ?? 0) + 1;
450
+ } else if (rawLine.startsWith("-") && !rawLine.startsWith("---")) {
451
+ current.removed = (current.removed ?? 0) + 1;
452
+ }
453
+ }
454
+ flush();
455
+
456
+ if (entries.length === 0) return undefined;
457
+ return {
458
+ entries,
459
+ diff: patchText,
460
+ summary: entries.map((entry) => [entry.kind, entry.path].filter(Boolean).join(" ")).join("\n"),
461
+ status,
462
+ };
463
+ }
464
+
393
465
  function upsertHistoryTool(
394
466
  itemsById: Map<string, StoredAgentTimelineItem>,
395
467
  conversationId: string,
@@ -402,21 +474,27 @@ function upsertHistoryTool(
402
474
  const id = `history-tool:${callId}`;
403
475
  const existing = itemsById.get(id);
404
476
  const commandExecution = commandFromCodexTool(name, rawInput, existing?.commandExecution?.output);
477
+ const status: StoredToolStatus = existing?.toolCall?.status ?? "running";
478
+ const fileChange = fileChangeFromApplyPatch(patchTextFromCodexTool(name, rawInput, input), status);
405
479
  itemsById.set(id, {
406
480
  id,
407
481
  conversationId,
408
482
  type: "tool_call",
409
- kind: historyToolKind(name),
483
+ kind: fileChange ? "file_change" : historyToolKind(name),
410
484
  itemId: callId,
411
485
  toolCall: {
412
486
  id: callId,
413
- name: historyToolName(name),
414
- input: input ?? existing?.toolCall?.input,
487
+ name: fileChange ? "文件修改" : historyToolName(name),
488
+ input: fileChange?.summary ?? input ?? existing?.toolCall?.input,
415
489
  output: existing?.toolCall?.output,
416
490
  createdAt: existing?.toolCall?.createdAt ?? createdAt,
417
- status: existing?.toolCall?.status ?? "running",
491
+ status,
418
492
  },
419
493
  commandExecution: commandExecution ?? existing?.commandExecution,
494
+ fileChange: fileChange ?? existing?.fileChange,
495
+ text: fileChange
496
+ ? `已编辑 ${fileChange.entries.length} 个文件`
497
+ : existing?.text,
420
498
  createdAt: existing?.createdAt ?? createdAt,
421
499
  updatedAt: createdAt,
422
500
  metadata: { source: "device-history", provider: "codex" },
@@ -435,6 +513,9 @@ function completeHistoryTool(
435
513
  const commandExecution = existing?.commandExecution
436
514
  ? { ...existing.commandExecution, output, status: "completed" as const }
437
515
  : undefined;
516
+ const fileChange = existing?.fileChange
517
+ ? { ...existing.fileChange, summary: existing.fileChange.summary ?? output, status: "completed" as const }
518
+ : undefined;
438
519
  itemsById.set(id, {
439
520
  id,
440
521
  conversationId,
@@ -450,6 +531,8 @@ function completeHistoryTool(
450
531
  status: "completed",
451
532
  },
452
533
  commandExecution,
534
+ fileChange,
535
+ text: fileChange ? existing?.text ?? `已编辑 ${fileChange.entries.length} 个文件` : existing?.text,
453
536
  createdAt: existing?.createdAt ?? createdAt,
454
537
  updatedAt: createdAt,
455
538
  metadata: { source: "device-history", provider: "codex" },