gsd-pi 2.74.0-dev.703eabc → 2.74.0-dev.b2838e6

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 (113) hide show
  1. package/dist/resources/extensions/gsd/auto/phases.js +51 -6
  2. package/dist/resources/extensions/gsd/auto-model-selection.js +3 -3
  3. package/dist/resources/extensions/gsd/auto-recovery.js +24 -10
  4. package/dist/resources/extensions/gsd/auto-worktree.js +2 -0
  5. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -3
  6. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -5
  7. package/dist/resources/extensions/gsd/cache.js +16 -5
  8. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  9. package/dist/resources/extensions/gsd/commands/handlers/core.js +5 -1
  10. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +50 -3
  11. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -0
  12. package/dist/resources/extensions/gsd/guided-flow.js +8 -6
  13. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  14. package/dist/resources/extensions/gsd/preferences-validation.js +10 -0
  15. package/dist/resources/extensions/gsd/preferences.js +5 -0
  16. package/dist/resources/extensions/gsd/safety/evidence-collector.js +15 -30
  17. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  18. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  21. package/dist/web/standalone/.next/build-manifest.json +2 -2
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.html +1 -1
  40. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  47. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  49. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  50. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  51. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  52. package/package.json +1 -1
  53. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  54. package/packages/mcp-server/dist/workflow-tools.js +88 -6
  55. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  56. package/packages/mcp-server/src/workflow-tools.ts +95 -10
  57. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  58. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.d.ts +2 -0
  59. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.d.ts.map +1 -0
  60. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +61 -0
  61. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -0
  62. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  63. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  64. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +9 -3
  65. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  66. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts +8 -5
  67. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  68. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +27 -13
  69. package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  70. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  71. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  72. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
  73. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  75. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  76. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +17 -0
  77. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  78. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +92 -0
  79. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +12 -4
  80. package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +36 -15
  81. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +17 -0
  82. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -0
  83. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  84. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  85. package/packages/pi-tui/dist/tui.js +9 -2
  86. package/packages/pi-tui/dist/tui.js.map +1 -1
  87. package/packages/pi-tui/src/tui.ts +9 -1
  88. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  89. package/src/resources/extensions/gsd/auto/phases.ts +70 -6
  90. package/src/resources/extensions/gsd/auto-model-selection.ts +3 -3
  91. package/src/resources/extensions/gsd/auto-recovery.ts +29 -9
  92. package/src/resources/extensions/gsd/auto-worktree.ts +1 -0
  93. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +5 -3
  94. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +16 -5
  95. package/src/resources/extensions/gsd/cache.ts +16 -5
  96. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  97. package/src/resources/extensions/gsd/commands/handlers/core.ts +5 -1
  98. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +57 -3
  99. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -0
  100. package/src/resources/extensions/gsd/guided-flow.ts +4 -2
  101. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  102. package/src/resources/extensions/gsd/preferences-validation.ts +10 -0
  103. package/src/resources/extensions/gsd/preferences.ts +6 -0
  104. package/src/resources/extensions/gsd/safety/evidence-collector.ts +15 -31
  105. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  106. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +177 -0
  107. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +272 -0
  108. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +117 -0
  109. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  110. package/src/resources/extensions/gsd/tests/preferences.test.ts +145 -0
  111. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +57 -2
  112. /package/dist/web/standalone/.next/static/{3U-oZ5FT59BM7sm2GInic → wuiYdNtJdo9ISED55DAkz}/_buildManifest.js +0 -0
  113. /package/dist/web/standalone/.next/static/{3U-oZ5FT59BM7sm2GInic → wuiYdNtJdo9ISED55DAkz}/_ssgManifest.js +0 -0
