@selesai/code 0.13.12 → 0.13.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +77 -1
- package/dist/extensions/pi-subagents/VISION.md +12 -1
- package/dist/extensions/pi-subagents/docs/agents.md +13 -6
- package/dist/extensions/pi-subagents/docs/configuration.md +35 -9
- package/dist/extensions/pi-subagents/docs/extension-api.md +1 -1
- package/dist/extensions/pi-subagents/docs/missions.md +1 -1
- package/dist/extensions/pi-subagents/docs/models.md +6 -6
- package/dist/extensions/pi-subagents/docs/observability.md +6 -3
- package/dist/extensions/pi-subagents/docs/tool-reference.md +3 -3
- package/dist/extensions/pi-subagents/docs/watchdog.md +92 -114
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/install.mjs +1 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +6 -6
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +4 -4
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/review-and-validation.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -4
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +120 -124
- package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +5 -1
- package/dist/extensions/pi-subagents/src/api/preflight.ts +4 -0
- package/dist/extensions/pi-subagents/src/api/shared-types.ts +3 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +20 -0
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +1 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +6 -2
- package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
- package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +26 -8
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +103 -23
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -2
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +54 -3
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +16 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +22 -2
- package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +63 -6
- package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -1
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +69 -12
- package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +3 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +21 -8
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +59 -6
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +95 -18
- package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +11 -43
- package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +24 -3
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +4 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -6
- package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +1 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +33 -18
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +73 -37
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +33 -6
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +21 -7
- package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +1 -1
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +467 -63
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +3 -1
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +0 -12
- package/dist/extensions/pi-subagents/src/shared/fork-session-cwd.ts +27 -0
- package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +3 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +42 -4
- package/dist/extensions/pi-subagents/src/shared/utils.ts +18 -7
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1 -1
- package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +26 -12
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +61 -2
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +12 -7
- package/dist/extensions/pi-subagents/src/tui/render.ts +227 -14
- package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +56 -34
- package/dist/extensions/pi-subagents/src/watchdog/diff-tool.ts +77 -0
- package/dist/extensions/pi-subagents/src/watchdog/emission-guard.ts +5 -3
- package/dist/extensions/pi-subagents/src/watchdog/guidance.ts +20 -0
- package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +16 -13
- package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +10 -9
- package/dist/extensions/pi-subagents/src/watchdog/render.ts +4 -5
- package/dist/extensions/pi-subagents/src/watchdog/review.ts +15 -4
- package/dist/extensions/pi-subagents/src/watchdog/rules.ts +70 -0
- package/dist/extensions/pi-subagents/src/watchdog/runtime.ts +75 -92
- package/dist/extensions/pi-subagents/src/watchdog/scope.ts +2 -1
- package/dist/extensions/pi-subagents/src/watchdog/settings.ts +48 -104
- package/dist/extensions/pi-subagents/src/watchdog/types.ts +18 -32
- package/dist/extensions/pi-subagents/src/watchdog/warning-format.ts +0 -1
- package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -2
- package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +439 -0
- package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +28 -1
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +335 -21
- package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +22 -18
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +21 -18
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +307 -22
- package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/support/helpers.ts +25 -0
- package/dist/extensions/pi-subagents/test/support/isolated-temp-root.mjs +15 -4
- package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +17 -2
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +96 -15
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +53 -5
- package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +221 -8
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +45 -24
- package/dist/extensions/pi-subagents/test/unit/agent-scan-dirs.test.ts +133 -0
- package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +28 -2
- package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
- package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +4 -1
- package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +18 -4
- package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +16 -0
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +38 -0
- package/dist/extensions/pi-subagents/test/unit/default-extensions.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +21 -0
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +19 -6
- package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +26 -0
- package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +22 -1
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +2 -6
- package/dist/extensions/pi-subagents/test/unit/notify.test.ts +66 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args-permission-system.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +116 -10
- package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +136 -18
- package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +20 -0
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +1 -0
- package/dist/extensions/pi-subagents/test/unit/project-panes-public-api.test.ts +8 -0
- package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +127 -48
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +68 -0
- package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +17 -0
- package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +88 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -2
- package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +9 -9
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/steering.test.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +22 -9
- package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +91 -8
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +27 -0
- package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +50 -0
- package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +0 -1
- package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +34 -0
- package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -2
- package/dist/extensions/pi-subagents/test/unit/watchdog-child-status.test.ts +73 -13
- package/dist/extensions/pi-subagents/test/unit/watchdog-diff-tool.test.ts +79 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-permission-arbiter.test.ts +2 -4
- package/dist/extensions/pi-subagents/test/unit/watchdog-render.test.ts +7 -8
- package/dist/extensions/pi-subagents/test/unit/watchdog-review.test.ts +48 -3
- package/dist/extensions/pi-subagents/test/unit/watchdog-rules.test.ts +37 -0
- package/dist/extensions/pi-subagents/test/unit/watchdog-runtime.test.ts +36 -118
- package/dist/extensions/pi-subagents/test/unit/watchdog-scope.test.ts +1 -1
- package/dist/extensions/pi-subagents/test/unit/watchdog-settings.test.ts +37 -14
- package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +45 -26
- package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +223 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +50 -1
- package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +136 -4
- package/dist/extensions/pi-zentui/README.md +19 -5
- package/dist/extensions/pi-zentui/docs/configuration.md +37 -9
- package/dist/extensions/pi-zentui/extensions/zentui/config.ts +29 -0
- package/dist/extensions/pi-zentui/extensions/zentui/index.ts +31 -2
- package/dist/extensions/pi-zentui/extensions/zentui/prototype-patch-registry.ts +55 -11
- package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +115 -3
- package/dist/extensions/pi-zentui/extensions/zentui/settings-previews.ts +119 -2
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-experimental.ts +1768 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-status.ts +69 -0
- package/dist/extensions/pi-zentui/extensions/zentui/thinking-steps.ts +155 -0
- package/dist/extensions/pi-zentui/extensions/zentui/ui.ts +2 -1
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-osc.ts +16 -2
- package/dist/extensions/pi-zentui/extensions/zentui/user-message-styles.ts +1 -1
- package/dist/extensions/pi-zentui/test/config-load-lifecycle.test.ts +124 -0
- package/dist/extensions/pi-zentui/test/config.test.ts +82 -2
- package/dist/extensions/pi-zentui/test/editor-viewport-indicators.test.ts +19 -4
- package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +58 -9
- package/dist/extensions/pi-zentui/test/package-contents.mjs +33 -0
- package/dist/extensions/pi-zentui/test/prototype-patch-registry.test.ts +131 -0
- package/dist/extensions/pi-zentui/test/responsive-dependencies.test.ts +4 -4
- package/dist/extensions/pi-zentui/test/settings-command.test.ts +289 -7
- package/dist/extensions/pi-zentui/test/settings-previews.test.ts +106 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-docs.test.ts +51 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-loader-diagnostics.mjs +54 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-missing-export.test.ts +44 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-rows.test.ts +297 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental-tui.mjs +2329 -0
- package/dist/extensions/pi-zentui/test/thinking-experimental.test.ts +1668 -0
- package/dist/extensions/pi-zentui/test/thinking-status.test.ts +85 -0
- package/dist/extensions/pi-zentui/test/thinking-steps.test.ts +122 -0
- package/dist/extensions/pi-zentui/test/user-message-osc.test.ts +58 -0
- package/dist/skills/brandkit/SKILL.md +0 -2
- package/dist/skills/{industrial-brutalist-ui → brutalist}/SKILL.md +1 -3
- package/dist/skills/gpt-taste/SKILL.md +0 -2
- package/dist/skills/image-to-code/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -2
- package/dist/skills/imagegen-frontend-web/SKILL.md +0 -2
- package/dist/skills/{minimalist-ui → minimalist}/SKILL.md +1 -3
- package/dist/skills/{full-output-enforcement → output}/SKILL.md +1 -3
- package/dist/skills/{redesign-existing-projects → redesign}/SKILL.md +1 -3
- package/dist/skills/{high-end-visual-design → soft}/SKILL.md +1 -3
- package/dist/skills/stitch/DESIGN.md +121 -0
- package/dist/skills/{stitch-design-taste → stitch}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend → taste}/SKILL.md +1 -3
- package/dist/skills/{design-taste-frontend-v1 → taste-v1}/SKILL.md +2 -4
- package/package.json +1 -1
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
type FooterComponentConfig,
|
|
9
9
|
type PolishedTuiConfig,
|
|
10
10
|
type SelectorBordersComponentConfig,
|
|
11
|
+
type ThinkingStepsComponentConfig,
|
|
12
|
+
type ThinkingStepsMode,
|
|
11
13
|
type UserMessagesComponentConfig,
|
|
12
14
|
type WorkingLineComponentPatch,
|
|
13
15
|
} from "../extensions/zentui/config";
|
|
@@ -23,6 +25,7 @@ const sectionNames = [
|
|
|
23
25
|
"Appearance",
|
|
24
26
|
"Editor",
|
|
25
27
|
"User messages",
|
|
28
|
+
"Thinking",
|
|
26
29
|
"Working line",
|
|
27
30
|
"Footer",
|
|
28
31
|
"Segments",
|
|
@@ -121,6 +124,7 @@ function createHarness(
|
|
|
121
124
|
let command: Command | undefined;
|
|
122
125
|
let component: Component | undefined;
|
|
123
126
|
const notifications: string[] = [];
|
|
127
|
+
const notificationEvents: Array<{ message: string; severity: string }> = [];
|
|
124
128
|
let doneCalls = 0;
|
|
125
129
|
const sessionLifecycle = new SessionLifecycle();
|
|
126
130
|
const calls = {
|
|
@@ -129,6 +133,7 @@ function createHarness(
|
|
|
129
133
|
polishedCopyFriendly: [] as Array<Record<string, unknown>>,
|
|
130
134
|
accentRail: [] as Array<Record<string, unknown>>,
|
|
131
135
|
messages: [] as Partial<UserMessagesComponentConfig>[],
|
|
136
|
+
thinkingSteps: [] as Partial<ThinkingStepsComponentConfig>[],
|
|
132
137
|
workingLine: [] as WorkingLineComponentPatch[],
|
|
133
138
|
renders: { shared: 0, local: 0 },
|
|
134
139
|
selectors: [] as Partial<SelectorBordersComponentConfig>[],
|
|
@@ -169,6 +174,12 @@ function createHarness(
|
|
|
169
174
|
calls.messages.push(patch);
|
|
170
175
|
Object.assign(config.components.userMessages, patch);
|
|
171
176
|
},
|
|
177
|
+
thinkingStepsCapability: { available: true },
|
|
178
|
+
setThinkingStepsComponent(patch: Partial<ThinkingStepsComponentConfig>) {
|
|
179
|
+
calls.thinkingSteps.push(patch);
|
|
180
|
+
Object.assign(config.components.thinkingSteps, patch);
|
|
181
|
+
return { applied: true };
|
|
182
|
+
},
|
|
172
183
|
setWorkingLineComponent(patch: WorkingLineComponentPatch) {
|
|
173
184
|
calls.workingLine.push(patch);
|
|
174
185
|
const { messages, segments, ...componentPatch } = patch;
|
|
@@ -240,8 +251,9 @@ function createHarness(
|
|
|
240
251
|
cwd: process.cwd(),
|
|
241
252
|
ui: {
|
|
242
253
|
theme: theme(),
|
|
243
|
-
notify(message: string) {
|
|
254
|
+
notify(message: string, severity = "info") {
|
|
244
255
|
notifications.push(message);
|
|
256
|
+
notificationEvents.push({ message, severity });
|
|
245
257
|
},
|
|
246
258
|
async custom(factory: (...args: unknown[]) => unknown) {
|
|
247
259
|
component = factory(
|
|
@@ -276,6 +288,7 @@ function createHarness(
|
|
|
276
288
|
ctx,
|
|
277
289
|
calls,
|
|
278
290
|
notifications,
|
|
291
|
+
notificationEvents,
|
|
279
292
|
sessionLifecycle,
|
|
280
293
|
doneCalls: () => doneCalls,
|
|
281
294
|
};
|
|
@@ -286,7 +299,7 @@ afterEach(() => {
|
|
|
286
299
|
});
|
|
287
300
|
|
|
288
301
|
describe("component-oriented /zentui settings", () => {
|
|
289
|
-
it("uses the exact
|
|
302
|
+
it("uses the exact nine-section order in wide and narrow navigation", async () => {
|
|
290
303
|
const harness = createHarness();
|
|
291
304
|
await harness.command().handler("", harness.ctx);
|
|
292
305
|
const component = harness.component();
|
|
@@ -300,7 +313,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
300
313
|
for (const [index, name] of sectionNames.entries()) {
|
|
301
314
|
const lines = component.render(40);
|
|
302
315
|
expect(lines[1]).toContain(name);
|
|
303
|
-
expect(lines[1]).toContain(`(${index + 1}/
|
|
316
|
+
expect(lines[1]).toContain(`(${index + 1}/9)`);
|
|
304
317
|
expect(lines.every((line) => visibleWidth(line) <= 40)).toBe(true);
|
|
305
318
|
component.handleInput("\t");
|
|
306
319
|
}
|
|
@@ -344,6 +357,8 @@ describe("component-oriented /zentui settings", () => {
|
|
|
344
357
|
component.handleInput("\t");
|
|
345
358
|
expectFocusOrder(component, ["User messages", "Message style", "Message colors"]);
|
|
346
359
|
component.handleInput("\t");
|
|
360
|
+
expectFocusOrder(component, ["Enabled", "Mode"]);
|
|
361
|
+
component.handleInput("\t");
|
|
347
362
|
expectFocusOrder(component, [
|
|
348
363
|
"Enabled",
|
|
349
364
|
"Turn summary",
|
|
@@ -356,7 +371,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
356
371
|
"Custom messages",
|
|
357
372
|
"Tool",
|
|
358
373
|
"Elapsed",
|
|
359
|
-
"Thinking",
|
|
374
|
+
"Thinking time",
|
|
360
375
|
"Tokens",
|
|
361
376
|
"Message list",
|
|
362
377
|
]);
|
|
@@ -533,7 +548,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
533
548
|
{ showGit: false },
|
|
534
549
|
]);
|
|
535
550
|
|
|
536
|
-
for (let index = 0; index <
|
|
551
|
+
for (let index = 0; index < 6; index += 1) component.handleInput("\t");
|
|
537
552
|
for (const [label, value] of [
|
|
538
553
|
["Commit only on detached HEAD", "disabled"],
|
|
539
554
|
["Show exact-match tag", "disabled"],
|
|
@@ -691,6 +706,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
691
706
|
component.handleInput(" ");
|
|
692
707
|
component.handleInput("\t");
|
|
693
708
|
component.handleInput("\t");
|
|
709
|
+
component.handleInput("\t");
|
|
694
710
|
selectLabel(component, "Footer colors");
|
|
695
711
|
component.handleInput(" ");
|
|
696
712
|
selectLabel(component, "Footer model label");
|
|
@@ -774,6 +790,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
774
790
|
component.handleInput("\t");
|
|
775
791
|
component.handleInput("\t");
|
|
776
792
|
component.handleInput("\t");
|
|
793
|
+
component.handleInput("\t");
|
|
777
794
|
selectLabel(component, "Ignore submodules");
|
|
778
795
|
component.handleInput(" ");
|
|
779
796
|
component.handleInput("\x1b[Z");
|
|
@@ -792,6 +809,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
792
809
|
component.handleInput("\t");
|
|
793
810
|
component.handleInput("\t");
|
|
794
811
|
component.handleInput("\t");
|
|
812
|
+
component.handleInput("\t");
|
|
795
813
|
expect(row(component, "Ignore submodules")).toContain("enabled");
|
|
796
814
|
component.handleInput("\x1b[Z");
|
|
797
815
|
expect(row(component, "Model info")).toContain("enabled");
|
|
@@ -823,6 +841,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
823
841
|
component.handleInput("\t");
|
|
824
842
|
component.handleInput("\t");
|
|
825
843
|
component.handleInput("\t");
|
|
844
|
+
component.handleInput("\t");
|
|
826
845
|
selectLabel(component, "Model info");
|
|
827
846
|
component.handleInput(" ");
|
|
828
847
|
expect(focusedRow(component)).toContain("> Model info");
|
|
@@ -835,6 +854,267 @@ describe("component-oriented /zentui settings", () => {
|
|
|
835
854
|
]);
|
|
836
855
|
});
|
|
837
856
|
|
|
857
|
+
it("shows Thinking (Experimental) with only Enabled and Mode focus rows", async () => {
|
|
858
|
+
const harness = createHarness();
|
|
859
|
+
await harness.command().handler("", harness.ctx);
|
|
860
|
+
const component = harness.component();
|
|
861
|
+
goToSection(component, "Thinking");
|
|
862
|
+
expect(component.render(120).join("\n")).toContain("Thinking (Experimental)");
|
|
863
|
+
expectFocusOrder(component, ["Enabled", "Mode"]);
|
|
864
|
+
expect(component.render(120).join("\n")).toContain(
|
|
865
|
+
"Live switching supports Streaming → Rail/Tree and Rail ↔ Tree. Entering Streaming,",
|
|
866
|
+
);
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
it("shows saved and active mode mismatch honestly", async () => {
|
|
870
|
+
const config = cloneConfig();
|
|
871
|
+
config.components.thinkingSteps.enabled = true;
|
|
872
|
+
config.components.thinkingSteps.mode = "streaming";
|
|
873
|
+
const harness = createHarness(config, {
|
|
874
|
+
thinkingStepsCapability: {
|
|
875
|
+
state: {
|
|
876
|
+
available: true,
|
|
877
|
+
rendererAvailable: true,
|
|
878
|
+
streamingAvailable: false,
|
|
879
|
+
active: true,
|
|
880
|
+
activeMode: "tree",
|
|
881
|
+
startup: { enabled: true, mode: "tree" },
|
|
882
|
+
displaced: false,
|
|
883
|
+
restartRequired: true,
|
|
884
|
+
reason: "Streaming listener unavailable; restart required",
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
});
|
|
888
|
+
await harness.command().handler("", harness.ctx);
|
|
889
|
+
const component = harness.component();
|
|
890
|
+
goToSection(component, "Thinking");
|
|
891
|
+
const output = component.render(160).join("\n");
|
|
892
|
+
expect(output).toContain(
|
|
893
|
+
"Saved: Streaming · Active: Tree · Streaming unavailable · restart required · Streaming listener unavailable",
|
|
894
|
+
);
|
|
895
|
+
expect(output.match(/restart required/gi)).toHaveLength(2);
|
|
896
|
+
// One status appears in the preview and one in the focused setting description;
|
|
897
|
+
// neither duplicates reason text that already contained the phrase.
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
it("keeps private-renderer unavailability non-focusable and fails open to native", async () => {
|
|
901
|
+
const harness = createHarness(cloneConfig(), {
|
|
902
|
+
thinkingStepsCapability: { available: false },
|
|
903
|
+
});
|
|
904
|
+
await harness.command().handler("", harness.ctx);
|
|
905
|
+
const component = harness.component();
|
|
906
|
+
goToSection(component, "Thinking");
|
|
907
|
+
const output = component.render(120).join("\n");
|
|
908
|
+
expect(output).toContain("Saved: Disabled · Active: Native · Renderer unavailable");
|
|
909
|
+
expectFocusOrder(component, ["Enabled", "Mode"]);
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
it("routes Thinking-step enablement and mode independently", async () => {
|
|
913
|
+
const harness = createHarness();
|
|
914
|
+
await harness.command().handler("", harness.ctx);
|
|
915
|
+
const component = harness.component();
|
|
916
|
+
goToSection(component, "Thinking");
|
|
917
|
+
expectFocusOrder(component, ["Enabled", "Mode"]);
|
|
918
|
+
selectLabel(component, "Enabled");
|
|
919
|
+
component.handleInput(" ");
|
|
920
|
+
selectLabel(component, "Mode");
|
|
921
|
+
component.handleInput(" ");
|
|
922
|
+
expect(harness.calls.thinkingSteps).toEqual([{ enabled: true }, { mode: "rail" }]);
|
|
923
|
+
expect(harness.config.components.thinkingSteps).toEqual({
|
|
924
|
+
enabled: true,
|
|
925
|
+
mode: "rail",
|
|
926
|
+
});
|
|
927
|
+
expect(harness.notifications).toContain("Thinking (Experimental): Rail");
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it("cycles the real mode setting through Streaming to Tree", async () => {
|
|
931
|
+
const config = cloneConfig();
|
|
932
|
+
config.components.thinkingSteps = { enabled: true, mode: "streaming" };
|
|
933
|
+
const harness = createHarness(config);
|
|
934
|
+
await harness.command().handler("", harness.ctx);
|
|
935
|
+
const component = harness.component();
|
|
936
|
+
goToSection(component, "Thinking");
|
|
937
|
+
selectLabel(component, "Mode");
|
|
938
|
+
component.handleInput(" ");
|
|
939
|
+
expect(harness.calls.thinkingSteps).toEqual([{ mode: "tree" }]);
|
|
940
|
+
expect(harness.config.components.thinkingSteps.mode).toBe("tree");
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it("rebuilds Thinking settings in focus with honest live-success status and preview", async () => {
|
|
944
|
+
const config = cloneConfig();
|
|
945
|
+
config.components.thinkingSteps = { enabled: true, mode: "streaming" };
|
|
946
|
+
const state = {
|
|
947
|
+
available: true,
|
|
948
|
+
active: true,
|
|
949
|
+
activeMode: "streaming" as ThinkingStepsMode,
|
|
950
|
+
startup: { enabled: true, mode: "streaming" as ThinkingStepsMode },
|
|
951
|
+
displaced: false,
|
|
952
|
+
restartRequired: false,
|
|
953
|
+
};
|
|
954
|
+
const harness = createHarness(config, {
|
|
955
|
+
thinkingStepsCapability: { state },
|
|
956
|
+
setThinkingStepsComponent(patch: Partial<ThinkingStepsComponentConfig>) {
|
|
957
|
+
Object.assign(config.components.thinkingSteps, patch);
|
|
958
|
+
if (patch.mode) state.activeMode = patch.mode;
|
|
959
|
+
return { applied: true };
|
|
960
|
+
},
|
|
961
|
+
});
|
|
962
|
+
await harness.command().handler("", harness.ctx);
|
|
963
|
+
const component = harness.component();
|
|
964
|
+
goToSection(component, "Thinking");
|
|
965
|
+
selectLabel(component, "Mode");
|
|
966
|
+
component.handleInput(" ");
|
|
967
|
+
const output = component.render(160).join("\n");
|
|
968
|
+
expect(focusedRow(component)).toContain("> Mode");
|
|
969
|
+
expect(output).toContain("Saved: Tree · Active: Tree");
|
|
970
|
+
expect(harness.notificationEvents.at(-1)).toEqual({
|
|
971
|
+
message: "Thinking (Experimental): Tree",
|
|
972
|
+
severity: "info",
|
|
973
|
+
});
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
it("rebuilds Thinking settings in focus with saved/active failure status and preview", async () => {
|
|
977
|
+
const config = cloneConfig();
|
|
978
|
+
config.components.thinkingSteps = { enabled: true, mode: "rail" };
|
|
979
|
+
const state = {
|
|
980
|
+
available: true,
|
|
981
|
+
rendererAvailable: true,
|
|
982
|
+
streamingAvailable: true,
|
|
983
|
+
active: true,
|
|
984
|
+
activeMode: "rail" as ThinkingStepsMode,
|
|
985
|
+
startup: { enabled: true, mode: "tree" as ThinkingStepsMode },
|
|
986
|
+
displaced: false,
|
|
987
|
+
restartRequired: false,
|
|
988
|
+
reason: undefined as string | undefined,
|
|
989
|
+
};
|
|
990
|
+
const harness = createHarness(config, {
|
|
991
|
+
thinkingStepsCapability: { state },
|
|
992
|
+
setThinkingStepsComponent(patch: Partial<ThinkingStepsComponentConfig>) {
|
|
993
|
+
Object.assign(config.components.thinkingSteps, patch);
|
|
994
|
+
state.restartRequired = true;
|
|
995
|
+
return {
|
|
996
|
+
applied: false,
|
|
997
|
+
reason: "Saved: Streaming · Active: Rail · restart required",
|
|
998
|
+
};
|
|
999
|
+
},
|
|
1000
|
+
});
|
|
1001
|
+
await harness.command().handler("", harness.ctx);
|
|
1002
|
+
const component = harness.component();
|
|
1003
|
+
goToSection(component, "Thinking");
|
|
1004
|
+
selectLabel(component, "Mode");
|
|
1005
|
+
component.handleInput(" ");
|
|
1006
|
+
const output = component.render(180).join("\n");
|
|
1007
|
+
expect(focusedRow(component)).toContain("> Mode");
|
|
1008
|
+
const expectedStatus = "Saved: Streaming · Active: Rail · restart required";
|
|
1009
|
+
expect(output).toContain(expectedStatus);
|
|
1010
|
+
expect(output).toContain("Thinking 7.1s");
|
|
1011
|
+
expect(harness.notificationEvents.at(-1)).toEqual({
|
|
1012
|
+
message: `Thinking (Experimental): Streaming (${expectedStatus})`,
|
|
1013
|
+
severity: "warning",
|
|
1014
|
+
});
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
it("includes a cleanup warning in a successful Thinking notification", async () => {
|
|
1018
|
+
const config = cloneConfig();
|
|
1019
|
+
config.components.thinkingSteps = { enabled: true, mode: "streaming" };
|
|
1020
|
+
const state = {
|
|
1021
|
+
available: true,
|
|
1022
|
+
rendererAvailable: true,
|
|
1023
|
+
streamingAvailable: true,
|
|
1024
|
+
active: true,
|
|
1025
|
+
activeMode: "streaming" as ThinkingStepsMode,
|
|
1026
|
+
startup: { enabled: true, mode: "streaming" as ThinkingStepsMode },
|
|
1027
|
+
displaced: false,
|
|
1028
|
+
restartRequired: false,
|
|
1029
|
+
reason: undefined as string | undefined,
|
|
1030
|
+
};
|
|
1031
|
+
const warning =
|
|
1032
|
+
"Saved: Tree · Active: Tree · Streaming unavailable · Pi's terminal input listener cleanup is unavailable";
|
|
1033
|
+
const harness = createHarness(config, {
|
|
1034
|
+
thinkingStepsCapability: { state },
|
|
1035
|
+
setThinkingStepsComponent(patch: Partial<ThinkingStepsComponentConfig>) {
|
|
1036
|
+
Object.assign(config.components.thinkingSteps, patch);
|
|
1037
|
+
state.activeMode = "tree";
|
|
1038
|
+
state.streamingAvailable = false;
|
|
1039
|
+
state.reason = "Pi's terminal input listener cleanup is unavailable";
|
|
1040
|
+
return { applied: true, reason: warning };
|
|
1041
|
+
},
|
|
1042
|
+
});
|
|
1043
|
+
await harness.command().handler("", harness.ctx);
|
|
1044
|
+
const component = harness.component();
|
|
1045
|
+
goToSection(component, "Thinking");
|
|
1046
|
+
selectLabel(component, "Mode");
|
|
1047
|
+
component.handleInput(" ");
|
|
1048
|
+
expect(component.render(180).join("\n")).toContain(warning);
|
|
1049
|
+
expect(harness.notificationEvents.at(-1)).toEqual({
|
|
1050
|
+
message: `Thinking (Experimental): Tree (${warning})`,
|
|
1051
|
+
severity: "warning",
|
|
1052
|
+
});
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it("shows a warning when disabling Streaming restores native output with degraded cleanup", async () => {
|
|
1056
|
+
const config = cloneConfig();
|
|
1057
|
+
config.components.thinkingSteps = { enabled: true, mode: "streaming" };
|
|
1058
|
+
const state = {
|
|
1059
|
+
available: true,
|
|
1060
|
+
rendererAvailable: true,
|
|
1061
|
+
streamingAvailable: true,
|
|
1062
|
+
active: true,
|
|
1063
|
+
activeMode: "streaming" as ThinkingStepsMode | undefined,
|
|
1064
|
+
startup: { enabled: true, mode: "streaming" as ThinkingStepsMode },
|
|
1065
|
+
displaced: false,
|
|
1066
|
+
restartRequired: false,
|
|
1067
|
+
reason: undefined as string | undefined,
|
|
1068
|
+
};
|
|
1069
|
+
const warning =
|
|
1070
|
+
"Saved: Disabled · Active: Native · Streaming unavailable · Pi's terminal input listener cleanup is unavailable";
|
|
1071
|
+
const harness = createHarness(config, {
|
|
1072
|
+
thinkingStepsCapability: { state },
|
|
1073
|
+
setThinkingStepsComponent(patch: Partial<ThinkingStepsComponentConfig>) {
|
|
1074
|
+
Object.assign(config.components.thinkingSteps, patch);
|
|
1075
|
+
state.active = false;
|
|
1076
|
+
state.activeMode = undefined;
|
|
1077
|
+
state.streamingAvailable = false;
|
|
1078
|
+
state.reason = "Pi's terminal input listener cleanup is unavailable";
|
|
1079
|
+
return { applied: true, reason: warning };
|
|
1080
|
+
},
|
|
1081
|
+
});
|
|
1082
|
+
await harness.command().handler("", harness.ctx);
|
|
1083
|
+
const component = harness.component();
|
|
1084
|
+
goToSection(component, "Thinking");
|
|
1085
|
+
selectLabel(component, "Enabled");
|
|
1086
|
+
component.handleInput(" ");
|
|
1087
|
+
expect(component.render(180).join("\n")).toContain(warning);
|
|
1088
|
+
expect(harness.notificationEvents.at(-1)).toEqual({
|
|
1089
|
+
message: `Thinking (Experimental): disabled (${warning})`,
|
|
1090
|
+
severity: "warning",
|
|
1091
|
+
});
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it("restores Thinking-step rows after persistence failure and exposes no direct route", async () => {
|
|
1095
|
+
const harness = createHarness(cloneConfig(), {
|
|
1096
|
+
setThinkingStepsComponent() {
|
|
1097
|
+
throw new Error("read-only thinking");
|
|
1098
|
+
},
|
|
1099
|
+
});
|
|
1100
|
+
await harness.command().handler("", harness.ctx);
|
|
1101
|
+
const component = harness.component();
|
|
1102
|
+
goToSection(component, "Thinking");
|
|
1103
|
+
selectLabel(component, "Enabled");
|
|
1104
|
+
component.handleInput(" ");
|
|
1105
|
+
expect(focusedRow(component)).toContain("disabled");
|
|
1106
|
+
expect(harness.notifications).toContain("Could not update Zentui settings: read-only thinking");
|
|
1107
|
+
await harness.command().handler("thinking-steps", harness.ctx);
|
|
1108
|
+
expect(harness.notifications.at(-1)).toMatch(/^Usage:/);
|
|
1109
|
+
expect(
|
|
1110
|
+
harness
|
|
1111
|
+
.command()
|
|
1112
|
+
.getArgumentCompletions("")
|
|
1113
|
+
?.map((item) => item.value)
|
|
1114
|
+
.join("\n"),
|
|
1115
|
+
).not.toMatch(/thinking.steps/i);
|
|
1116
|
+
});
|
|
1117
|
+
|
|
838
1118
|
it("routes all Working-line rows independently", async () => {
|
|
839
1119
|
const harness = createHarness();
|
|
840
1120
|
await harness.command().handler("working-line", harness.ctx);
|
|
@@ -851,7 +1131,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
851
1131
|
"Custom messages",
|
|
852
1132
|
"Tool",
|
|
853
1133
|
"Elapsed",
|
|
854
|
-
"Thinking",
|
|
1134
|
+
"Thinking time",
|
|
855
1135
|
"Tokens",
|
|
856
1136
|
"Message list",
|
|
857
1137
|
]);
|
|
@@ -867,7 +1147,7 @@ describe("component-oriented /zentui settings", () => {
|
|
|
867
1147
|
["Custom messages", "disabled"],
|
|
868
1148
|
["Tool", "disabled"],
|
|
869
1149
|
["Elapsed", "disabled"],
|
|
870
|
-
["Thinking", "disabled"],
|
|
1150
|
+
["Thinking time", "disabled"],
|
|
871
1151
|
["Tokens", "disabled"],
|
|
872
1152
|
] as const) {
|
|
873
1153
|
selectLabel(component, label);
|
|
@@ -1154,6 +1434,8 @@ describe("component-oriented /zentui settings", () => {
|
|
|
1154
1434
|
]);
|
|
1155
1435
|
expect(vi.getTimerCount()).toBe(0);
|
|
1156
1436
|
component.handleInput("\t");
|
|
1437
|
+
expect(component.render(100).join("\n")).toContain("Thinking");
|
|
1438
|
+
component.handleInput("\t");
|
|
1157
1439
|
const workingRows = component.render(100);
|
|
1158
1440
|
expectStackedPreview(workingRows, "Sautéing…");
|
|
1159
1441
|
component.handleInput("\t");
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../extensions/zentui/config";
|
|
11
11
|
import {
|
|
12
12
|
renderEditorSettingsPreview,
|
|
13
|
+
renderThinkingStepsSettingsPreview,
|
|
13
14
|
renderUserMessageSettingsPreview,
|
|
14
15
|
SETTINGS_PREVIEW_MAX_ROWS,
|
|
15
16
|
SETTINGS_PREVIEW_MAX_WIDTH,
|
|
@@ -275,6 +276,111 @@ describe("settings previews", () => {
|
|
|
275
276
|
expect(disabledOutput).not.toContain("preview");
|
|
276
277
|
});
|
|
277
278
|
|
|
279
|
+
it("renders all Rail labels and the latest five Tree labels through native Markdown rows", () => {
|
|
280
|
+
const source = config();
|
|
281
|
+
for (const mode of ["rail", "tree"] as const) {
|
|
282
|
+
source.components.thinkingSteps.mode = mode;
|
|
283
|
+
const output = plain(renderThinkingStepsSettingsPreview(source, theme(), 72));
|
|
284
|
+
expect(output).toContain(mode === "rail" ? "│ Thinking" : "┆ Thinking");
|
|
285
|
+
expect(output).toContain(
|
|
286
|
+
mode === "rail" ? "│ • Verify compatibility" : "└─ • Verify compatibility",
|
|
287
|
+
);
|
|
288
|
+
if (mode === "tree") expect(output).not.toContain("Inspect the change");
|
|
289
|
+
else expect(output).toContain("Inspect the change");
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("renders a pure static Streaming preview with saved/restart/active status", () => {
|
|
294
|
+
const current = config();
|
|
295
|
+
current.components.thinkingSteps.enabled = true;
|
|
296
|
+
current.components.thinkingSteps.mode = "streaming";
|
|
297
|
+
const output = plain(
|
|
298
|
+
renderThinkingStepsSettingsPreview(current, theme(), 72, {
|
|
299
|
+
state: {
|
|
300
|
+
available: true,
|
|
301
|
+
active: true,
|
|
302
|
+
activeMode: "tree",
|
|
303
|
+
startup: { enabled: true, mode: "tree" },
|
|
304
|
+
restartRequired: true,
|
|
305
|
+
},
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
expect(output).toContain("Thinking 7.1s (configured thinking toggle to expand)");
|
|
309
|
+
expect(output).toContain("Saved: Streaming · Active: Tree · restart required");
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it.each([
|
|
313
|
+
{ active: true, activeMode: "rail" as const, expected: "active rail" },
|
|
314
|
+
{ active: false, activeMode: undefined, expected: "active native" },
|
|
315
|
+
])("keeps saved, active, restart, and unavailable reason in status ($expected)", (fixture) => {
|
|
316
|
+
const current = config();
|
|
317
|
+
current.components.thinkingSteps.enabled = true;
|
|
318
|
+
current.components.thinkingSteps.mode = "streaming";
|
|
319
|
+
const output = plain(
|
|
320
|
+
renderThinkingStepsSettingsPreview(current, theme(), 72, {
|
|
321
|
+
state: {
|
|
322
|
+
available: true,
|
|
323
|
+
rendererAvailable: true,
|
|
324
|
+
streamingAvailable: false,
|
|
325
|
+
active: fixture.active,
|
|
326
|
+
...(fixture.activeMode ? { activeMode: fixture.activeMode } : {}),
|
|
327
|
+
startup: { enabled: true, mode: "rail" },
|
|
328
|
+
restartRequired: true,
|
|
329
|
+
reason: "Pi's terminal input listener is unavailable; restart required",
|
|
330
|
+
},
|
|
331
|
+
}),
|
|
332
|
+
);
|
|
333
|
+
expect(output).toContain(
|
|
334
|
+
`Saved: Streaming · ${fixture.expected.replace("active", "Active:").replace("rail", "Rail").replace("native", "Native")} · Streaming unavailable`,
|
|
335
|
+
);
|
|
336
|
+
expect(output).toContain("Pi's terminal input listener is unavailable");
|
|
337
|
+
expect(output.match(/restart required/gi)).toHaveLength(1);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("uses direct accent connectors and native thinking/bold Markdown callbacks", () => {
|
|
341
|
+
const calls: Array<[string, string]> = [];
|
|
342
|
+
const currentTheme = {
|
|
343
|
+
...theme(),
|
|
344
|
+
fg: (color: string, text: string) => {
|
|
345
|
+
calls.push([color, text]);
|
|
346
|
+
return text;
|
|
347
|
+
},
|
|
348
|
+
} as unknown as Theme;
|
|
349
|
+
const output = plain(renderThinkingStepsSettingsPreview(config(), currentTheme, 72));
|
|
350
|
+
expect(output).toContain("┆ Thinking");
|
|
351
|
+
expect(calls).toEqual(
|
|
352
|
+
expect.arrayContaining([
|
|
353
|
+
["accent", "┆ "],
|
|
354
|
+
["accent", "└─ • "],
|
|
355
|
+
]),
|
|
356
|
+
);
|
|
357
|
+
expect(calls.some(([color]) => color === "thinkingText")).toBe(true);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("keeps unavailable/native status visible while disabled and width-bounded", () => {
|
|
361
|
+
const current = config();
|
|
362
|
+
current.components.thinkingSteps.enabled = false;
|
|
363
|
+
for (const mode of ["rail", "tree", "streaming"] as const) {
|
|
364
|
+
current.components.thinkingSteps.mode = mode;
|
|
365
|
+
const rendered = renderThinkingStepsSettingsPreview(current, theme(), 20, false);
|
|
366
|
+
expect(rendered.every((row) => visibleWidth(row) <= 20)).toBe(true);
|
|
367
|
+
expect(plain(rendered)).toContain("Renderer unavailable");
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("re-renders Thinking-step preview with current mode and theme immediately", () => {
|
|
372
|
+
const current = config();
|
|
373
|
+
current.components.thinkingSteps.mode = "rail";
|
|
374
|
+
const rail = renderThinkingStepsSettingsPreview(current, theme(0), 72);
|
|
375
|
+
current.components.thinkingSteps.mode = "tree";
|
|
376
|
+
const tree = renderThinkingStepsSettingsPreview(current, theme(37), 72);
|
|
377
|
+
expect(tree).not.toEqual(rail);
|
|
378
|
+
expect(plain(rail)).toContain("│ Thinking");
|
|
379
|
+
expect(plain(tree)).toContain("┆ Thinking");
|
|
380
|
+
expect(plain(rail)).not.toMatch(/Thinking ·|Thinking.*Rail|Thinking.*Tree/);
|
|
381
|
+
expect(plain(tree)).not.toMatch(/Thinking ·|Thinking.*Rail|Thinking.*Tree/);
|
|
382
|
+
});
|
|
383
|
+
|
|
278
384
|
it("sanitizes hostile source and configured icons before trusted preview styling", () => {
|
|
279
385
|
const current = config();
|
|
280
386
|
current.icons.rail = "\x1b]2;RAIL-OSC\x07│\u009b31m\u009dRAIL-C1\u009c\u009b0m";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
const root = join(import.meta.dirname, "..");
|
|
6
|
+
|
|
7
|
+
describe("Thinking (Experimental) documentation", () => {
|
|
8
|
+
it("documents live transitions, restart gating, native Markdown rows, all modes, and bounded ownership", () => {
|
|
9
|
+
for (const path of ["README.md", "docs/configuration.md"]) {
|
|
10
|
+
const documentation = readFileSync(join(root, path), "utf8");
|
|
11
|
+
expect(documentation).toContain("Thinking (Experimental)");
|
|
12
|
+
expect(documentation).toMatch(/0\.80\.5.*0\.82\.1.*0\.83\.0.*0\.84\.0.*0\.84\.4/s);
|
|
13
|
+
expect(documentation).toMatch(/switch.*live|live switching/i);
|
|
14
|
+
expect(documentation).toMatch(/restart/i);
|
|
15
|
+
expect(documentation).toMatch(/native.*Markdown|Pi `Markdown`/i);
|
|
16
|
+
expect(documentation).toMatch(/strict(?: 7-bit CSI)? SGR styling is stripped/i);
|
|
17
|
+
expect(documentation).toMatch(/every other terminal control/i);
|
|
18
|
+
expect(documentation).toMatch(/256/);
|
|
19
|
+
expect(documentation).toMatch(/Working line/);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("uses the public Thinking copy and states the live boundary exactly", () => {
|
|
24
|
+
const readme = readFileSync(join(root, "README.md"), "utf8");
|
|
25
|
+
expect(readme).toContain(
|
|
26
|
+
"Active Streaming can switch live to Rail or Tree, and Rail and Tree can switch live between each other. Entering Streaming from a structural mode, first enable, and re-enable after a live disable require restarting Pi.",
|
|
27
|
+
);
|
|
28
|
+
expect(readme).not.toMatch(
|
|
29
|
+
/switches Rail, Tree, and Streaming live|Entering Streaming transactionally|Streaming → Tree → Rail → Streaming/,
|
|
30
|
+
);
|
|
31
|
+
const configuration = readFileSync(join(root, "docs/configuration.md"), "utf8");
|
|
32
|
+
expect(configuration).toContain("Rail parses each native contiguous thinking run");
|
|
33
|
+
expect(configuration).not.toMatch(/Streaming → Tree → Rail → Streaming/);
|
|
34
|
+
for (const path of [
|
|
35
|
+
"README.md",
|
|
36
|
+
"docs/configuration.md",
|
|
37
|
+
"extensions/zentui/settings-command.ts",
|
|
38
|
+
]) {
|
|
39
|
+
const source = readFileSync(join(root, path), "utf8");
|
|
40
|
+
expect(source, path).not.toMatch(/Thinking steps(?: mode)?/);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("keeps both adapted MIT notices adjacent in packaged renderer source", () => {
|
|
45
|
+
const source = readFileSync(join(root, "extensions/zentui/thinking-experimental.ts"), "utf8");
|
|
46
|
+
expect(source).toContain("Copyright (c) 2026 Zach Yuen");
|
|
47
|
+
expect(source).toContain("Copyright (c) 2026 Marc Mironescu / FluxGear");
|
|
48
|
+
expect(source.match(/Permission is hereby granted, free of charge/g)).toHaveLength(2);
|
|
49
|
+
expect(source).not.toMatch(/setWorkingMessage|setWorkingVisible/);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
|
|
3
|
+
const diagnosticPatterns = [
|
|
4
|
+
/Failed to load extension(?:\s|:|")/i,
|
|
5
|
+
/Extension\s+(?:"[^"\r\n]*"|'[^'\r\n]*')\s+error\s*:/i,
|
|
6
|
+
/Extension error\s*\(/i,
|
|
7
|
+
/loader diagnostic\s*:/i,
|
|
8
|
+
/\b(?:ERR_MODULE_NOT_FOUND|ERR_PACKAGE_PATH_NOT_EXPORTED|ERR_UNKNOWN_FILE_EXTENSION|MODULE_NOT_FOUND)\b/i,
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export function hasExtensionLoaderDiagnostic(output) {
|
|
12
|
+
return diagnosticPatterns.some((pattern) => pattern.test(output));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const rejectedDiagnosticFixtures = Object.freeze([
|
|
16
|
+
'Extension "/tmp/any path/zentui.ts" error: SyntaxError: unexpected token',
|
|
17
|
+
"Extension '/private/quoted/path with spaces/index.ts' error: missing export",
|
|
18
|
+
'Failed to load extension "/tmp/zentui.ts": import failed',
|
|
19
|
+
"Failed to load extension: transform failed",
|
|
20
|
+
"Extension error (/tmp/zentui.ts): runtime failure",
|
|
21
|
+
"loader diagnostic: source module rejected",
|
|
22
|
+
"ERR_MODULE_NOT_FOUND: Cannot find package pi-tui",
|
|
23
|
+
"ERR_PACKAGE_PATH_NOT_EXPORTED: Package subpath './package.json' is not defined",
|
|
24
|
+
"ERR_UNKNOWN_FILE_EXTENSION: Unknown file extension .ts",
|
|
25
|
+
"MODULE_NOT_FOUND: Cannot find module 'pi-ai'",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export const acceptedOutputFixtures = Object.freeze([
|
|
29
|
+
"Thinking 0.1s (ctrl+t to expand)",
|
|
30
|
+
"Extension loaded: /tmp/zentui.ts",
|
|
31
|
+
"The user asked whether a loader diagnostic would help explain an extension error.",
|
|
32
|
+
"The assistant described the phrase extension error while discussing a loader diagnostic.",
|
|
33
|
+
"The assistant discussed extension recovery strategy.",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
export function assertExtensionLoaderDiagnosticFixtures() {
|
|
37
|
+
for (const fixture of rejectedDiagnosticFixtures) {
|
|
38
|
+
if (!hasExtensionLoaderDiagnostic(fixture)) {
|
|
39
|
+
throw new Error(`loader diagnostic fixture was not rejected: ${JSON.stringify(fixture)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const fixture of acceptedOutputFixtures) {
|
|
43
|
+
if (hasExtensionLoaderDiagnostic(fixture)) {
|
|
44
|
+
throw new Error(`ordinary output fixture was rejected: ${JSON.stringify(fixture)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
50
|
+
assertExtensionLoaderDiagnosticFixtures();
|
|
51
|
+
console.log(
|
|
52
|
+
`loader-diagnostics: rejected=${rejectedDiagnosticFixtures.length} accepted=${acceptedOutputFixtures.length}`,
|
|
53
|
+
);
|
|
54
|
+
}
|