gsd-pi 2.80.0-dev.96f4d89d5 → 2.80.0-dev.b2a374d5b
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/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +37 -30
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto.js +16 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
- package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
- package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
- package/dist/resources/extensions/gsd/detection.js +106 -0
- package/dist/resources/extensions/gsd/gsd-db.js +102 -2
- package/dist/resources/extensions/gsd/guided-flow.js +63 -12
- package/dist/resources/extensions/gsd/migrate/command.js +40 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/dist/resources/extensions/gsd/quick.js +34 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +4 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +3 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +50 -19
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +77 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +68 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +53 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +21 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +3 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
- package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +68 -19
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +88 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +91 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +25 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +17 -7
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/tui.ts +19 -7
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +36 -27
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +50 -8
- package/src/resources/extensions/gsd/auto/phases.ts +42 -28
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
- package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
- package/src/resources/extensions/gsd/auto-start.ts +7 -6
- package/src/resources/extensions/gsd/auto.ts +23 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
- package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
- package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
- package/src/resources/extensions/gsd/detection.ts +128 -0
- package/src/resources/extensions/gsd/gsd-db.ts +119 -1
- package/src/resources/extensions/gsd/guided-flow.ts +66 -12
- package/src/resources/extensions/gsd/migrate/command.ts +47 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/src/resources/extensions/gsd/quick.ts +37 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
- package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +14 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
- /package/dist/web/standalone/.next/static/{y7d--cnc6Cibuva-Lzzw_ → FU0iL9dhdc2IBzEYBDOlY}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{y7d--cnc6Cibuva-Lzzw_ → FU0iL9dhdc2IBzEYBDOlY}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD2 TUI - Shared chat frame renderer for assistant, user, and system cards.
|
|
1
2
|
import { style, truncateToWidth, visibleWidth } from "@gsd/pi-tui";
|
|
2
3
|
import { theme } from "../theme/theme.js";
|
|
3
4
|
import { formatTimestamp, type TimestampFormat } from "./timestamp.js";
|
|
@@ -34,7 +35,7 @@ export function renderChatFrame(
|
|
|
34
35
|
: "borderAccent";
|
|
35
36
|
const borderMuted = isPurple ? "customMessageLabel" : "borderMuted";
|
|
36
37
|
const border = (s: string) => theme.fg(borderColor, s);
|
|
37
|
-
const leftRaw =
|
|
38
|
+
const leftRaw = opts.label;
|
|
38
39
|
const rightRaw =
|
|
39
40
|
opts.showTimestamp === false || !opts.timestamp
|
|
40
41
|
? ""
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD2 TUI Tests - Runtime coverage for tool-card cleanup and success notification rendering.
|
|
1
2
|
// Runtime regression tests for the post-compaction tool-card cleanup and the
|
|
2
3
|
// green-bordered success-notification rendering. Replaces the source-grep
|
|
3
4
|
// `src/tests/tui-running-and-success-box.test.ts` that was deleted in #4875
|
|
@@ -74,7 +75,7 @@ function makeMockTUI(): MockTui {
|
|
|
74
75
|
// ─── Bug 1: tool cards stuck in "Running" after compaction ──────────────
|
|
75
76
|
|
|
76
77
|
describe("ToolExecutionComponent post-compaction cleanup", () => {
|
|
77
|
-
it("renders '
|
|
78
|
+
it("renders 'running' status while the tool call has no result", () => {
|
|
78
79
|
// Baseline: a freshly-constructed component (mid-stream) must show
|
|
79
80
|
// the running badge — this is the state we need to flip OUT of when
|
|
80
81
|
// compaction removes the result message.
|
|
@@ -88,12 +89,12 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
|
|
|
88
89
|
);
|
|
89
90
|
const rendered = c.render(60).map(stripAnsi).join("\n");
|
|
90
91
|
assert.ok(
|
|
91
|
-
rendered.includes("
|
|
92
|
-
"freshly constructed component should render '
|
|
92
|
+
rendered.includes("running"),
|
|
93
|
+
"freshly constructed component should render 'running' badge",
|
|
93
94
|
);
|
|
94
95
|
});
|
|
95
96
|
|
|
96
|
-
it("markHistoricalNoResult flips a stuck tool card OUT of '
|
|
97
|
+
it("markHistoricalNoResult flips a stuck tool card OUT of 'running'", () => {
|
|
97
98
|
// Real bug: after session-history replay (post-compaction or session
|
|
98
99
|
// switch), tool calls without matching tool_result messages stay in
|
|
99
100
|
// isPartial = true forever. markHistoricalNoResult must produce a
|
|
@@ -111,13 +112,13 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
|
|
|
111
112
|
|
|
112
113
|
const rendered = c.render(60).map(stripAnsi).join("\n");
|
|
113
114
|
assert.ok(
|
|
114
|
-
!rendered.includes("
|
|
115
|
-
"after markHistoricalNoResult, the tool card must NOT render '
|
|
115
|
+
!rendered.includes("running"),
|
|
116
|
+
"after markHistoricalNoResult, the tool card must NOT render 'running' -- got:\n" +
|
|
116
117
|
rendered,
|
|
117
118
|
);
|
|
118
119
|
assert.ok(
|
|
119
|
-
rendered.includes("
|
|
120
|
-
"flipped card should render '
|
|
120
|
+
rendered.includes("success"),
|
|
121
|
+
"flipped card should render 'success' status (no-result success)",
|
|
121
122
|
);
|
|
122
123
|
});
|
|
123
124
|
|
|
@@ -145,7 +146,7 @@ describe("ToolExecutionComponent post-compaction cleanup", () => {
|
|
|
145
146
|
);
|
|
146
147
|
|
|
147
148
|
const before = c.render(60).map(stripAnsi).join("\n");
|
|
148
|
-
assert.ok(before.includes("
|
|
149
|
+
assert.ok(before.includes("success"), "after complete(), card shows 'success'");
|
|
149
150
|
|
|
150
151
|
c.markHistoricalNoResult(); // must early-return
|
|
151
152
|
const after = c.render(60).map(stripAnsi).join("\n");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD-2 Interactive Tool Execution Rendering
|
|
1
2
|
import {
|
|
2
3
|
Box,
|
|
3
4
|
Container,
|
|
@@ -106,9 +107,9 @@ function renderToolFrame(
|
|
|
106
107
|
|
|
107
108
|
const borderColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
|
|
108
109
|
const topColor = borderColor;
|
|
109
|
-
const labelColor = opts.tone === "error" ? "toolError" : "
|
|
110
|
+
const labelColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
|
|
110
111
|
const statusColor = opts.tone === "error" ? "toolError" : opts.tone === "pending" ? "toolRunning" : "toolSuccess";
|
|
111
|
-
const leftStyled = theme.fg(labelColor, theme.bold(
|
|
112
|
+
const leftStyled = theme.fg(labelColor, theme.bold(opts.label));
|
|
112
113
|
const rightStyled = theme.fg(statusColor, opts.status);
|
|
113
114
|
const gap = Math.max(1, outerWidth - visibleWidth(leftStyled) - visibleWidth(rightStyled));
|
|
114
115
|
const headerRow = `${leftStyled}${" ".repeat(gap)}${rightStyled}`;
|
|
@@ -127,10 +128,37 @@ function renderToolFrame(
|
|
|
127
128
|
.render(bodyLines, outerWidth);
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
function renderCollapsedToolRow(
|
|
132
|
+
label: string,
|
|
133
|
+
status: string,
|
|
134
|
+
width: number,
|
|
135
|
+
tone: Extract<ToolFrameTone, "success">,
|
|
136
|
+
): string[] {
|
|
137
|
+
const outerWidth = Math.max(20, width);
|
|
138
|
+
const contentWidth = Math.max(1, outerWidth - 2);
|
|
139
|
+
const statusColor = tone === "success" ? "toolSuccess" : "toolMuted";
|
|
140
|
+
const labelStyled = theme.fg(statusColor, label);
|
|
141
|
+
const statusStyled = theme.fg(statusColor, status);
|
|
142
|
+
const labelWidth = Math.max(1, contentWidth - visibleWidth(statusStyled) - 1);
|
|
143
|
+
const clippedLabel = truncateToWidth(labelStyled, labelWidth, "");
|
|
144
|
+
const gap = Math.max(1, contentWidth - visibleWidth(clippedLabel) - visibleWidth(statusStyled));
|
|
145
|
+
const line = `${clippedLabel}${" ".repeat(gap)}${statusStyled}`;
|
|
146
|
+
return style()
|
|
147
|
+
.border("minimal")
|
|
148
|
+
.borderColor((text) => theme.fg(statusColor, text))
|
|
149
|
+
.render([line], outerWidth);
|
|
150
|
+
}
|
|
151
|
+
|
|
130
152
|
const COMPACT_ARG_VALUE_LIMIT = 60;
|
|
131
153
|
const GENERIC_OUTPUT_PREVIEW_LINES = 10;
|
|
132
154
|
const GENERIC_ARGS_JSON_PREVIEW_LINES = 10;
|
|
133
155
|
|
|
156
|
+
export type ToolExecutionPhase = {
|
|
157
|
+
label: string;
|
|
158
|
+
count: number;
|
|
159
|
+
durationMs: number;
|
|
160
|
+
};
|
|
161
|
+
|
|
134
162
|
function formatElapsed(ms: number): string {
|
|
135
163
|
if (ms < 1000) return `${ms}ms`;
|
|
136
164
|
return `${Math.max(1, Math.round(ms / 1000))}s`;
|
|
@@ -552,18 +580,13 @@ export class ToolExecutionComponent extends Container {
|
|
|
552
580
|
const frameTone: ToolFrameTone =
|
|
553
581
|
this.result?.isError ? "error" : this.isPartial || !this.result ? "pending" : "success";
|
|
554
582
|
const elapsed = formatElapsed((this.endedAt ?? Date.now()) - this.startedAt);
|
|
555
|
-
const frameStatus = `${this.isPartial || !this.result ? "
|
|
583
|
+
const frameStatus = `${this.isPartial || !this.result ? "running" : this.result.isError ? "failed" : "success"} · ${elapsed}`;
|
|
556
584
|
const parsed = parseMcpToolName(this.toolName);
|
|
557
585
|
const frameLabel = parsed
|
|
558
|
-
?
|
|
559
|
-
:
|
|
586
|
+
? `${parsed.server}·${parsed.tool}`
|
|
587
|
+
: prettifyToolName(this.toolName, this.toolDefinition?.label) || "unknown";
|
|
560
588
|
if (this.shouldRenderCompactSuccess()) {
|
|
561
|
-
|
|
562
|
-
const summaryWidth = Math.max(1, availableWidth - visibleWidth(frameStatus) - 1);
|
|
563
|
-
const summary = truncateToWidth(this.getCompactSummary(frameLabel), summaryWidth, "");
|
|
564
|
-
const gap = Math.max(1, availableWidth - visibleWidth(summary) - visibleWidth(frameStatus));
|
|
565
|
-
const line = `${theme.fg("toolSuccess", summary)}${" ".repeat(gap)}${theme.fg("toolSuccess", frameStatus)}`;
|
|
566
|
-
return ["", ...style().border("minimal").borderColor((text) => theme.fg("toolSuccess", text)).render([line], frameWidth)];
|
|
589
|
+
return ["", ...renderCollapsedToolRow(this.getCompactSummary(frameLabel), frameStatus, frameWidth, "success")];
|
|
567
590
|
}
|
|
568
591
|
const lines = super.render(contentWidth);
|
|
569
592
|
const framed = renderToolFrame(lines, frameWidth, {
|
|
@@ -577,11 +600,38 @@ export class ToolExecutionComponent extends Container {
|
|
|
577
600
|
private shouldRenderCompactSuccess(): boolean {
|
|
578
601
|
if (this.expanded || this.isPartial || !this.result || this.result.isError) return false;
|
|
579
602
|
const hasImages = this.result.content?.some((block) => block.type === "image") ?? false;
|
|
580
|
-
return !hasImages
|
|
603
|
+
return !hasImages;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
getRollupPhase(): ToolExecutionPhase | null {
|
|
607
|
+
if (!this.shouldRenderCompactSuccess()) return null;
|
|
608
|
+
const label = this.getPhaseLabel();
|
|
609
|
+
const endedAt = this.endedAt ?? Date.now();
|
|
610
|
+
return {
|
|
611
|
+
label,
|
|
612
|
+
count: 1,
|
|
613
|
+
durationMs: Math.max(0, endedAt - this.startedAt),
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private getPhaseLabel(): string {
|
|
618
|
+
const name = this.normalizedToolName;
|
|
619
|
+
const displayName = prettifyToolName(this.toolName, this.toolDefinition?.label);
|
|
620
|
+
|
|
621
|
+
if (name === "bash") return "Setup / shell";
|
|
622
|
+
if (name === "read" || name === "ls" || name === "find" || name === "grep") return "Context reads";
|
|
623
|
+
if (name === "write" || name === "edit") return "File changes";
|
|
624
|
+
if (name === "web_search" || displayName === "ToolSearch") return "Discovery";
|
|
625
|
+
if (displayName === "Memory Query" || displayName === "Memory Capture" || displayName === "Gsd Graph") {
|
|
626
|
+
return "Memory lookups";
|
|
627
|
+
}
|
|
628
|
+
if (displayName === "Update Requirement" || displayName === "Save Requirement") return "Requirement writes";
|
|
629
|
+
if (displayName.startsWith("Complete ")) return "Finalization";
|
|
630
|
+
return "Other tool actions";
|
|
581
631
|
}
|
|
582
632
|
|
|
583
633
|
private getCompactSummary(frameLabel: string): string {
|
|
584
|
-
return
|
|
634
|
+
return frameLabel;
|
|
585
635
|
}
|
|
586
636
|
|
|
587
637
|
private updateDisplay(): void {
|
|
@@ -1231,3 +1281,28 @@ export class ToolExecutionComponent extends Container {
|
|
|
1231
1281
|
return text;
|
|
1232
1282
|
}
|
|
1233
1283
|
}
|
|
1284
|
+
|
|
1285
|
+
export class ToolPhaseSummaryComponent extends Container {
|
|
1286
|
+
constructor(private readonly phases: ToolExecutionPhase[]) {
|
|
1287
|
+
super();
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
getPhases(): ToolExecutionPhase[] {
|
|
1291
|
+
return this.phases.map((phase) => ({ ...phase }));
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
override render(width: number): string[] {
|
|
1295
|
+
const frameWidth = Math.max(20, width);
|
|
1296
|
+
const rows = this.phases.map((phase) => {
|
|
1297
|
+
const left = `${phase.label} ${phase.count} ${phase.count === 1 ? "action" : "actions"}`;
|
|
1298
|
+
const right = `success · ${formatElapsed(phase.durationMs)}`;
|
|
1299
|
+
const contentWidth = Math.max(1, frameWidth - 2);
|
|
1300
|
+
const leftWidth = Math.max(1, contentWidth - visibleWidth(right) - 1);
|
|
1301
|
+
const leftText = truncateToWidth(left, leftWidth, "");
|
|
1302
|
+
const gap = Math.max(1, contentWidth - visibleWidth(leftText) - visibleWidth(right));
|
|
1303
|
+
return `${theme.fg("toolSuccess", leftText)}${" ".repeat(gap)}${theme.fg("toolSuccess", right)}`;
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
return ["", ...style().border("minimal").borderColor((text) => theme.fg("toolSuccess", text)).render(rows, frameWidth)];
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
+
import { Container } from "@gsd/pi-tui";
|
|
4
|
+
import stripAnsi from "strip-ansi";
|
|
3
5
|
|
|
4
6
|
import { findLatestPinnableText, handleAgentEvent } from "./chat-controller.js";
|
|
5
7
|
import { initTheme } from "../theme/theme.js";
|
|
@@ -111,3 +113,57 @@ test("handleAgentEvent: agent_start clears stale adaptive blocking error", async
|
|
|
111
113
|
assert.equal(cleared, true);
|
|
112
114
|
assert.equal(requestedRender, true);
|
|
113
115
|
});
|
|
116
|
+
|
|
117
|
+
test("handleAgentEvent: standalone completed tool events roll up incrementally", async () => {
|
|
118
|
+
initTheme("dark", false);
|
|
119
|
+
const chatContainer = new Container();
|
|
120
|
+
let renderCount = 0;
|
|
121
|
+
const host = {
|
|
122
|
+
isInitialized: true,
|
|
123
|
+
footer: { invalidate() {} },
|
|
124
|
+
settingsManager: {
|
|
125
|
+
getTimestampFormat() {
|
|
126
|
+
return "date-time-iso";
|
|
127
|
+
},
|
|
128
|
+
getShowImages() {
|
|
129
|
+
return false;
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
getRegisteredToolDefinition() {
|
|
133
|
+
return undefined;
|
|
134
|
+
},
|
|
135
|
+
chatContainer,
|
|
136
|
+
pendingTools: new Map(),
|
|
137
|
+
ui: {
|
|
138
|
+
requestRender() {
|
|
139
|
+
renderCount++;
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
} as any;
|
|
143
|
+
|
|
144
|
+
for (const [toolCallId, toolName] of [
|
|
145
|
+
["read-1", "read"],
|
|
146
|
+
["read-2", "read"],
|
|
147
|
+
["edit-1", "edit"],
|
|
148
|
+
] as const) {
|
|
149
|
+
await handleAgentEvent(host, {
|
|
150
|
+
type: "tool_execution_start",
|
|
151
|
+
toolCallId,
|
|
152
|
+
toolName,
|
|
153
|
+
args: { path: `/tmp/${toolCallId}.txt` },
|
|
154
|
+
} as any);
|
|
155
|
+
await handleAgentEvent(host, {
|
|
156
|
+
type: "tool_execution_end",
|
|
157
|
+
toolCallId,
|
|
158
|
+
toolName,
|
|
159
|
+
result: { content: [], isError: false },
|
|
160
|
+
isError: false,
|
|
161
|
+
} as any);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const rendered = stripAnsi(chatContainer.render(100).join("\n"));
|
|
165
|
+
assert.match(rendered, /Context reads 2 actions\s+success · \d+(ms|s)/);
|
|
166
|
+
assert.match(rendered, /File changes 1 action\s+success · \d+(ms|s)/);
|
|
167
|
+
assert.doesNotMatch(rendered, /\bread\s+success ·/);
|
|
168
|
+
assert.ok(renderCount > 0);
|
|
169
|
+
});
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
// GSD-2 Interactive Chat Controller
|
|
1
2
|
import { Loader, Markdown, Spacer, Text } from "@gsd/pi-tui";
|
|
2
3
|
|
|
3
4
|
import type { InteractiveModeEvent, InteractiveModeStateHost } from "../interactive-mode-state.js";
|
|
4
5
|
import { theme } from "../theme/theme.js";
|
|
5
6
|
import { AssistantMessageComponent } from "../components/assistant-message.js";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ToolExecutionComponent,
|
|
9
|
+
ToolPhaseSummaryComponent,
|
|
10
|
+
type ToolExecutionPhase,
|
|
11
|
+
} from "../components/tool-execution.js";
|
|
7
12
|
import { DynamicBorder } from "../components/dynamic-border.js";
|
|
8
13
|
import { appKey } from "../components/keybinding-hints.js";
|
|
9
14
|
|
|
@@ -23,7 +28,8 @@ type RenderedSegment =
|
|
|
23
28
|
contentType: "text" | "thinking";
|
|
24
29
|
component: AssistantMessageComponent;
|
|
25
30
|
}
|
|
26
|
-
| { kind: "tool"; contentIndex: number; component: ToolExecutionComponent }
|
|
31
|
+
| { kind: "tool"; contentIndex: number; component: ToolExecutionComponent }
|
|
32
|
+
| { kind: "tool-summary"; component: ToolPhaseSummaryComponent; phases: ToolExecutionPhase[] };
|
|
27
33
|
|
|
28
34
|
let renderedSegments: RenderedSegment[] = [];
|
|
29
35
|
// When providers reuse one assistant lifecycle across internal sub-turns,
|
|
@@ -100,6 +106,84 @@ let pinnedBorder: DynamicBorder | undefined;
|
|
|
100
106
|
// Reference to the pinned markdown component below the border
|
|
101
107
|
let pinnedTextComponent: Markdown | undefined;
|
|
102
108
|
|
|
109
|
+
function mergeToolPhases(phases: ToolExecutionPhase[]): ToolExecutionPhase[] {
|
|
110
|
+
const merged: ToolExecutionPhase[] = [];
|
|
111
|
+
for (const phase of phases) {
|
|
112
|
+
const previous = merged[merged.length - 1];
|
|
113
|
+
if (previous?.label === phase.label) {
|
|
114
|
+
previous.count += phase.count;
|
|
115
|
+
previous.durationMs += phase.durationMs;
|
|
116
|
+
} else {
|
|
117
|
+
merged.push({ ...phase });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return merged;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function replaceCompactToolRowsWithPhaseSummary(
|
|
124
|
+
host: InteractiveModeStateHost & { ui: { requestRender: () => void } },
|
|
125
|
+
): void {
|
|
126
|
+
let changed = false;
|
|
127
|
+
const nextRenderedSegments: RenderedSegment[] = [];
|
|
128
|
+
let rollupRun: Array<{
|
|
129
|
+
seg: Extract<RenderedSegment, { kind: "tool" | "tool-summary" }>;
|
|
130
|
+
phases: ToolExecutionPhase[];
|
|
131
|
+
}> = [];
|
|
132
|
+
|
|
133
|
+
const flushRollupRun = () => {
|
|
134
|
+
const actionCount = rollupRun.reduce(
|
|
135
|
+
(total, item) => total + item.phases.reduce((sum, phase) => sum + phase.count, 0),
|
|
136
|
+
0,
|
|
137
|
+
);
|
|
138
|
+
if (actionCount < 2) {
|
|
139
|
+
nextRenderedSegments.push(...rollupRun.map((item) => item.seg));
|
|
140
|
+
rollupRun = [];
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const firstIndex = Math.max(0, host.chatContainer.children.indexOf(rollupRun[0].seg.component));
|
|
145
|
+
const phases = mergeToolPhases(rollupRun.flatMap((item) => item.phases));
|
|
146
|
+
const summary = new ToolPhaseSummaryComponent(phases);
|
|
147
|
+
|
|
148
|
+
for (const { seg } of rollupRun) {
|
|
149
|
+
host.chatContainer.removeChild(seg.component);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
host.chatContainer.addChild(summary);
|
|
153
|
+
const summaryIndex = host.chatContainer.children.indexOf(summary);
|
|
154
|
+
if (summaryIndex !== -1 && summaryIndex !== firstIndex) {
|
|
155
|
+
host.chatContainer.children.splice(summaryIndex, 1);
|
|
156
|
+
host.chatContainer.children.splice(firstIndex, 0, summary);
|
|
157
|
+
(host.chatContainer as unknown as { _prevRender: string[] | null })._prevRender = null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
changed = true;
|
|
161
|
+
nextRenderedSegments.push({ kind: "tool-summary", component: summary, phases });
|
|
162
|
+
rollupRun = [];
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
for (const seg of renderedSegments) {
|
|
166
|
+
const phase = seg.kind === "tool" ? seg.component.getRollupPhase() : null;
|
|
167
|
+
if (seg.kind === "tool" && phase) {
|
|
168
|
+
rollupRun.push({ seg, phases: [phase] });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (seg.kind === "tool-summary") {
|
|
172
|
+
rollupRun.push({ seg, phases: seg.component.getPhases() });
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
flushRollupRun();
|
|
177
|
+
nextRenderedSegments.push(seg);
|
|
178
|
+
}
|
|
179
|
+
flushRollupRun();
|
|
180
|
+
|
|
181
|
+
if (changed) {
|
|
182
|
+
renderedSegments = nextRenderedSegments;
|
|
183
|
+
host.ui.requestRender();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
103
187
|
export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
104
188
|
init: () => Promise<void>;
|
|
105
189
|
getMarkdownThemeWithSettings: () => any;
|
|
@@ -339,6 +423,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
339
423
|
details: externalToolResult.details,
|
|
340
424
|
isError: externalToolResult.isError,
|
|
341
425
|
});
|
|
426
|
+
replaceCompactToolRowsWithPhaseSummary(host);
|
|
342
427
|
}
|
|
343
428
|
}
|
|
344
429
|
|
|
@@ -791,6 +876,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
791
876
|
for (const [, component] of host.pendingTools.entries()) {
|
|
792
877
|
component.setArgsComplete();
|
|
793
878
|
}
|
|
879
|
+
replaceCompactToolRowsWithPhaseSummary(host);
|
|
794
880
|
}
|
|
795
881
|
host.streamingComponent = undefined;
|
|
796
882
|
host.streamingMessage = undefined;
|
|
@@ -823,6 +909,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
823
909
|
component.setExpanded(host.toolOutputExpanded);
|
|
824
910
|
host.chatContainer.addChild(component);
|
|
825
911
|
host.pendingTools.set(event.toolCallId, component);
|
|
912
|
+
renderedSegments.push({ kind: "tool", contentIndex: Number.MAX_SAFE_INTEGER, component });
|
|
826
913
|
host.ui.requestRender();
|
|
827
914
|
}
|
|
828
915
|
break;
|
|
@@ -840,7 +927,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
840
927
|
const component = host.pendingTools.get(event.toolCallId);
|
|
841
928
|
if (component) {
|
|
842
929
|
component.updateResult({ ...event.result, isError: event.isError });
|
|
843
|
-
host
|
|
930
|
+
replaceCompactToolRowsWithPhaseSummary(host);
|
|
844
931
|
host.ui.requestRender();
|
|
845
932
|
}
|
|
846
933
|
break;
|
|
@@ -856,6 +943,7 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
856
943
|
host.streamingComponent.setShowMetadata(true);
|
|
857
944
|
host.streamingComponent.updateContent(host.streamingMessage);
|
|
858
945
|
}
|
|
946
|
+
replaceCompactToolRowsWithPhaseSummary(host);
|
|
859
947
|
host.streamingComponent = undefined;
|
|
860
948
|
host.streamingMessage = undefined;
|
|
861
949
|
renderedSegments = [];
|
|
@@ -1378,6 +1378,9 @@ export class InteractiveMode {
|
|
|
1378
1378
|
})();
|
|
1379
1379
|
},
|
|
1380
1380
|
getSystemPrompt: () => this.session.systemPrompt,
|
|
1381
|
+
setCompactionThresholdOverride: (percent) => {
|
|
1382
|
+
this.session.settingsManager.setCompactionThresholdOverride(percent);
|
|
1383
|
+
},
|
|
1381
1384
|
});
|
|
1382
1385
|
|
|
1383
1386
|
// Set up the extension shortcut handler on the default editor
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// GSD2 TUI - Built-in terminal theme definitions.
|
|
1
2
|
/**
|
|
2
3
|
* Built-in theme definitions.
|
|
3
4
|
*
|
|
@@ -18,27 +19,30 @@ import type { ThemeJson } from "./theme-schema.js";
|
|
|
18
19
|
const dark: ThemeJson = {
|
|
19
20
|
name: "dark",
|
|
20
21
|
vars: {
|
|
21
|
-
cyan: "#
|
|
22
|
-
blue: "#
|
|
23
|
-
green: "#
|
|
24
|
-
red: "#
|
|
25
|
-
yellow: "#
|
|
26
|
-
gray: "#
|
|
27
|
-
dimGray: "#
|
|
28
|
-
darkGray: "#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
cyan: "#5cc8c8",
|
|
23
|
+
blue: "#79b8ff",
|
|
24
|
+
green: "#7ad38b",
|
|
25
|
+
red: "#f27b7b",
|
|
26
|
+
yellow: "#d6b85f",
|
|
27
|
+
gray: "#87949b",
|
|
28
|
+
dimGray: "#657178",
|
|
29
|
+
darkGray: "#303a40",
|
|
30
|
+
line: "#52616a",
|
|
31
|
+
lineSoft: "#303a40",
|
|
32
|
+
textSoft: "#cbd6dc",
|
|
33
|
+
accent: "#5cc8c8",
|
|
34
|
+
selectedBg: "#1d282a",
|
|
35
|
+
userMsgBg: "#202a2f",
|
|
36
|
+
toolPendingBg: "#252721",
|
|
37
|
+
toolSuccessBg: "#1c251f",
|
|
38
|
+
toolErrorBg: "#251e20",
|
|
39
|
+
customMsgBg: "#202a2f",
|
|
36
40
|
},
|
|
37
41
|
colors: {
|
|
38
42
|
accent: "accent",
|
|
39
|
-
border: "
|
|
40
|
-
borderAccent: "
|
|
41
|
-
borderMuted: "
|
|
43
|
+
border: "line",
|
|
44
|
+
borderAccent: "accent",
|
|
45
|
+
borderMuted: "lineSoft",
|
|
42
46
|
success: "green",
|
|
43
47
|
error: "red",
|
|
44
48
|
warning: "yellow",
|
|
@@ -49,16 +53,21 @@ const dark: ThemeJson = {
|
|
|
49
53
|
|
|
50
54
|
selectedBg: "selectedBg",
|
|
51
55
|
userMessageBg: "userMsgBg",
|
|
52
|
-
userMessageText: "
|
|
53
|
-
assistantMessageText: "",
|
|
56
|
+
userMessageText: "textSoft",
|
|
57
|
+
assistantMessageText: "textSoft",
|
|
54
58
|
customMessageBg: "customMsgBg",
|
|
55
|
-
customMessageText: "",
|
|
56
|
-
customMessageLabel: "
|
|
59
|
+
customMessageText: "textSoft",
|
|
60
|
+
customMessageLabel: "blue",
|
|
57
61
|
toolPendingBg: "toolPendingBg",
|
|
58
62
|
toolSuccessBg: "toolSuccessBg",
|
|
59
63
|
toolErrorBg: "toolErrorBg",
|
|
60
|
-
toolTitle: "
|
|
61
|
-
toolOutput: "
|
|
64
|
+
toolTitle: "gray",
|
|
65
|
+
toolOutput: "textSoft",
|
|
66
|
+
surfaceTitle: "accent",
|
|
67
|
+
surfaceAccent: "accent",
|
|
68
|
+
toolRunning: "yellow",
|
|
69
|
+
toolSuccess: "green",
|
|
70
|
+
toolError: "red",
|
|
62
71
|
|
|
63
72
|
mdHeading: "#fbbf24",
|
|
64
73
|
mdLink: "#93c5fd",
|
|
@@ -95,9 +104,9 @@ const dark: ThemeJson = {
|
|
|
95
104
|
bashMode: "green",
|
|
96
105
|
},
|
|
97
106
|
export: {
|
|
98
|
-
pageBg: "#
|
|
99
|
-
cardBg: "#
|
|
100
|
-
infoBg: "#
|
|
107
|
+
pageBg: "#111416",
|
|
108
|
+
cardBg: "#1e2529",
|
|
109
|
+
infoBg: "#252721",
|
|
101
110
|
},
|
|
102
111
|
};
|
|
103
112
|
|
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
4
|
import { describe, test } from "node:test";
|
|
5
|
+
import stripAnsi from "strip-ansi";
|
|
5
6
|
|
|
7
|
+
import { AdaptiveLayoutComponent } from "./components/adaptive-layout.js";
|
|
8
|
+
import { initTheme } from "./theme/theme.js";
|
|
6
9
|
import { resolveTuiMode } from "./tui-mode.js";
|
|
7
10
|
|
|
11
|
+
initTheme("dark", false);
|
|
12
|
+
|
|
8
13
|
describe("resolveTuiMode", () => {
|
|
9
14
|
test("explicit overrides beat auto selection", () => {
|
|
10
15
|
assert.equal(
|
|
@@ -38,3 +43,23 @@ describe("resolveTuiMode", () => {
|
|
|
38
43
|
assert.equal(resolveTuiMode({ terminalWidth: 100 }), "chat");
|
|
39
44
|
});
|
|
40
45
|
});
|
|
46
|
+
|
|
47
|
+
describe("AdaptiveLayoutComponent", () => {
|
|
48
|
+
test("renders workflow layout with prototype rule frames", () => {
|
|
49
|
+
const layout = new AdaptiveLayoutComponent(() => ({
|
|
50
|
+
override: "workflow",
|
|
51
|
+
activeToolCount: 2,
|
|
52
|
+
gsdPhase: "execute-task",
|
|
53
|
+
sessionName: "main",
|
|
54
|
+
cwd: "/Users/example/project",
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
const plain = layout.render(120).map(stripAnsi);
|
|
58
|
+
|
|
59
|
+
assert.match(plain[0], /^─+/, "workflow layout should start with a rule frame");
|
|
60
|
+
assert.ok(plain.some((line) => line.includes("GSD Command Center")), "workflow title should render");
|
|
61
|
+
assert.ok(plain.some((line) => line.includes("signals")), "inspector title should render");
|
|
62
|
+
assert.ok(plain.some((line) => line.includes("│ Active")), "body rows should keep prototype gutter");
|
|
63
|
+
assert.ok(!plain.some((line) => /[╭╮╰╯]/.test(line)), "workflow layout should not use rounded box corners");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
|
|
6
|
+
test("MemoryStorage persists sql.js snapshots through the atomic DB snapshot writer", () => {
|
|
7
|
+
const src = readFileSync(
|
|
8
|
+
join(process.cwd(), "packages", "pi-coding-agent", "src", "resources", "extensions", "memory", "storage.ts"),
|
|
9
|
+
"utf-8",
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
assert.match(src, /atomicWriteDbSnapshotSync\(/, "storage must use atomic DB snapshot writes");
|
|
13
|
+
assert.doesNotMatch(src, /writeFileSync\(this\.dbPath/, "direct live DB overwrite is forbidden");
|
|
14
|
+
});
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
import initSqlJs, { type Database as SqlJsDatabase } from "sql.js";
|
|
11
11
|
import { randomUUID } from "crypto";
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync } from "fs";
|
|
13
13
|
import { dirname } from "path";
|
|
14
|
+
import { atomicWriteDbSnapshotSync } from "../../../core/db-snapshot.js";
|
|
14
15
|
|
|
15
16
|
export interface ThreadRow {
|
|
16
17
|
thread_id: string;
|
|
@@ -74,7 +75,7 @@ export class MemoryStorage {
|
|
|
74
75
|
|
|
75
76
|
private persist(): void {
|
|
76
77
|
const data = this.db.export();
|
|
77
|
-
|
|
78
|
+
atomicWriteDbSnapshotSync(this.dbPath, data);
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
private schedulePersist(): void {
|