@@ -0,0 +1,92 @@
1
+ import { test, describe } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import stripAnsi from "strip-ansi";
4
+ import { renderChatFrame } from "../chat-frame.js";
5
+ import { initTheme } from "../../theme/theme.js";
6
+
7
+ initTheme("dark", false);
8
+
9
+ // Regression tests for the "compaction" tone added to renderChatFrame.
10
+ // The compaction notice shares the same visual frame as user / assistant
11
+ // messages (top rule, `• label` header, `│ ` body prefix) but uses the
12
+ // purple `customMessageLabel` color key so it is visually distinct from
13
+ // conversation turns.
14
+
15
+ describe("renderChatFrame — compaction tone", () => {
16
+ test("produces a top rule, `• compaction` header row, and a │ body margin", () => {
17
+ const lines = renderChatFrame(
18
+ ["Compacted from 1,224,262 tokens (ctrl+o to expand)"],
19
+ 60,
20
+ {
21
+ label: "compaction",
22
+ tone: "compaction",
23
+ timestampFormat: "date-time-iso",
24
+ showTimestamp: false,
25
+ },
26
+ );
27
+
28
+ // Structure: top rule, header, body line(s)
29
+ assert.ok(lines.length >= 3, `expected at least 3 frame lines, got ${lines.length}`);
30
+
31
+ const plain = lines.map((line) => stripAnsi(line));
32
+
33
+ // Top rule is a solid horizontal bar
34
+ assert.match(plain[0], /^─+$/, "first line should be the solid top rule");
35
+
36
+ // Header row contains `• compaction`
37
+ assert.ok(
38
+ plain[1].includes("• compaction"),
39
+ `expected header to contain "• compaction", got ${JSON.stringify(plain[1])}`,
40
+ );
41
+
42
+ // Body line(s) start with `│ `
43
+ assert.ok(
44
+ plain[2].startsWith("│ "),
45
+ `expected body line to start with "│ ", got ${JSON.stringify(plain[2])}`,
46
+ );
47
+ assert.ok(
48
+ plain[2].includes("Compacted from 1,224,262 tokens"),
49
+ "body line should include the original content",
50
+ );
51
+ });
52
+
53
+ test("does not render a right-aligned timestamp when showTimestamp is false", () => {
54
+ const lines = renderChatFrame(["body"], 60, {
55
+ label: "compaction",
56
+ tone: "compaction",
57
+ timestamp: Date.now(),
58
+ timestampFormat: "date-time-iso",
59
+ showTimestamp: false,
60
+ });
61
+
62
+ const header = stripAnsi(lines[1]);
63
+ // No four-digit year should appear anywhere in the header row
64
+ assert.ok(
65
+ !/\b20\d{2}\b/.test(header),
66
+ `timestamp should be suppressed when showTimestamp=false, got ${JSON.stringify(header)}`,
67
+ );
68
+ });
69
+
70
+ test("emits ANSI color codes distinct from the assistant tone", () => {
71
+ const assistantFrame = renderChatFrame(["body"], 60, {
72
+ label: "claude",
73
+ tone: "assistant",
74
+ timestampFormat: "date-time-iso",
75
+ showTimestamp: false,
76
+ }).join("\n");
77
+
78
+ const compactionFrame = renderChatFrame(["body"], 60, {
79
+ label: "compaction",
80
+ tone: "compaction",
81
+ timestampFormat: "date-time-iso",
82
+ showTimestamp: false,
83
+ }).join("\n");
84
+
85
+ // Both frames carry ANSI; the compaction frame should not be identical
86
+ // to the assistant frame (different color mappings).
87
+ assert.ok(
88
+ assistantFrame !== compactionFrame,
89
+ "compaction tone must produce a different styled output than assistant tone",
90
+ );
91
+ });
92
+ });
@@ -2,7 +2,7 @@ import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
2
2
  import { theme } from "../theme/theme.js";
3
3
  import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
4
4
 
5
- type FrameTone = "assistant" | "user";
5
+ type FrameTone = "assistant" | "user" | "compaction";
6
6
 
