lsd-pi 1.3.9 → 1.3.11
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/loader.js +0 -0
- package/dist/resources/agents/scout.md +1 -0
- package/dist/resources/extensions/mcp-client/index.js +191 -83
- package/dist/resources/extensions/mcp-client/mcp-manager-component.js +220 -0
- package/dist/resources/extensions/slash-commands/plan.js +67 -13
- package/dist/resources/extensions/subagent/agents.js +7 -0
- package/dist/resources/extensions/subagent/index.js +25 -8
- package/dist/resources/extensions/subagent/model-resolution.js +1 -0
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js +104 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-summary-line.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +39 -2
- 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 +135 -18
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts +21 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js +146 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-summary-line.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.js +51 -13
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.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 +75 -4
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- 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 +31 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-summary-line.test.ts +129 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +158 -18
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-summary-line.ts +163 -9
- package/packages/pi-coding-agent/src/modes/interactive/controllers/__tests__/chat-controller.collapsed-tool-summary.test.ts +60 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +86 -5
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -2
- package/pkg/package.json +1 -1
- package/src/resources/agents/scout.md +1 -0
- package/src/resources/extensions/mcp-client/index.ts +212 -90
- package/src/resources/extensions/mcp-client/mcp-manager-component.ts +256 -0
- package/src/resources/extensions/mcp-client/tests/mcp-manager-component.test.ts +141 -0
- package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +18 -2
- package/src/resources/extensions/slash-commands/plan.ts +70 -13
- package/src/resources/extensions/subagent/agents.ts +9 -0
- package/src/resources/extensions/subagent/index.ts +30 -8
- package/src/resources/extensions/subagent/model-resolution.ts +1 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +34 -1
- package/dist/headless-query.d.ts +0 -40
- package/dist/headless-query.js +0 -77
- package/src/resources/extensions/gsd/tests/test-helpers.ts +0 -61
|
@@ -277,7 +277,7 @@ async function setModelIfNeeded(pi, ctx, modelRef) {
|
|
|
277
277
|
return true;
|
|
278
278
|
}
|
|
279
279
|
function buildExecutionKickoffMessage(options) {
|
|
280
|
-
const { permissionMode, executeWithSubagent = false } = options;
|
|
280
|
+
const { permissionMode, executeWithSubagent = false, executionNote } = options;
|
|
281
281
|
const task = state.task.trim();
|
|
282
282
|
if (!executeWithSubagent) {
|
|
283
283
|
const details = [
|
|
@@ -287,6 +287,9 @@ function buildExecutionKickoffMessage(options) {
|
|
|
287
287
|
details.push(`Original task: ${task}`);
|
|
288
288
|
if (state.latestPlanPath)
|
|
289
289
|
details.push(`Use the approved plan artifact at ${state.latestPlanPath} as the execution plan.`);
|
|
290
|
+
if (executionNote)
|
|
291
|
+
details.push(`User execution note: ${executionNote}`);
|
|
292
|
+
details.push("After implementation: guide the user through verification by presenting a concise checklist based on the plan's Acceptance Criteria and Verification Plan. Run applicable checks (build, lint, tests) and report results.");
|
|
290
293
|
return details.join(" ");
|
|
291
294
|
}
|
|
292
295
|
const codingModel = readPlanModeCodingModel();
|
|
@@ -303,13 +306,24 @@ function buildExecutionKickoffMessage(options) {
|
|
|
303
306
|
details.push(`Original task: ${task}`);
|
|
304
307
|
if (state.latestPlanPath)
|
|
305
308
|
details.push(`Primary plan artifact: ${state.latestPlanPath}`);
|
|
309
|
+
if (executionNote) {
|
|
310
|
+
details.push(`User execution note: ${executionNote}`);
|
|
311
|
+
// If the note contains a model request, surface it explicitly so the agent
|
|
312
|
+
// doesn't silently drop it. The model override will be resolved by
|
|
313
|
+
// normalizeSubagentModel when the subagent tool is invoked.
|
|
314
|
+
const modelMatch = executionNote.match(/\b(?:model|use\s+model)\s+["']?([\w.-]+(?:\/[\w.-]+)?)["']?\b/i);
|
|
315
|
+
if (modelMatch?.[1]) {
|
|
316
|
+
details.push(`The user explicitly requested model "${modelMatch[1]}". You MUST pass model="${modelMatch[1]}" in the subagent tool call. If normalizeSubagentModel cannot resolve this model, report the error to the user instead of silently falling back.`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
306
319
|
details.push("Important: if the plan is large and you estimate it would exceed a single subagent's context window (~200k tokens), " +
|
|
307
320
|
"split execution across multiple sequential subagents instead of one. " +
|
|
308
321
|
"Use the subagent tool's chain mode: pass a \"chain\" array where each entry covers one self-contained phase or group of steps from the plan. " +
|
|
309
322
|
"Each chain entry should include the agent name, a focused task description for that phase, and may reference {previous} to receive the prior phase's output as handoff context. " +
|
|
310
323
|
"Only split when genuinely needed — prefer a single subagent for plans that fit comfortably.");
|
|
311
324
|
details.push("After all subagents complete: (1) do a quick review of the implementation — check that the plan steps were actually carried out, spot obvious issues or missed pieces, and verify the code compiles/passes lint if applicable. " +
|
|
312
|
-
"(2) Then
|
|
325
|
+
"(2) Then guide the user through verification: present a concise checklist based on the plan's Acceptance Criteria and Verification Plan sections. Run applicable checks (build, lint, tests) and report results. " +
|
|
326
|
+
"(3) Summarize what was done, what (if anything) needs follow-up, and flag any concerns found during review.");
|
|
313
327
|
return details.join(" ");
|
|
314
328
|
}
|
|
315
329
|
let pendingNewSession = null;
|
|
@@ -331,7 +345,7 @@ function scheduleNewSession(pi, ctx) {
|
|
|
331
345
|
// Must use the /prefix so tryExecuteExtensionCommand parses the name correctly.
|
|
332
346
|
pi.executeSlashCommand("/plan-execute-new-session");
|
|
333
347
|
}
|
|
334
|
-
async function approvePlan(pi, ctx, permissionMode, executeWithSubagent = false) {
|
|
348
|
+
async function approvePlan(pi, ctx, permissionMode, executeWithSubagent = false, executionNote) {
|
|
335
349
|
// Do NOT switch to reasoning model during execution.
|
|
336
350
|
// The reasoning model is only for plan-mode investigation, not execution.
|
|
337
351
|
// If a coding model is configured and we're using a subagent, the explicit
|
|
@@ -347,7 +361,7 @@ async function approvePlan(pi, ctx, permissionMode, executeWithSubagent = false)
|
|
|
347
361
|
// subagent tool with the default session model BEFORE it ever sees the
|
|
348
362
|
// explicit model="<planModeCodingModel>" instruction. Steering ensures the
|
|
349
363
|
// configured plan-mode coding model reaches the subagent invocation.
|
|
350
|
-
await pi.sendUserMessage(buildExecutionKickoffMessage({ permissionMode, executeWithSubagent }), { deliverAs: "steer" });
|
|
364
|
+
await pi.sendUserMessage(buildExecutionKickoffMessage({ permissionMode, executeWithSubagent, executionNote }), { deliverAs: "steer" });
|
|
351
365
|
}
|
|
352
366
|
async function cancelPlan(pi, ctx, clearTask = true) {
|
|
353
367
|
const restoreMode = state.previousMode ?? "accept-on-edit";
|
|
@@ -359,10 +373,12 @@ async function cancelPlan(pi, ctx, clearTask = true) {
|
|
|
359
373
|
function buildPlanModeSystemPrompt() {
|
|
360
374
|
const details = [
|
|
361
375
|
"You are currently in plan mode.",
|
|
376
|
+
"Terse output. All technical substance stays. Only fluff dies. Fragments OK.",
|
|
362
377
|
"Investigate, clarify scope, and produce a persisted execution plan before making source changes.",
|
|
363
378
|
"If requirements are ambiguous or constraints are missing, ask concise clarifying questions before drafting or saving a plan.",
|
|
364
379
|
`Before writing or updating a plan artifact, make sure your confidence is at least ${MIN_PLAN_CONFIDENCE}/10. If confidence is lower, investigate more or ask clarifying questions first.`,
|
|
365
380
|
"Include an explicit confidence line in every saved plan, for example: \"Confidence: 8/10\" or higher.",
|
|
381
|
+
"Every saved plan MUST include explicit \"Acceptance Criteria\" and \"Verification Plan\" sections. Plans missing these sections will be rejected for approval.",
|
|
366
382
|
"When adjusting an existing saved plan, prefer the edit tool for targeted changes. Rewrite the whole file only when the structure changes substantially or an exact edit is impractical.",
|
|
367
383
|
"Do not modify source files or run side-effect commands while plan mode is active.",
|
|
368
384
|
"Persist plan artifacts under .lsd/plan/.",
|
|
@@ -411,6 +427,29 @@ function buildApprovalActionInstructions() {
|
|
|
411
427
|
function buildApprovalDialogInstructions() {
|
|
412
428
|
return buildApprovalActionInstructions();
|
|
413
429
|
}
|
|
430
|
+
/** Required heading patterns for plan artifacts. Matches common variants. */
|
|
431
|
+
const REQUIRED_PLAN_SECTIONS = [
|
|
432
|
+
{ pattern: /\b(acceptance\s*criteria|success\s*criteria|done\s*criteria)\b/i, label: "Acceptance Criteria" },
|
|
433
|
+
{ pattern: /\b(verification\s*(plan|steps|strategy)|how\s+to\s+verify|testing\s*plan)\b/i, label: "Verification Plan" },
|
|
434
|
+
];
|
|
435
|
+
function validatePlanArtifact(markdown) {
|
|
436
|
+
const missing = [];
|
|
437
|
+
for (const section of REQUIRED_PLAN_SECTIONS) {
|
|
438
|
+
if (!section.pattern.test(markdown)) {
|
|
439
|
+
missing.push(section.label);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return { valid: missing.length === 0, missing };
|
|
443
|
+
}
|
|
444
|
+
function buildPlanValidationSteeringMessage(planPath, missing) {
|
|
445
|
+
const missingList = missing.map((m) => `- **${m}**`).join("\n");
|
|
446
|
+
return [
|
|
447
|
+
`Plan artifact saved at ${planPath}.`,
|
|
448
|
+
`The plan is missing required sections before it can be approved for implementation:`,
|
|
449
|
+
missingList,
|
|
450
|
+
`Please revise the plan to include these sections, then re-save. Do not ask for approval until all required sections are present.`,
|
|
451
|
+
].join("\n\n");
|
|
452
|
+
}
|
|
414
453
|
function buildApprovalSteeringMessage(planPath) {
|
|
415
454
|
return [
|
|
416
455
|
`Plan artifact saved at ${planPath}.`,
|
|
@@ -621,13 +660,26 @@ export default function planCommand(pi) {
|
|
|
621
660
|
return;
|
|
622
661
|
}
|
|
623
662
|
const planMarkdown = readPlanArtifact(path);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
663
|
+
if (!planMarkdown) {
|
|
664
|
+
ctx.ui?.notify?.("Plan artifact could not be read for validation", "warning");
|
|
665
|
+
pi.sendUserMessage(buildApprovalSteeringMessage(path), { deliverAs: "steer" });
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
const validation = validatePlanArtifact(planMarkdown);
|
|
669
|
+
if (!validation.valid) {
|
|
670
|
+
ctx.ui?.notify?.("Plan missing required sections — see guidance below", "warning");
|
|
671
|
+
pi.sendUserMessage(buildPlanValidationSteeringMessage(path, validation.missing), { deliverAs: "steer" });
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
pi.sendMessage({
|
|
675
|
+
customType: "plan-mode-preview",
|
|
676
|
+
content: buildPlanPreviewMessage(path, planMarkdown),
|
|
677
|
+
display: true,
|
|
678
|
+
});
|
|
679
|
+
ctx.ui?.notify?.("/plan to show plan", "info");
|
|
680
|
+
pi.sendUserMessage(buildApprovalSteeringMessage(path), { deliverAs: "steer" });
|
|
681
|
+
}
|
|
682
|
+
}
|
|
631
683
|
}
|
|
632
684
|
return;
|
|
633
685
|
}
|
|
@@ -676,12 +728,13 @@ export default function planCommand(pi) {
|
|
|
676
728
|
permissionMode: DEFAULT_APPROVAL_PERMISSION_MODE,
|
|
677
729
|
executeWithSubagent: false,
|
|
678
730
|
};
|
|
731
|
+
const permissionNote = getAnswerNote(permissionAnswer);
|
|
679
732
|
state = { ...state, targetPermissionMode: executionMode.permissionMode };
|
|
680
733
|
if (executionMode.executeWithSubagent) {
|
|
681
734
|
const modeLabel = executionMode.permissionMode === "danger-full-access" ? "bypass" : "auto";
|
|
682
735
|
ctx.ui?.notify?.(`Plan approved: subagent(${modeLabel})`, "info");
|
|
683
736
|
}
|
|
684
|
-
await approvePlan(pi, ctx, executionMode.permissionMode, executionMode.executeWithSubagent);
|
|
737
|
+
await approvePlan(pi, ctx, executionMode.permissionMode, executionMode.executeWithSubagent, permissionNote);
|
|
685
738
|
return;
|
|
686
739
|
}
|
|
687
740
|
// ── First question answered (action) ──────────────────────────────────
|
|
@@ -699,12 +752,13 @@ export default function planCommand(pi) {
|
|
|
699
752
|
permissionMode: DEFAULT_APPROVAL_PERMISSION_MODE,
|
|
700
753
|
executeWithSubagent: false,
|
|
701
754
|
};
|
|
755
|
+
const actionNote = getAnswerNote(actionAnswer);
|
|
702
756
|
state = { ...state, targetPermissionMode: executionMode.permissionMode };
|
|
703
757
|
if (executionMode.executeWithSubagent) {
|
|
704
758
|
const modeLabel = executionMode.permissionMode === "danger-full-access" ? "bypass" : "auto";
|
|
705
759
|
ctx.ui?.notify?.(`Plan approved: subagent(${modeLabel})`, "info");
|
|
706
760
|
}
|
|
707
|
-
await approvePlan(pi, ctx, executionMode.permissionMode, executionMode.executeWithSubagent);
|
|
761
|
+
await approvePlan(pi, ctx, executionMode.permissionMode, executionMode.executeWithSubagent, actionNote);
|
|
708
762
|
return;
|
|
709
763
|
}
|
|
710
764
|
if (actionSelection.includes(REVIEW_LABEL)) {
|
|
@@ -6,6 +6,8 @@ import * as path from "node:path";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { getAgentDir, parseFrontmatter } from "@gsd/pi-coding-agent";
|
|
8
8
|
const PROJECT_AGENT_DIR_CANDIDATES = [".lsd", ".gsd", ".pi"];
|
|
9
|
+
/** Fixed read-only tool set for the reserved `scout` agent. */
|
|
10
|
+
const SCOUT_ALLOWED_TOOLS = ["read", "lsp", "grep", "find", "ls"];
|
|
9
11
|
function normalizeAgentModel(model) {
|
|
10
12
|
const trimmed = model?.trim();
|
|
11
13
|
if (!trimmed)
|
|
@@ -117,6 +119,11 @@ export function discoverAgents(cwd, scope) {
|
|
|
117
119
|
else {
|
|
118
120
|
addAgents(projectAgents);
|
|
119
121
|
}
|
|
122
|
+
// Enforce reserved agent tool policies — scout is always read-only
|
|
123
|
+
const scout = agentMap.get("scout");
|
|
124
|
+
if (scout) {
|
|
125
|
+
scout.tools = [...SCOUT_ALLOWED_TOOLS];
|
|
126
|
+
}
|
|
120
127
|
return { agents: Array.from(agentMap.values()), projectAgentsDir };
|
|
121
128
|
}
|
|
122
129
|
export function formatAgentList(agents, maxItems) {
|
|
@@ -997,11 +997,13 @@ export default function (pi) {
|
|
|
997
997
|
const hasTasks = (params.tasks?.length ?? 0) > 0;
|
|
998
998
|
const hasSingle = Boolean(params.agent && params.task);
|
|
999
999
|
const modeCount = Number(hasChain) + Number(hasTasks) + Number(hasSingle);
|
|
1000
|
-
const makeDetails = (mode) => (results) => ({
|
|
1000
|
+
const makeDetails = (mode) => (results, opts) => ({
|
|
1001
1001
|
mode,
|
|
1002
1002
|
agentScope,
|
|
1003
1003
|
projectAgentsDir: discovery.projectAgentsDir,
|
|
1004
1004
|
results,
|
|
1005
|
+
totalChainSteps: opts?.totalSteps,
|
|
1006
|
+
currentChainStep: opts?.currentStep,
|
|
1005
1007
|
});
|
|
1006
1008
|
const trackInProcessDepth = (started, depth, ancestry) => {
|
|
1007
1009
|
const sessionId = started.handle.sessionId;
|
|
@@ -1090,7 +1092,8 @@ export default function (pi) {
|
|
|
1090
1092
|
for (let i = 0; i < params.chain.length; i++) {
|
|
1091
1093
|
const step = params.chain[i];
|
|
1092
1094
|
const taskWithContext = step.task.replace(/\{previous\}/g, previousOutput);
|
|
1093
|
-
// Create update callback that includes all previous results
|
|
1095
|
+
// Create update callback that includes all previous results + chain progress
|
|
1096
|
+
const chainTotalSteps = params.chain.length;
|
|
1094
1097
|
const chainUpdate = onUpdate
|
|
1095
1098
|
? (partial) => {
|
|
1096
1099
|
// Combine completed results with current streaming result
|
|
@@ -1099,7 +1102,10 @@ export default function (pi) {
|
|
|
1099
1102
|
const allResults = [...results, currentResult];
|
|
1100
1103
|
onUpdate({
|
|
1101
1104
|
content: partial.content,
|
|
1102
|
-
details: makeDetails("chain")(allResults
|
|
1105
|
+
details: makeDetails("chain")(allResults, {
|
|
1106
|
+
totalSteps: chainTotalSteps,
|
|
1107
|
+
currentStep: i + 1,
|
|
1108
|
+
}),
|
|
1103
1109
|
});
|
|
1104
1110
|
}
|
|
1105
1111
|
}
|
|
@@ -1117,7 +1123,7 @@ export default function (pi) {
|
|
|
1117
1123
|
const errorMsg = result.errorMessage || result.stderr || getFinalOutput(result.messages) || "(no output)";
|
|
1118
1124
|
return {
|
|
1119
1125
|
content: [{ type: "text", text: `Chain stopped at step ${i + 1} (${step.agent}): ${errorMsg}` }],
|
|
1120
|
-
details: makeDetails("chain")(results),
|
|
1126
|
+
details: makeDetails("chain")(results, { totalSteps: params.chain.length }),
|
|
1121
1127
|
isError: true,
|
|
1122
1128
|
};
|
|
1123
1129
|
}
|
|
@@ -1125,7 +1131,7 @@ export default function (pi) {
|
|
|
1125
1131
|
}
|
|
1126
1132
|
return {
|
|
1127
1133
|
content: [{ type: "text", text: getFinalOutput(results[results.length - 1].messages) || "(no output)" }],
|
|
1128
|
-
details: makeDetails("chain")(results),
|
|
1134
|
+
details: makeDetails("chain")(results, { totalSteps: params.chain.length }),
|
|
1129
1135
|
};
|
|
1130
1136
|
}
|
|
1131
1137
|
if (params.tasks && params.tasks.length > 0) {
|
|
@@ -1653,13 +1659,24 @@ export default function (pi) {
|
|
|
1653
1659
|
};
|
|
1654
1660
|
if (details.mode === "chain") {
|
|
1655
1661
|
const successCount = details.results.filter((r) => r.exitCode === 0).length;
|
|
1656
|
-
const
|
|
1662
|
+
const isRunning = details.currentChainStep != null;
|
|
1663
|
+
const icon = !isRunning && successCount === details.results.length
|
|
1664
|
+
? theme.fg("success", "✓")
|
|
1665
|
+
: isRunning
|
|
1666
|
+
? theme.fg("accent", "◉")
|
|
1667
|
+
: theme.fg("error", "✗");
|
|
1668
|
+
const totalLabel = details.totalChainSteps != null
|
|
1669
|
+
? `${details.results.length}/${details.totalChainSteps} steps`
|
|
1670
|
+
: `${details.results.length} steps`;
|
|
1671
|
+
const progressLabel = isRunning
|
|
1672
|
+
? ` (step ${details.currentChainStep} running)`
|
|
1673
|
+
: "";
|
|
1657
1674
|
if (expanded) {
|
|
1658
1675
|
const container = new Container();
|
|
1659
1676
|
container.addChild(new Text(icon +
|
|
1660
1677
|
" " +
|
|
1661
1678
|
theme.fg("toolTitle", theme.bold("chain ")) +
|
|
1662
|
-
theme.fg("accent", `${
|
|
1679
|
+
theme.fg("accent", `${totalLabel}${progressLabel}`), 0, 0));
|
|
1663
1680
|
for (const r of details.results) {
|
|
1664
1681
|
const rIcon = r.exitCode === 0 ? theme.fg("success", "✓") : theme.fg("error", "✗");
|
|
1665
1682
|
const displayItems = getDisplayItems(r.messages);
|
|
@@ -1693,7 +1710,7 @@ export default function (pi) {
|
|
|
1693
1710
|
let text = icon +
|
|
1694
1711
|
" " +
|
|
1695
1712
|
theme.fg("toolTitle", theme.bold("chain ")) +
|
|
1696
|
-
theme.fg("accent", `${
|
|
1713
|
+
theme.fg("accent", `${totalLabel}${progressLabel}`);
|
|
1697
1714
|
for (const r of details.results) {
|
|
1698
1715
|
const rIcon = r.exitCode === 0 ? theme.fg("success", "✓") : theme.fg("error", "✗");
|
|
1699
1716
|
const displayItems = getDisplayItems(r.messages);
|
|
@@ -11,6 +11,7 @@ const BARE_MODEL_PROVIDER_RULES = [
|
|
|
11
11
|
matches: (modelId) => /^(mistral-|ministral-|codestral-)/.test(modelId),
|
|
12
12
|
},
|
|
13
13
|
{ provider: "groq", matches: (modelId) => modelId.startsWith("llama-") || modelId.startsWith("mixtral-") },
|
|
14
|
+
{ provider: "zhipu", matches: (modelId) => modelId.startsWith("glm-") },
|
|
14
15
|
];
|
|
15
16
|
export function inferProviderForBareModel(modelId) {
|
|
16
17
|
const normalizedModelId = modelId.trim().toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import stripAnsi from "strip-ansi";
|
|
4
|
-
import { ToolSummaryLine } from "../tool-summary-line.js";
|
|
4
|
+
import { ToolSummaryLine, extractToolLabel } from "../tool-summary-line.js";
|
|
5
5
|
import { initTheme } from "../../theme/theme.js";
|
|
6
6
|
initTheme("dark");
|
|
7
7
|
describe("ToolSummaryLine", () => {
|
|
@@ -13,7 +13,8 @@ describe("ToolSummaryLine", () => {
|
|
|
13
13
|
assert.match(rendered, /^ ● /);
|
|
14
14
|
assert.ok(rendered.includes("reading 2 files · 0.8s"));
|
|
15
15
|
assert.equal(summary.canGroupWith("read"), true);
|
|
16
|
-
assert.equal(summary.canGroupWith("find"),
|
|
16
|
+
assert.equal(summary.canGroupWith("find"), true);
|
|
17
|
+
assert.equal(summary.canGroupWith("bash"), false);
|
|
17
18
|
assert.equal(rendered.includes("collapsed tools"), false);
|
|
18
19
|
assert.equal(rendered.includes("⎯"), false);
|
|
19
20
|
});
|
|
@@ -30,5 +31,106 @@ describe("ToolSummaryLine", () => {
|
|
|
30
31
|
summary.setHidden(true);
|
|
31
32
|
assert.deepEqual(summary.render(80), []);
|
|
32
33
|
});
|
|
34
|
+
it("renders spinner and label line when pending tools exist", () => {
|
|
35
|
+
const summary = new ToolSummaryLine();
|
|
36
|
+
summary.addPendingTool("t1", "read", { path: "src/foo/bar.ts" });
|
|
37
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
38
|
+
assert.ok(!rendered.startsWith(" ●"));
|
|
39
|
+
assert.ok(rendered.includes(" └ bar.ts"));
|
|
40
|
+
assert.ok(rendered.includes("bar.ts"));
|
|
41
|
+
});
|
|
42
|
+
it("keeps last tool label after pending tool completes", () => {
|
|
43
|
+
const summary = new ToolSummaryLine();
|
|
44
|
+
summary.addPendingTool("t1", "read", { path: "file.ts" });
|
|
45
|
+
assert.equal(summary.hasPendingTools(), true);
|
|
46
|
+
summary.removePendingTool("t1");
|
|
47
|
+
summary.addTool("read", 500);
|
|
48
|
+
assert.equal(summary.hasPendingTools(), false);
|
|
49
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
50
|
+
assert.ok(rendered.includes("●"));
|
|
51
|
+
assert.ok(rendered.includes("0.5s"));
|
|
52
|
+
assert.ok(rendered.includes("└ file.ts"));
|
|
53
|
+
});
|
|
54
|
+
it("aggregates completed and pending tools in summary text", () => {
|
|
55
|
+
const summary = new ToolSummaryLine();
|
|
56
|
+
summary.addTool("read", 300);
|
|
57
|
+
summary.addPendingTool("t1", "grep", { pattern: "TODO" });
|
|
58
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
59
|
+
assert.ok(rendered.includes("1 file"));
|
|
60
|
+
assert.ok(rendered.includes("1 pattern"));
|
|
61
|
+
assert.ok(rendered.includes("…"));
|
|
62
|
+
assert.ok(rendered.includes("TODO"));
|
|
63
|
+
});
|
|
64
|
+
it("shows expand hint when set and tools are pending", () => {
|
|
65
|
+
const summary = new ToolSummaryLine();
|
|
66
|
+
summary.setExpandHint("(ctrl+o to expand)");
|
|
67
|
+
summary.addPendingTool("t1", "read", { path: "test.ts" });
|
|
68
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
69
|
+
assert.ok(rendered.includes("ctrl+o to expand"));
|
|
70
|
+
});
|
|
71
|
+
it("does not show expand hint when no pending tools", () => {
|
|
72
|
+
const summary = new ToolSummaryLine();
|
|
73
|
+
summary.setExpandHint("(ctrl+o to expand)");
|
|
74
|
+
summary.addTool("read", 300);
|
|
75
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
76
|
+
assert.ok(!rendered.includes("ctrl+o to expand"));
|
|
77
|
+
});
|
|
78
|
+
it("updates label when pending tool args change", () => {
|
|
79
|
+
const summary = new ToolSummaryLine();
|
|
80
|
+
summary.addPendingTool("t1", "read", { path: "old.ts" });
|
|
81
|
+
let rendered = stripAnsi(summary.render(160).join("\n"));
|
|
82
|
+
assert.ok(rendered.includes("old.ts"));
|
|
83
|
+
summary.updatePendingToolArgs("t1", { path: "new.ts" });
|
|
84
|
+
rendered = stripAnsi(summary.render(160).join("\n"));
|
|
85
|
+
assert.ok(rendered.includes("new.ts"));
|
|
86
|
+
});
|
|
87
|
+
it("clears pending spinner without removing last label", () => {
|
|
88
|
+
const summary = new ToolSummaryLine();
|
|
89
|
+
summary.addPendingTool("t1", "read", { path: "file.ts" });
|
|
90
|
+
summary.clearPendingTools();
|
|
91
|
+
const rendered = stripAnsi(summary.render(160).join("\n"));
|
|
92
|
+
assert.ok(!rendered.includes("◯"));
|
|
93
|
+
assert.ok(!rendered.includes("◔"));
|
|
94
|
+
assert.ok(!rendered.includes("◑"));
|
|
95
|
+
assert.ok(!rendered.includes("◕"));
|
|
96
|
+
assert.ok(!rendered.includes("● Listing"));
|
|
97
|
+
assert.ok(!rendered.includes("…"));
|
|
98
|
+
assert.ok(rendered === "" || rendered.includes("file.ts"));
|
|
99
|
+
});
|
|
100
|
+
it("canGroupWith considers pending tools", () => {
|
|
101
|
+
const summary = new ToolSummaryLine();
|
|
102
|
+
summary.addPendingTool("t1", "read", { path: "a.ts" });
|
|
103
|
+
assert.equal(summary.canGroupWith("read"), true);
|
|
104
|
+
assert.equal(summary.canGroupWith("grep"), true);
|
|
105
|
+
assert.equal(summary.canGroupWith("bash"), false);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
describe("extractToolLabel", () => {
|
|
109
|
+
it("extracts basename for read tool", () => {
|
|
110
|
+
assert.equal(extractToolLabel("read", { path: "src/foo/bar.ts" }), "bar.ts");
|
|
111
|
+
assert.equal(extractToolLabel("read", { file_path: "/abs/path/file.json" }), "file.json");
|
|
112
|
+
assert.equal(extractToolLabel("read", {}), "read");
|
|
113
|
+
});
|
|
114
|
+
it("extracts pattern for grep tool", () => {
|
|
115
|
+
assert.equal(extractToolLabel("grep", { pattern: "TODO" }), "TODO");
|
|
116
|
+
assert.equal(extractToolLabel("grep", {}), "grep");
|
|
117
|
+
});
|
|
118
|
+
it("extracts pattern for find tool", () => {
|
|
119
|
+
assert.equal(extractToolLabel("find", { pattern: "*.ts" }), "*.ts");
|
|
120
|
+
assert.equal(extractToolLabel("find", {}), "find");
|
|
121
|
+
});
|
|
122
|
+
it("extracts path for ls tool", () => {
|
|
123
|
+
assert.equal(extractToolLabel("ls", { path: "src/components" }), "components");
|
|
124
|
+
assert.equal(extractToolLabel("ls", {}), ".");
|
|
125
|
+
});
|
|
126
|
+
it("extracts symbol or file for lsp tool", () => {
|
|
127
|
+
assert.equal(extractToolLabel("lsp", { symbol: "MyClass" }), "MyClass");
|
|
128
|
+
assert.equal(extractToolLabel("lsp", { file: "src/index.ts" }), "index.ts");
|
|
129
|
+
assert.equal(extractToolLabel("lsp", { symbol: "foo", file: "bar.ts" }), "foo");
|
|
130
|
+
assert.equal(extractToolLabel("lsp", {}), "lsp");
|
|
131
|
+
});
|
|
132
|
+
it("returns tool name for unknown tools", () => {
|
|
133
|
+
assert.equal(extractToolLabel("custom_tool", { whatever: "value" }), "custom_tool");
|
|
134
|
+
});
|
|
33
135
|
});
|
|
34
136
|
//# sourceMappingURL=tool-summary-line.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-summary-line.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-summary-line.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,SAAS,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,SAAS,CAAC,MAAM,CAAC,CAAC;AAElB,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE7B,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QAClD,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAEpC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC/C,MAAM,OAAO,GAAG,IAAI,eAAe,EAAE,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAEzC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport stripAnsi from \"strip-ansi\";\n\nimport { ToolSummaryLine } from \"../tool-summary-line.js\";\nimport { initTheme } from \"../../theme/theme.js\";\n\ninitTheme(\"dark\");\n\ndescribe(\"ToolSummaryLine\", () => {\n\tit(\"renders action-based summaries for grouped identical tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 600);\n\t\tsummary.addTool(\"read\", 150);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.match(rendered, /^ ● /);\n\t\tassert.ok(rendered.includes(\"reading 2 files · 0.8s\"));\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"find\"), false);\n\t\tassert.equal(rendered.includes(\"collapsed tools\"), false);\n\t\tassert.equal(rendered.includes(\"⎯\"), false);\n\t});\n\n\tit(\"keeps fallback format for unknown tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"custom_tool\", 100);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"custom_tool · 0.1s\"));\n\t});\n\n\tit(\"renders nothing when empty or hidden\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tassert.deepEqual(summary.render(80), []);\n\n\t\tsummary.addTool(\"grep\", 100);\n\t\tsummary.setHidden(true);\n\t\tassert.deepEqual(summary.render(80), []);\n\t});\n});\n"]}
|
|
1
|
+
{"version":3,"file":"tool-summary-line.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/tool-summary-line.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport stripAnsi from \"strip-ansi\";\n\nimport { ToolSummaryLine, extractToolLabel } from \"../tool-summary-line.js\";\nimport { initTheme } from \"../../theme/theme.js\";\n\ninitTheme(\"dark\");\n\ndescribe(\"ToolSummaryLine\", () => {\n\tit(\"renders action-based summaries for grouped identical tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 600);\n\t\tsummary.addTool(\"read\", 150);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.match(rendered, /^ ● /);\n\t\tassert.ok(rendered.includes(\"reading 2 files · 0.8s\"));\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"find\"), true);\n\t\tassert.equal(summary.canGroupWith(\"bash\"), false);\n\t\tassert.equal(rendered.includes(\"collapsed tools\"), false);\n\t\tassert.equal(rendered.includes(\"⎯\"), false);\n\t});\n\n\tit(\"keeps fallback format for unknown tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"custom_tool\", 100);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"custom_tool · 0.1s\"));\n\t});\n\n\tit(\"renders nothing when empty or hidden\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tassert.deepEqual(summary.render(80), []);\n\n\t\tsummary.addTool(\"grep\", 100);\n\t\tsummary.setHidden(true);\n\t\tassert.deepEqual(summary.render(80), []);\n\t});\n\n\tit(\"renders spinner and label line when pending tools exist\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"src/foo/bar.ts\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.startsWith(\" ●\"));\n\t\tassert.ok(rendered.includes(\" └ bar.ts\"));\n\t\tassert.ok(rendered.includes(\"bar.ts\"));\n\t});\n\n\tit(\"keeps last tool label after pending tool completes\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"file.ts\" });\n\t\tassert.equal(summary.hasPendingTools(), true);\n\n\t\tsummary.removePendingTool(\"t1\");\n\t\tsummary.addTool(\"read\", 500);\n\t\tassert.equal(summary.hasPendingTools(), false);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"●\"));\n\t\tassert.ok(rendered.includes(\"0.5s\"));\n\t\tassert.ok(rendered.includes(\"└ file.ts\"));\n\t});\n\n\tit(\"aggregates completed and pending tools in summary text\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addTool(\"read\", 300);\n\t\tsummary.addPendingTool(\"t1\", \"grep\", { pattern: \"TODO\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"1 file\"));\n\t\tassert.ok(rendered.includes(\"1 pattern\"));\n\t\tassert.ok(rendered.includes(\"…\"));\n\t\tassert.ok(rendered.includes(\"TODO\"));\n\t});\n\n\tit(\"shows expand hint when set and tools are pending\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.setExpandHint(\"(ctrl+o to expand)\");\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"test.ts\" });\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"ctrl+o to expand\"));\n\t});\n\n\tit(\"does not show expand hint when no pending tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.setExpandHint(\"(ctrl+o to expand)\");\n\t\tsummary.addTool(\"read\", 300);\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.includes(\"ctrl+o to expand\"));\n\t});\n\n\tit(\"updates label when pending tool args change\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"old.ts\" });\n\n\t\tlet rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"old.ts\"));\n\n\t\tsummary.updatePendingToolArgs(\"t1\", { path: \"new.ts\" });\n\t\trendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(rendered.includes(\"new.ts\"));\n\t});\n\n\tit(\"clears pending spinner without removing last label\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"file.ts\" });\n\n\t\tsummary.clearPendingTools();\n\n\t\tconst rendered = stripAnsi(summary.render(160).join(\"\\n\"));\n\t\tassert.ok(!rendered.includes(\"◯\"));\n\t\tassert.ok(!rendered.includes(\"◔\"));\n\t\tassert.ok(!rendered.includes(\"◑\"));\n\t\tassert.ok(!rendered.includes(\"◕\"));\n\t\tassert.ok(!rendered.includes(\"● Listing\"));\n\t\tassert.ok(!rendered.includes(\"…\"));\n\t\tassert.ok(rendered === \"\" || rendered.includes(\"file.ts\"));\n\t});\n\n\tit(\"canGroupWith considers pending tools\", () => {\n\t\tconst summary = new ToolSummaryLine();\n\t\tsummary.addPendingTool(\"t1\", \"read\", { path: \"a.ts\" });\n\n\t\tassert.equal(summary.canGroupWith(\"read\"), true);\n\t\tassert.equal(summary.canGroupWith(\"grep\"), true);\n\t\tassert.equal(summary.canGroupWith(\"bash\"), false);\n\t});\n});\n\ndescribe(\"extractToolLabel\", () => {\n\tit(\"extracts basename for read tool\", () => {\n\t\tassert.equal(extractToolLabel(\"read\", { path: \"src/foo/bar.ts\" }), \"bar.ts\");\n\t\tassert.equal(extractToolLabel(\"read\", { file_path: \"/abs/path/file.json\" }), \"file.json\");\n\t\tassert.equal(extractToolLabel(\"read\", {}), \"read\");\n\t});\n\n\tit(\"extracts pattern for grep tool\", () => {\n\t\tassert.equal(extractToolLabel(\"grep\", { pattern: \"TODO\" }), \"TODO\");\n\t\tassert.equal(extractToolLabel(\"grep\", {}), \"grep\");\n\t});\n\n\tit(\"extracts pattern for find tool\", () => {\n\t\tassert.equal(extractToolLabel(\"find\", { pattern: \"*.ts\" }), \"*.ts\");\n\t\tassert.equal(extractToolLabel(\"find\", {}), \"find\");\n\t});\n\n\tit(\"extracts path for ls tool\", () => {\n\t\tassert.equal(extractToolLabel(\"ls\", { path: \"src/components\" }), \"components\");\n\t\tassert.equal(extractToolLabel(\"ls\", {}), \".\");\n\t});\n\n\tit(\"extracts symbol or file for lsp tool\", () => {\n\t\tassert.equal(extractToolLabel(\"lsp\", { symbol: \"MyClass\" }), \"MyClass\");\n\t\tassert.equal(extractToolLabel(\"lsp\", { file: \"src/index.ts\" }), \"index.ts\");\n\t\tassert.equal(extractToolLabel(\"lsp\", { symbol: \"foo\", file: \"bar.ts\" }), \"foo\");\n\t\tassert.equal(extractToolLabel(\"lsp\", {}), \"lsp\");\n\t});\n\n\tit(\"returns tool name for unknown tools\", () => {\n\t\tassert.equal(extractToolLabel(\"custom_tool\", { whatever: \"value\" }), \"custom_tool\");\n\t});\n});\n"]}
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import type { AssistantMessage } from "@gsd/pi-ai";
|
|
2
|
-
import {
|
|
2
|
+
import type { Component } from "@gsd/pi-tui";
|
|
3
|
+
import { Container, Markdown, type MarkdownTheme, Text } from "@gsd/pi-tui";
|
|
3
4
|
import { type TimestampFormat } from "./timestamp.js";
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* Create a Markdown component for an assistant text block.
|
|
7
|
+
* @param text - Text content (should be trimmed by caller)
|
|
8
|
+
* @param withMarker - Whether to prefix with the response marker
|
|
9
|
+
* @param markdownTheme - Markdown theme
|
|
10
|
+
*/
|
|
11
|
+
export declare function createTextMarkdown(text: string, withMarker: boolean, markdownTheme: MarkdownTheme): Markdown;
|
|
12
|
+
/**
|
|
13
|
+
* Create a Markdown component for a thinking block.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createThinkingMarkdown(thinking: string, markdownTheme: MarkdownTheme): Markdown;
|
|
16
|
+
/**
|
|
17
|
+
* Create an error/abort Text component.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createErrorText(message: string): Text;
|
|
20
|
+
/**
|
|
21
|
+
* Component that renders a complete assistant message.
|
|
22
|
+
*
|
|
23
|
+
* Supports two rendering modes:
|
|
24
|
+
* 1. Legacy: `updateContent(message)` renders all text/thinking into a contentContainer.
|
|
25
|
+
* Tool rows are expected to be added as siblings in the parent container.
|
|
26
|
+
* 2. Interleaved: `updateContentOrdered(message, toolComponents)` renders text/thinking
|
|
27
|
+
* AND tool components in content order. Tool components become children of this container.
|
|
6
28
|
*/
|
|
7
29
|
export declare class AssistantMessageComponent extends Container {
|
|
8
30
|
private contentContainer;
|
|
@@ -15,6 +37,21 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
15
37
|
invalidate(): void;
|
|
16
38
|
setHideThinkingBlock(hide: boolean): void;
|
|
17
39
|
setThinkingLevel(level: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Legacy rendering: renders text/thinking blocks into contentContainer.
|
|
42
|
+
* Stops rendering at the first tool-type block (toolCall/serverToolUse).
|
|
43
|
+
* Post-tool text blocks are handled by the chat-controller to preserve
|
|
44
|
+
* content ordering relative to tool rows.
|
|
45
|
+
*/
|
|
18
46
|
updateContent(message: AssistantMessage): void;
|
|
47
|
+
/**
|
|
48
|
+
* Interleaved rendering: renders text/thinking AND tool components in content order.
|
|
49
|
+
* Tool components become children of this container, preserving visual ordering.
|
|
50
|
+
*
|
|
51
|
+
* @param message - The assistant message
|
|
52
|
+
* @param toolComponents - Map of content block ID → pre-created Component (ToolExecutionComponent etc.)
|
|
53
|
+
* @returns Map of content block ID → the tool Component that was placed (for pending tool tracking)
|
|
54
|
+
*/
|
|
55
|
+
updateContentOrdered(message: AssistantMessage, toolComponents?: Map<string, Component>): Map<string, Component>;
|
|
19
56
|
}
|
|
20
57
|
//# sourceMappingURL=assistant-message.d.ts.map
|
package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAU,IAAI,EAAE,MAAM,aAAa,CAAC;AAEpF,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAUvE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,OAAO,EACnB,aAAa,EAAE,aAAa,GAC1B,QAAQ,CAGV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,aAAa,GAC1B,QAAQ,CAKV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAErD;AAED;;;;;;;;GAQG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,eAAe,CAAkB;gBAGxC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,eAAe,GAAE,eAAiC,EAClD,aAAa,SAAQ;IAkBb,UAAU,IAAI,IAAI;IAO3B,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAIzC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIrC;;;;;OAKG;IACH,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IA8E9C;;;;;;;OAOG;IACH,oBAAoB,CACnB,OAAO,EAAE,gBAAgB,EACzB,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GACrC,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;CA+EzB"}
|