@oh-my-pi/pi-coding-agent 16.0.10 → 16.1.0
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 +57 -0
- package/dist/cli.js +3344 -3371
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/transcript-recorder.d.ts +52 -0
- package/dist/types/commit/agentic/agent.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +14 -8
- package/dist/types/edit/file-snapshot-store.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/modes/components/__tests__/skill-message.test.d.ts +1 -0
- package/dist/types/modes/components/agent-hub.d.ts +6 -1
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +39 -0
- package/dist/types/modes/components/assistant-message.d.ts +8 -0
- package/dist/types/modes/components/cache-invalidation-marker.d.ts +34 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +42 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +14 -1
- package/dist/types/modes/components/index.d.ts +0 -1
- package/dist/types/modes/components/message-frame.d.ts +6 -4
- package/dist/types/modes/controllers/command-controller.d.ts +3 -2
- package/dist/types/modes/interactive-mode.d.ts +4 -2
- package/dist/types/modes/theme/theme.d.ts +7 -1
- package/dist/types/modes/types.d.ts +9 -2
- package/dist/types/registry/agent-registry.d.ts +10 -3
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +20 -1
- package/dist/types/session/compact-modes.d.ts +60 -0
- package/dist/types/session/session-context.d.ts +7 -0
- package/dist/types/session/session-dump-format.d.ts +1 -0
- package/dist/types/session/streaming-output.d.ts +0 -2
- package/dist/types/session/tool-choice-queue.d.ts +14 -0
- package/dist/types/system-prompt.d.ts +3 -3
- package/dist/types/tools/__tests__/json-tree.test.d.ts +1 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/resolve.d.ts +15 -5
- package/package.json +12 -12
- package/src/advisor/index.ts +1 -0
- package/src/advisor/transcript-recorder.ts +136 -0
- package/src/cli/stats-cli.ts +2 -11
- package/src/collab/host.ts +25 -13
- package/src/commit/agentic/agent.ts +2 -1
- package/src/commit/agentic/tools/git-file-diff.ts +2 -2
- package/src/commit/changelog/index.ts +1 -1
- package/src/commit/map-reduce/map-phase.ts +1 -1
- package/src/commit/map-reduce/utils.ts +1 -1
- package/src/config/settings-schema.ts +16 -9
- package/src/config/settings.ts +0 -6
- package/src/debug/log-viewer.ts +4 -4
- package/src/debug/raw-sse.ts +4 -4
- package/src/edit/file-snapshot-store.ts +1 -1
- package/src/edit/renderer.ts +9 -9
- package/src/eval/js/tool-bridge.ts +3 -2
- package/src/eval/py/prelude.py +3 -2
- package/src/export/html/tool-views.generated.js +28 -28
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/history-protocol.ts +8 -3
- package/src/irc/bus.ts +8 -0
- package/src/lsp/index.ts +2 -2
- package/src/lsp/render.ts +7 -7
- package/src/main.ts +4 -1
- package/src/modes/acp/acp-agent.ts +63 -0
- package/src/modes/components/__tests__/skill-message.test.ts +92 -0
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/agent-hub.ts +97 -920
- package/src/modes/components/agent-transcript-viewer.ts +461 -0
- package/src/modes/components/assistant-message.ts +21 -0
- package/src/modes/components/cache-invalidation-marker.ts +84 -0
- package/src/modes/components/chat-transcript-builder.ts +476 -0
- package/src/modes/components/compaction-summary-message.ts +29 -1
- package/src/modes/components/custom-message.ts +4 -1
- package/src/modes/components/diff.ts +12 -35
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +5 -5
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/index.ts +0 -1
- package/src/modes/components/message-frame.ts +10 -6
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/overlay-box.ts +10 -9
- package/src/modes/components/skill-message.ts +39 -19
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +12 -2
- package/src/modes/controllers/event-controller.ts +15 -1
- package/src/modes/controllers/input-controller.ts +8 -1
- package/src/modes/controllers/selector-controller.ts +11 -1
- package/src/modes/interactive-mode.ts +13 -3
- package/src/modes/theme/theme.ts +14 -0
- package/src/modes/types.ts +9 -2
- package/src/modes/utils/ui-helpers.ts +20 -2
- package/src/prompts/steering/user-interjection.md +3 -4
- package/src/prompts/tools/read.md +1 -1
- package/src/registry/agent-registry.ts +13 -4
- package/src/sdk.ts +9 -7
- package/src/session/agent-session.ts +182 -16
- package/src/session/compact-modes.ts +105 -0
- package/src/session/messages.ts +7 -9
- package/src/session/session-context.ts +54 -7
- package/src/session/session-dump-format.ts +4 -2
- package/src/session/session-history-format.ts +1 -1
- package/src/session/snapcompact-inline.ts +2 -2
- package/src/session/streaming-output.ts +5 -5
- package/src/session/tool-choice-queue.ts +59 -0
- package/src/slash-commands/builtin-registry.ts +16 -4
- package/src/system-prompt.ts +10 -9
- package/src/task/executor.ts +1 -1
- package/src/task/output-manager.ts +5 -0
- package/src/tools/__tests__/json-tree.test.ts +35 -0
- package/src/tools/approval.ts +1 -1
- package/src/tools/bash-interactive.ts +4 -4
- package/src/tools/bash.ts +0 -1
- package/src/tools/browser.ts +0 -1
- package/src/tools/eval.ts +1 -1
- package/src/tools/gh.ts +1 -1
- package/src/tools/index.ts +4 -0
- package/src/tools/irc.ts +1 -1
- package/src/tools/json-tree.ts +22 -5
- package/src/tools/read.ts +5 -6
- package/src/tools/resolve.ts +66 -41
- package/src/tui/output-block.ts +9 -9
- package/src/web/scrapers/firefox-addons.ts +1 -1
- package/src/web/scrapers/github.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/metacpan.ts +2 -2
- package/src/web/scrapers/nvd.ts +2 -2
- package/src/web/scrapers/ollama.ts +1 -1
- package/src/web/scrapers/opencorporates.ts +1 -1
- package/src/web/scrapers/pub-dev.ts +1 -1
- package/src/web/scrapers/repology.ts +1 -1
- package/src/web/scrapers/sourcegraph.ts +1 -1
- package/src/web/scrapers/terraform.ts +6 -6
- package/src/web/scrapers/wikidata.ts +2 -2
- package/src/workspace-tree.ts +1 -1
- package/dist/types/modes/components/branch-summary-message.d.ts +0 -13
- package/src/modes/components/branch-summary-message.ts +0 -46
|
@@ -40,9 +40,12 @@ export class HistoryProtocolHandler implements ProtocolHandler {
|
|
|
40
40
|
async resolve(url: InternalUrl): Promise<InternalResource> {
|
|
41
41
|
const agentId = url.rawHost || url.hostname;
|
|
42
42
|
const registry = AgentRegistry.global();
|
|
43
|
+
// Advisor transcripts are observability-only — surfaced in the Agent Hub, never
|
|
44
|
+
// in the agent-facing roster. Hide them from the index, lookup, and completions.
|
|
45
|
+
const visible = registry.list().filter(ref => ref.kind !== "advisor");
|
|
43
46
|
|
|
44
47
|
if (!agentId) {
|
|
45
|
-
const content = this.#renderIndex(
|
|
48
|
+
const content = this.#renderIndex(visible);
|
|
46
49
|
return {
|
|
47
50
|
url: url.href,
|
|
48
51
|
content,
|
|
@@ -52,13 +55,14 @@ export class HistoryProtocolHandler implements ProtocolHandler {
|
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
let ref = registry.get(agentId);
|
|
58
|
+
if (ref?.kind === "advisor") ref = undefined;
|
|
55
59
|
if (!ref) {
|
|
56
60
|
// Case-insensitive fallback: agent ids are human-typed (e.g. AuthLoader).
|
|
57
61
|
const lower = agentId.toLowerCase();
|
|
58
|
-
ref =
|
|
62
|
+
ref = visible.find(candidate => candidate.id.toLowerCase() === lower);
|
|
59
63
|
}
|
|
60
64
|
if (!ref) {
|
|
61
|
-
const known =
|
|
65
|
+
const known = visible.map(candidate => candidate.id);
|
|
62
66
|
const knownStr = known.length > 0 ? known.join(", ") : "none";
|
|
63
67
|
throw new Error(`Unknown agent: ${agentId}\nKnown agents: ${knownStr}\nList all with history://`);
|
|
64
68
|
}
|
|
@@ -105,6 +109,7 @@ export class HistoryProtocolHandler implements ProtocolHandler {
|
|
|
105
109
|
async complete(): Promise<UrlCompletion[]> {
|
|
106
110
|
return AgentRegistry.global()
|
|
107
111
|
.list()
|
|
112
|
+
.filter(ref => ref.kind !== "advisor")
|
|
108
113
|
.map(ref => ({
|
|
109
114
|
value: ref.id,
|
|
110
115
|
description: `${ref.status} · ${ref.kind}${ref.parentId ? ` · parent ${ref.parentId}` : ""}`,
|
package/src/irc/bus.ts
CHANGED
|
@@ -98,6 +98,14 @@ export class IrcBus {
|
|
|
98
98
|
if (!ref || ref.status === "aborted") {
|
|
99
99
|
return { to: message.to, outcome: "failed", error: `Unknown or terminated agent "${message.to}".` };
|
|
100
100
|
}
|
|
101
|
+
// Advisor refs are observability-only transcripts, never messageable peers.
|
|
102
|
+
if (ref.kind === "advisor") {
|
|
103
|
+
return {
|
|
104
|
+
to: message.to,
|
|
105
|
+
outcome: "failed",
|
|
106
|
+
error: `Agent "${message.to}" is a read-only advisor transcript and cannot be messaged.`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
101
109
|
|
|
102
110
|
let revived = false;
|
|
103
111
|
if (ref.status === "parked") {
|
package/src/lsp/index.ts
CHANGED
|
@@ -598,7 +598,7 @@ async function runWorkspaceDiagnostics(
|
|
|
598
598
|
// Limit output length
|
|
599
599
|
const lines = combined.split("\n");
|
|
600
600
|
if (lines.length > 50) {
|
|
601
|
-
return { output: `${lines.slice(0, 50).join("\n")}\n
|
|
601
|
+
return { output: `${lines.slice(0, 50).join("\n")}\n[…${lines.length - 50}ln elided…]`, projectType };
|
|
602
602
|
}
|
|
603
603
|
return { output: combined, projectType };
|
|
604
604
|
} catch (e) {
|
|
@@ -2035,7 +2035,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
2035
2035
|
const lines = limitedSymbols.map(s => formatSymbolInformation(s, this.session.cwd));
|
|
2036
2036
|
const truncationLine =
|
|
2037
2037
|
dedupedSymbols.length > WORKSPACE_SYMBOL_LIMIT
|
|
2038
|
-
? `\n
|
|
2038
|
+
? `\n[…${dedupedSymbols.length - WORKSPACE_SYMBOL_LIMIT} symbols elided…]`
|
|
2039
2039
|
: "";
|
|
2040
2040
|
return {
|
|
2041
2041
|
content: [
|
package/src/lsp/render.ts
CHANGED
|
@@ -223,10 +223,10 @@ function renderHover(
|
|
|
223
223
|
const langLabel = lang ? theme.fg("mdCodeBlockBorder", ` ${lang}`) : "";
|
|
224
224
|
|
|
225
225
|
if (expanded) {
|
|
226
|
-
const h = theme.
|
|
227
|
-
const v = theme.
|
|
228
|
-
const top = `${theme.
|
|
229
|
-
const bottom = `${theme.
|
|
226
|
+
const h = theme.boxRound.horizontal;
|
|
227
|
+
const v = theme.boxRound.vertical;
|
|
228
|
+
const top = `${theme.boxRound.topLeft}${h.repeat(3)}`;
|
|
229
|
+
const bottom = `${theme.boxRound.bottomLeft}${h.repeat(3)}`;
|
|
230
230
|
let output = `${icon}${langLabel}`;
|
|
231
231
|
if (beforeCode) {
|
|
232
232
|
for (const line of beforeCode.split("\n")) {
|
|
@@ -254,9 +254,9 @@ function renderHover(
|
|
|
254
254
|
const preview = truncateToWidth(beforeCode, TRUNCATE_LENGTHS.TITLE);
|
|
255
255
|
output += `\n ${theme.fg("dim", theme.tree.branch)} ${theme.fg("muted", preview)}`;
|
|
256
256
|
}
|
|
257
|
-
const h = theme.
|
|
258
|
-
const v = theme.
|
|
259
|
-
const bottom = `${theme.
|
|
257
|
+
const h = theme.boxRound.horizontal;
|
|
258
|
+
const v = theme.boxRound.vertical;
|
|
259
|
+
const bottom = `${theme.boxRound.bottomLeft}${h.repeat(3)}`;
|
|
260
260
|
output += `\n ${theme.fg("mdCodeBlockBorder", v)} ${firstCodeLine}`;
|
|
261
261
|
|
|
262
262
|
if (codeLines.length > 1) {
|
package/src/main.ts
CHANGED
|
@@ -133,9 +133,12 @@ const HOST_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
|
|
133
133
|
"memory.backend",
|
|
134
134
|
"memories.enabled",
|
|
135
135
|
// Advisor is interactive-session assistance. Protocol hosts opt in explicitly
|
|
136
|
-
// instead of inheriting a user's globally-enabled local preference
|
|
136
|
+
// instead of inheriting a user's globally-enabled local preference, and when
|
|
137
|
+
// they do opt in they get the default tuning rather than the user's local tuning.
|
|
137
138
|
"advisor.enabled",
|
|
138
139
|
"advisor.subagents",
|
|
140
|
+
"advisor.syncBacklog",
|
|
141
|
+
"advisor.immuneTurns",
|
|
139
142
|
];
|
|
140
143
|
|
|
141
144
|
const RPC_BACKGROUND_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
|
@@ -68,10 +68,17 @@ import type { SessionInfo as StoredSessionInfo } from "../../session/session-lis
|
|
|
68
68
|
import { SessionManager } from "../../session/session-manager";
|
|
69
69
|
import { executeAcpBuiltinSlashCommand } from "../../slash-commands/acp-builtins";
|
|
70
70
|
import { buildAvailableSlashCommands, toAcpAvailableCommands } from "../../slash-commands/available-commands";
|
|
71
|
+
import { DEFAULT_STT_MODEL_KEY, STT_MODEL_OPTIONS } from "../../stt/models";
|
|
71
72
|
import { AUTO_THINKING, parseConfiguredThinkingLevel } from "../../thinking";
|
|
72
73
|
import { normalizeLocalScheme } from "../../tools/path-utils";
|
|
73
74
|
import { runResolveInvocation } from "../../tools/resolve";
|
|
74
75
|
import { ToolError } from "../../tools/tool-errors";
|
|
76
|
+
import {
|
|
77
|
+
DEFAULT_TTS_LOCAL_MODEL_KEY,
|
|
78
|
+
DEFAULT_TTS_VOICE,
|
|
79
|
+
TTS_LOCAL_MODELS,
|
|
80
|
+
TTS_LOCAL_VOICE_OPTIONS,
|
|
81
|
+
} from "../../tts/models";
|
|
75
82
|
import { canonicalizeMessage } from "../../utils/thinking-display";
|
|
76
83
|
import { createAcpClientBridge } from "./acp-client-bridge";
|
|
77
84
|
import {
|
|
@@ -91,6 +98,7 @@ const MODEL_CONFIG_ID = "model";
|
|
|
91
98
|
const THINKING_CONFIG_ID = "thinking";
|
|
92
99
|
const THINKING_OFF = "off";
|
|
93
100
|
const SESSION_PAGE_SIZE = 50;
|
|
101
|
+
const SPEECH_MODELS_LIST_METHOD = "speech.models.list";
|
|
94
102
|
/**
|
|
95
103
|
* Delay between `session/new` (or `session/load` / `session/resume` /
|
|
96
104
|
* `unstable_session/fork`) returning and the agent firing the first
|
|
@@ -195,6 +203,59 @@ type MCPSourceMap = {
|
|
|
195
203
|
|
|
196
204
|
type CreateAcpSession = (cwd: string) => Promise<AgentSession>;
|
|
197
205
|
|
|
206
|
+
type AcpSpeechOption = {
|
|
207
|
+
value: string;
|
|
208
|
+
label: string;
|
|
209
|
+
description?: string;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
type AcpSpeechVoiceOption = {
|
|
213
|
+
value: string;
|
|
214
|
+
label: string;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
type AcpSpeechTtsModelOption = AcpSpeechOption & {
|
|
218
|
+
voices: AcpSpeechVoiceOption[];
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
function buildAcpSpeechModelsCatalog(): Record<string, unknown> {
|
|
222
|
+
const voices = TTS_LOCAL_VOICE_OPTIONS.map(({ value, label }) => ({ value, label }));
|
|
223
|
+
return {
|
|
224
|
+
settings: {
|
|
225
|
+
speechToTextModel: "stt.modelName",
|
|
226
|
+
textToSpeechModel: "tts.localModel",
|
|
227
|
+
textToSpeechVoice: "tts.localVoice",
|
|
228
|
+
speechVoice: "speech.voice",
|
|
229
|
+
},
|
|
230
|
+
defaults: {
|
|
231
|
+
speechToTextModel: DEFAULT_STT_MODEL_KEY,
|
|
232
|
+
textToSpeechModel: DEFAULT_TTS_LOCAL_MODEL_KEY,
|
|
233
|
+
voice: DEFAULT_TTS_VOICE,
|
|
234
|
+
},
|
|
235
|
+
speechToText: {
|
|
236
|
+
setting: "stt.modelName",
|
|
237
|
+
defaultValue: DEFAULT_STT_MODEL_KEY,
|
|
238
|
+
models: STT_MODEL_OPTIONS.map(({ value, label, description }) => ({ value, label, description })),
|
|
239
|
+
},
|
|
240
|
+
textToSpeech: {
|
|
241
|
+
modelSetting: "tts.localModel",
|
|
242
|
+
voiceSetting: "tts.localVoice",
|
|
243
|
+
speechVoiceSetting: "speech.voice",
|
|
244
|
+
defaultModel: DEFAULT_TTS_LOCAL_MODEL_KEY,
|
|
245
|
+
defaultVoice: DEFAULT_TTS_VOICE,
|
|
246
|
+
models: TTS_LOCAL_MODELS.map(
|
|
247
|
+
({ key, label, description, voices: modelVoices }): AcpSpeechTtsModelOption => ({
|
|
248
|
+
value: key,
|
|
249
|
+
label,
|
|
250
|
+
description,
|
|
251
|
+
voices: modelVoices.map(({ id, label: voiceLabel }) => ({ value: id, label: voiceLabel })),
|
|
252
|
+
}),
|
|
253
|
+
),
|
|
254
|
+
voices,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
198
259
|
/**
|
|
199
260
|
* Bridge a single ExtensionUIContext call to the ACP `unstable_createElicitation`
|
|
200
261
|
* surface. Skills/extensions ask for one value at a time (a chosen option, a
|
|
@@ -850,6 +911,8 @@ export class AcpAgent implements Agent {
|
|
|
850
911
|
|
|
851
912
|
async extMethod(method: string, params: { [key: string]: unknown }): Promise<{ [key: string]: unknown }> {
|
|
852
913
|
switch (method) {
|
|
914
|
+
case SPEECH_MODELS_LIST_METHOD:
|
|
915
|
+
return buildAcpSpeechModelsCatalog();
|
|
853
916
|
case "_omp/sessions/listAll": {
|
|
854
917
|
const limit = typeof params.limit === "number" ? Math.max(1, Math.min(5000, params.limit as number)) : 1000;
|
|
855
918
|
const sessions = await SessionManager.listAll();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "bun:test";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { Settings } from "../../../config/settings";
|
|
5
|
+
import type { CustomMessage, SkillPromptDetails } from "../../../session/messages";
|
|
6
|
+
import { getThemeByName, setThemeInstance, type Theme } from "../../theme/theme";
|
|
7
|
+
import { SkillMessageComponent } from "../skill-message";
|
|
8
|
+
|
|
9
|
+
// Drop SGR colors and OSC 8 hyperlink wrappers so assertions see the visible text only.
|
|
10
|
+
const strip = (lines: readonly string[]): string =>
|
|
11
|
+
lines
|
|
12
|
+
.join("\n")
|
|
13
|
+
.replace(/\x1b\]8;[^\x1b\x07]*(?:\x07|\x1b\\)/g, "")
|
|
14
|
+
.replace(/\x1b\[[0-9;]*m/g, "");
|
|
15
|
+
|
|
16
|
+
function makeMessage(
|
|
17
|
+
details: SkillPromptDetails,
|
|
18
|
+
content = "Use the atomic-commit workflow.",
|
|
19
|
+
): CustomMessage<SkillPromptDetails> {
|
|
20
|
+
return { role: "custom", customType: "skill-prompt", content, display: true, details, timestamp: Date.now() };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("SkillMessageComponent", () => {
|
|
24
|
+
let uiTheme: Theme;
|
|
25
|
+
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
await Settings.init({ inMemory: true });
|
|
28
|
+
const loaded = await getThemeByName("dark");
|
|
29
|
+
if (!loaded) throw new Error("theme unavailable");
|
|
30
|
+
uiTheme = loaded;
|
|
31
|
+
setThemeInstance(uiTheme);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const skillPath = path.join(os.homedir(), ".agent/skills/atomic-commit/SKILL.md");
|
|
35
|
+
|
|
36
|
+
it("renders a compact, outlined card instead of the archaic key:value dump", () => {
|
|
37
|
+
const component = new SkillMessageComponent(
|
|
38
|
+
makeMessage({ name: "atomic-commit", path: skillPath, lineCount: 88 }),
|
|
39
|
+
);
|
|
40
|
+
const text = strip(component.render(80));
|
|
41
|
+
|
|
42
|
+
// New look: an icon-tagged "skill" header with the name and a single meta line.
|
|
43
|
+
expect(text).toContain("skill");
|
|
44
|
+
expect(text).toContain("atomic-commit");
|
|
45
|
+
expect(text).toContain("88 lines");
|
|
46
|
+
|
|
47
|
+
// The card is drawn with an outline.
|
|
48
|
+
expect(text).toContain(uiTheme.boxRound.topLeft);
|
|
49
|
+
expect(text).toContain(uiTheme.boxRound.bottomRight);
|
|
50
|
+
|
|
51
|
+
// Path is home-shortened and never leaks the absolute home dir.
|
|
52
|
+
expect(text).toContain("~/.agent/skills/atomic-commit/SKILL.md");
|
|
53
|
+
expect(text).not.toContain(os.homedir());
|
|
54
|
+
|
|
55
|
+
// The old archaic framing is gone.
|
|
56
|
+
expect(text).not.toContain("[skill]");
|
|
57
|
+
expect(text).not.toContain("Skill:");
|
|
58
|
+
expect(text).not.toContain("Path:");
|
|
59
|
+
expect(text).not.toContain("Prompt:");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("flattens multi-line args onto the single-line header", () => {
|
|
63
|
+
const component = new SkillMessageComponent(
|
|
64
|
+
makeMessage({ name: "atomic-commit", path: skillPath, lineCount: 88, args: "stage all\nthen split" }),
|
|
65
|
+
);
|
|
66
|
+
const text = strip(component.render(80));
|
|
67
|
+
// Whitespace (including the newline) collapsed to single spaces so the header can't break.
|
|
68
|
+
expect(text).toContain("stage all then split");
|
|
69
|
+
expect(text).not.toContain("stage all\nthen split");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("uses a singular unit for a one-line prompt", () => {
|
|
73
|
+
const component = new SkillMessageComponent(makeMessage({ name: "tiny", path: skillPath, lineCount: 1 }));
|
|
74
|
+
const text = strip(component.render(80));
|
|
75
|
+
expect(text).toContain("1 line");
|
|
76
|
+
expect(text).not.toContain("1 lines");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("reveals the prompt body under a calm subheader only when expanded", () => {
|
|
80
|
+
const details: SkillPromptDetails = { name: "atomic-commit", path: skillPath, lineCount: 88 };
|
|
81
|
+
const body = "Step one: stage hunks.";
|
|
82
|
+
|
|
83
|
+
const collapsed = new SkillMessageComponent(makeMessage(details, body));
|
|
84
|
+
expect(strip(collapsed.render(80))).not.toContain(body);
|
|
85
|
+
|
|
86
|
+
const expanded = new SkillMessageComponent(makeMessage(details, body));
|
|
87
|
+
expanded.setExpanded(true);
|
|
88
|
+
const text = strip(expanded.render(80));
|
|
89
|
+
expect(text).toContain("prompt");
|
|
90
|
+
expect(text).toContain(body);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -321,7 +321,7 @@ class TwoColumnBody implements Component {
|
|
|
321
321
|
const rightLines = this.rightPane.render(rightWidth);
|
|
322
322
|
const lineCount = this.maxHeight;
|
|
323
323
|
const out: string[] = [];
|
|
324
|
-
const separator = theme.fg("dim", ` ${theme.
|
|
324
|
+
const separator = theme.fg("dim", ` ${theme.boxRound.vertical} `);
|
|
325
325
|
|
|
326
326
|
for (let i = 0; i < lineCount; i++) {
|
|
327
327
|
const left = truncateToWidth(leftLines[i] ?? "", leftWidth);
|