7
7
  function trimOuterBlankLines(lines: string[]): string[] {
8
8
  let start = 0;
@@ -25,8 +25,14 @@ export function renderChatFrame(
25
25
  ): string[] {
26
26
  const outerWidth = Math.max(20, width);
27
27
  const contentWidth = Math.max(1, outerWidth - 2); // "│ " + content
28
- const borderColor = opts.tone === "user" ? "borderAccent" : "border";
29
- const borderMuted = opts.tone === "user" ? "borderMuted" : "borderMuted";
28
+ const borderColor =
29
+ opts.tone === "user"
30
+ ? "borderAccent"
31
+ : opts.tone === "compaction"
32
+ ? "customMessageLabel"
33
+ : "border";
34
+ const borderMuted =
35
+ opts.tone === "compaction" ? "customMessageLabel" : "borderMuted";
30
36
  const border = (s: string) => theme.fg(borderColor, s);
31
37
  const leftRaw = `• ${opts.label}`;
32
38
  const rightRaw =
@@ -41,7 +47,9 @@ export function renderChatFrame(
41
47
  const leftStyled =
42
48
  opts.tone === "user"
43
49
  ? theme.fg("accent", theme.bold(left))
44
- : theme.fg("muted", theme.bold(left));
50
+ : opts.tone === "compaction"
51
+ ? theme.fg("customMessageLabel", theme.bold(left))
52
+ : theme.fg("muted", theme.bold(left));
45
53
  const rightStyled = rightRaw ? theme.fg("dim", rightRaw) : "";
46
54
  const gap =
47
55
  rightRaw.length > 0
@@ -1,41 +1,46 @@
1
- import { Box, Markdown, type MarkdownTheme, Spacer, Text } from "@gsd/pi-tui";
1
+ import { Container, Markdown, type MarkdownTheme, Text } from "@gsd/pi-tui";
2
2
  import type { CompactionSummaryMessage } from "../../../core/messages.js";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
+ import { renderChatFrame } from "./chat-frame.js";
4
5
  import { editorKey } from "./keybinding-hints.js";
5
6
 
6
7
  /**
7
- * Component that renders a compaction message with collapsed/expanded state.
8
- * Uses same background color as custom messages for visual consistency.
8
+ * Renders a compaction notice in the shared chat-frame style (top rule,
9
+ * `• compaction` header, `│ ` body margin) with purple border/label so it
10
+ * visually matches the other framed messages (user / assistant / tool
11
+ * execution) while standing apart from the conversation flow.
9
12
  */
10
- export class CompactionSummaryMessageComponent extends Box {
13
+ export class CompactionSummaryMessageComponent extends Container {
11
14
  private expanded = false;
12
15
  private message: CompactionSummaryMessage;
13
16
  private markdownTheme: MarkdownTheme;
14
17
 
15
- constructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {
16
- super(1, 1, (t) => theme.bg("customMessageBg", t));
18
+ constructor(
19
+ message: CompactionSummaryMessage,
20
+ markdownTheme: MarkdownTheme = getMarkdownTheme(),
21
+ ) {
22
+ super();
17
23
  this.message = message;
18
24
  this.markdownTheme = markdownTheme;
19
- this.updateDisplay();
25
+ this.rebuild();
20
26
  }
21
27
 
22
28
  setExpanded(expanded: boolean): void {
23
- this.expanded = expanded;
24
- this.updateDisplay();
29
+ if (this.expanded !== expanded) {
30
+ this.expanded = expanded;
31
+ this.rebuild();
32
+ }
25
33
  }
26
34
 
27
35
  override invalidate(): void {
28
36
  super.invalidate();
29
- this.updateDisplay();
37
+ this.rebuild();
30
38
  }
31
39
 
32
- private updateDisplay(): void {
40
+ private rebuild(): void {
33
41
  this.clear();
34
42
 
35
43
  const tokenStr = this.message.tokensBefore.toLocaleString();
36
- const label = theme.fg("customMessageLabel", theme.bold("[compaction]"));
37
- this.addChild(new Text(label, 0, 0));
38
- this.addChild(new Spacer(1));
39
44
 
40
45
  if (this.expanded) {
41
46
  const header = `**Compacted from ${tokenStr} tokens**\n\n`;
@@ -47,7 +52,10 @@ export class CompactionSummaryMessageComponent extends Box {
47
52
  } else {
48
53
  this.addChild(
49
54
  new Text(
50
- theme.fg("customMessageText", `Compacted from ${tokenStr} tokens (`) +
55
+ theme.fg(
56
+ "customMessageText",
57
+ `Compacted from ${tokenStr} tokens (`,
58
+ ) +
51
59
  theme.fg("dim", editorKey("expandTools")) +
52
60
  theme.fg("customMessageText", " to expand)"),
53
61
  0,
@@ -56,4 +64,17 @@ export class CompactionSummaryMessageComponent extends Box {
56
64
  );
57
65
  }
58
66
  }
67
+
68
+ override render(width: number): string[] {
69
+ const frameWidth = Math.max(20, width);
70
+ const contentWidth = Math.max(1, frameWidth - 4);
71
+ const lines = super.render(contentWidth);
72
+ const framed = renderChatFrame(lines, frameWidth, {
73
+ label: "compaction",
74
+ tone: "compaction",
75
+ timestampFormat: "date-time-iso",
76
+ showTimestamp: false,
77
+ });
78
+ return framed.length > 0 ? ["", ...framed] : framed;
79
+ }
59
80
  }
@@ -427,6 +427,23 @@ export class ToolExecutionComponent extends Container {
427
427
  this.maybeConvertImagesForKitty();
428
428
  }
429
429
 
430
+ /**
431
+ * Mark a tool call as historical when replaying from session context and
432
+ * no matching tool result is available. Happens after compaction squashes
433
+ * tool_result messages out of history — the tool call block survives but
434
+ * the result is gone. Without this, the component stays in "Running" state
435
+ * forever even though the tool completed long ago.
436
+ */
437
+ markHistoricalNoResult(): void {
438
+ if (this.result) return; // real result already set, nothing to do
439
+ this.isPartial = false;
440
+ this.result = {
441
+ content: [],
442
+ isError: false,
443
+ };
444
+ this.updateDisplay();
445
+ }
446
+
430
447
  /**
431
448
  * Finalize a pending tool call as failed/interrupted while preserving any streamed partial output.
432
449
  */
@@ -1827,6 +1827,8 @@ export class InteractiveMode {
1827
1827
  this.showError(message);
1828
1828
  } else if (type === "warning") {
1829
1829
  this.showWarning(message);
1830
+ } else if (type === "success") {
1831
+ this.showSuccess(message);
1830
1832
  } else {
1831
1833
  this.showStatus(message, { append: true });
1832
1834
  }
@@ -2339,6 +2341,12 @@ export class InteractiveMode {
2339
2341
  }
2340
2342
  }
2341
2343
 
2344
+ // Any pendingTools entries left over after replay are historical tool
2345
+ // calls whose results were squashed out of session context (commonly by
2346
+ // compaction). Mark them finished so the frame stops showing "Running".
2347
+ for (const component of this.pendingTools.values()) {
2348
+ component.markHistoricalNoResult();
2349
+ }
2342
2350
  this.pendingTools.clear();
2343
2351
  this.trimChatHistory();
2344
2352
  this.ui.requestRender();
@@ -2737,6 +2745,17 @@ export class InteractiveMode {
2737
2745
  this.ui.requestRender();
2738
2746
  }
2739
2747
 
2748
+ showSuccess(successMessage: string): void {
2749
+ this.chatContainer.addChild(new Spacer(1));
2750
+ this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("success", text)));
2751
+ this.chatContainer.addChild(
2752
+ new Text(theme.fg("success", successMessage), 1, 0),
2753
+ );
2754
+ this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("success", text)));
2755
+ this.chatContainer.addChild(new Spacer(1));
2756
+ this.ui.requestRender();
2757
+ }
2758
+
2740
2759
  showTip(message: string): void {
2741
2760
  this.chatContainer.addChild(new Spacer(1));
2742
2761
  this.chatContainer.addChild(new Text(theme.fg("dim", `💡 ${message}`), 1, 0));