@sayknow-cli/coding-agent 0.3.5 → 0.3.7
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/README.md +76 -0
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/cli/local-provider-smoke.d.ts +59 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/telegram-cli.d.ts +13 -0
- package/dist/types/cli/update-cli.d.ts +24 -1
- package/dist/types/commands/local-provider.d.ts +34 -0
- package/dist/types/commands/telegram.d.ts +22 -0
- package/dist/types/config/keybindings.d.ts +12 -6
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +284 -4
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/config/telegram-autostart.d.ts +14 -0
- package/dist/types/config/telegram-env-bridge.d.ts +18 -0
- package/dist/types/coordinator-mcp/server.d.ts +8 -0
- package/dist/types/defaults/skc-defaults.d.ts +7 -0
- package/dist/types/discovery/cursor.d.ts +4 -1
- package/dist/types/discovery/index.d.ts +0 -1
- package/dist/types/discovery/windsurf.d.ts +3 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/i18n/messages/en.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -1
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +5 -1
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -7
- package/dist/types/modes/components/status-line.d.ts +15 -3
- package/dist/types/modes/components/two-column-body.d.ts +9 -0
- package/dist/types/modes/components/welcome.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +6 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +10 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +41 -7
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/tmux-scroll.d.ts +18 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
- package/dist/types/notifications/daemon-paths.d.ts +9 -0
- package/dist/types/notifications/html-format.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +5 -1
- package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
- package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
- package/dist/types/notifications/telegram-daemon.d.ts +4 -10
- package/dist/types/notifications/telegram-reference.d.ts +4 -0
- package/dist/types/notifications/threaded-render.d.ts +1 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +9 -0
- package/dist/types/runtime-mcp/config.d.ts +2 -0
- package/dist/types/runtime-mcp/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/skc-runtime/launch-tmux.d.ts +2 -2
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/skc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +5 -0
- package/dist/types/skc-runtime/state-writer.d.ts +1 -0
- package/dist/types/skc-runtime/team-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/tmux-common.d.ts +12 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +7 -1
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +0 -4
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +7 -0
- package/dist/types/tools/skill.d.ts +1 -1
- package/dist/types/utils/cmux-workspace.d.ts +52 -0
- package/dist/types/web/search/index.d.ts +4 -2
- package/dist/types/web/search/provider.d.ts +14 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/web/search/render.d.ts +1 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/build-binary.ts +3 -0
- package/scripts/verify-insane-vendor.ts +0 -5
- package/src/capability/mcp.ts +2 -0
- package/src/cli/local-provider-smoke.ts +632 -0
- package/src/cli/notify-cli.ts +4 -2
- package/src/cli/telegram-cli.ts +156 -0
- package/src/cli/update-cli.ts +179 -13
- package/src/cli/web-search-cli.ts +5 -7
- package/src/cli.ts +20 -1
- package/src/commands/launch.ts +47 -2
- package/src/commands/local-provider.ts +62 -0
- package/src/commands/notify.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/commands/telegram.ts +47 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-registry.ts +55 -1
- package/src/config/models-config-schema.ts +9 -0
- package/src/config/settings-schema.ts +256 -4
- package/src/config/settings.ts +11 -0
- package/src/config/telegram-autostart.ts +107 -0
- package/src/config/telegram-env-bridge.ts +92 -0
- package/src/coordinator-mcp/server.ts +276 -34
- package/src/defaults/skc/skills/ralplan/SKILL.md +29 -20
- package/src/defaults/skc/skills/ultragoal/SKILL.md +37 -112
- package/src/defaults/skc-defaults.ts +16 -0
- package/src/discovery/builtin.ts +12 -0
- package/src/discovery/cursor.ts +5 -84
- package/src/discovery/gemini.ts +3 -89
- package/src/discovery/index.ts +4 -1
- package/src/discovery/mcp-json.ts +11 -0
- package/src/discovery/opencode.ts +5 -122
- package/src/discovery/windsurf.ts +4 -82
- package/src/edit/read-file.ts +9 -1
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/skills.ts +30 -2
- package/src/hooks/skill-state.ts +33 -73
- package/src/i18n/messages/de.ts +1 -0
- package/src/i18n/messages/en.ts +1 -0
- package/src/i18n/messages/es.ts +1 -0
- package/src/i18n/messages/fr.ts +1 -0
- package/src/i18n/messages/ja.settings.ts +28 -0
- package/src/i18n/messages/ja.ts +1 -0
- package/src/i18n/messages/ko.settings.ts +28 -0
- package/src/i18n/messages/ko.ts +1 -0
- package/src/i18n/messages/zh.settings.ts +28 -0
- package/src/i18n/messages/zh.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +12 -7
- package/src/main.ts +5 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/components/agent-dashboard.ts +3 -35
- package/src/modes/components/custom-editor.ts +26 -0
- package/src/modes/components/custom-model-preset-wizard.ts +30 -189
- package/src/modes/components/extensions/extension-dashboard.ts +2 -48
- package/src/modes/components/hook-selector.ts +21 -3
- package/src/modes/components/model-selector.ts +246 -11
- package/src/modes/components/session-observer-overlay.ts +3 -3
- package/src/modes/components/session-selector.ts +19 -9
- package/src/modes/components/settings-selector.ts +551 -11
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line/presets.ts +11 -0
- package/src/modes/components/status-line/segments.ts +16 -19
- package/src/modes/components/status-line/types.ts +6 -2
- package/src/modes/components/status-line.ts +281 -117
- package/src/modes/components/tree-selector.ts +28 -8
- package/src/modes/components/two-column-body.ts +36 -0
- package/src/modes/components/welcome.ts +165 -30
- package/src/modes/controllers/command-controller.ts +26 -2
- package/src/modes/controllers/event-controller.ts +102 -4
- package/src/modes/controllers/extension-ui-controller.ts +21 -2
- package/src/modes/controllers/input-controller.ts +214 -47
- package/src/modes/controllers/selector-controller.ts +35 -33
- package/src/modes/interactive-mode.ts +36 -30
- package/src/modes/prompt-action-autocomplete.ts +48 -8
- package/src/modes/rpc/rpc-types.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +72 -15
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/theme/theme.ts +5 -1
- package/src/modes/tmux-scroll.ts +92 -0
- package/src/modes/types.ts +8 -1
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/terminal-bell.ts +48 -0
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/daemon-paths.ts +22 -0
- package/src/notifications/html-format.ts +75 -1
- package/src/notifications/index.ts +32 -2
- package/src/notifications/lifecycle-control-runtime.ts +12 -3
- package/src/notifications/recent-activity.ts +32 -8
- package/src/notifications/telegram-daemon-cli.ts +125 -13
- package/src/notifications/telegram-daemon-control.ts +9 -0
- package/src/notifications/telegram-daemon.ts +88 -46
- package/src/notifications/telegram-reference.ts +36 -14
- package/src/notifications/threaded-render.ts +14 -6
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +20 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/runtime-mcp/config-writer.ts +28 -0
- package/src/runtime-mcp/config.ts +7 -0
- package/src/runtime-mcp/loader.ts +3 -0
- package/src/runtime-mcp/manager.ts +3 -0
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk.ts +10 -11
- package/src/session/agent-session.ts +103 -13
- package/src/session/session-manager.ts +58 -18
- package/src/skc-runtime/launch-tmux.ts +34 -8
- package/src/skc-runtime/launch-worktree.ts +16 -2
- package/src/skc-runtime/ralplan-runtime.ts +22 -6
- package/src/skc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/skc-runtime/session-state-sidecar.ts +167 -24
- package/src/skc-runtime/state-runtime.ts +8 -5
- package/src/skc-runtime/state-writer.ts +5 -5
- package/src/skc-runtime/team-runtime.ts +21 -6
- package/src/skc-runtime/tmux-common.ts +16 -0
- package/src/skc-runtime/tmux-sessions.ts +13 -9
- package/src/skc-runtime/ultragoal-guard.ts +150 -24
- package/src/skc-runtime/ultragoal-runtime.ts +16 -124
- package/src/skc-runtime/workflow-manifest.generated.json +0 -8
- package/src/skc-runtime/workflow-manifest.ts +0 -1
- package/src/slash-commands/builtin-registry.ts +136 -23
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/executor.ts +5 -0
- package/src/tools/ast-grep.ts +10 -7
- package/src/tools/bash.ts +39 -14
- package/src/tools/index.ts +8 -1
- package/src/tools/skill.ts +1 -1
- package/src/utils/cmux-workspace.ts +233 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +137 -52
- package/src/web/search/provider.ts +80 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/web/search/render.ts +3 -0
- package/src/workflow/workflow-intent-diff.ts +199 -0
- package/vendor/insane-search/MANIFEST.json +1 -3
- package/vendor/insane-search/engine/tests/test_u1.py +0 -28
- package/vendor/insane-search/engine/transport.py +2 -3
- package/vendor/insane-search/engine/validators.py +0 -14
- package/src/defaults/skc/rules/ponytail.md +0 -68
|
@@ -15,6 +15,12 @@ export interface LspServerInfo {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export type WelcomeLogoMode = "unicode" | "square" | "ascii";
|
|
18
|
+
export interface WelcomeComponentOptions {
|
|
19
|
+
getViewportRows?: () => number | undefined;
|
|
20
|
+
getReservedBottomRows?: (termWidth: number) => number;
|
|
21
|
+
changelogMarkdown?: string;
|
|
22
|
+
collapseChangelog?: boolean;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
/**
|
|
20
26
|
* Sayknow-CLI launch surface: a blue-gradient SAYKNOW wordmark, compact
|
|
@@ -32,6 +38,7 @@ export class WelcomeComponent implements Component {
|
|
|
32
38
|
private recentSessions: RecentSession[] = [],
|
|
33
39
|
private lspServers: LspServerInfo[] = [],
|
|
34
40
|
private readonly logoMode: WelcomeLogoMode = "unicode",
|
|
41
|
+
private readonly options: WelcomeComponentOptions = {},
|
|
35
42
|
) {}
|
|
36
43
|
|
|
37
44
|
invalidate(): void {}
|
|
@@ -52,6 +59,11 @@ export class WelcomeComponent implements Component {
|
|
|
52
59
|
}
|
|
53
60
|
requestRender();
|
|
54
61
|
}, INTRO_TICK_MS);
|
|
62
|
+
this.#animTimer.unref?.();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dispose(): void {
|
|
66
|
+
this.#stopAnimation();
|
|
55
67
|
}
|
|
56
68
|
|
|
57
69
|
#stopAnimation(): void {
|
|
@@ -76,15 +88,20 @@ export class WelcomeComponent implements Component {
|
|
|
76
88
|
}
|
|
77
89
|
|
|
78
90
|
render(termWidth: number): string[] {
|
|
79
|
-
|
|
80
|
-
const boxWidth = Math.max(0, termWidth - 2);
|
|
91
|
+
const boxWidth = Math.max(0, termWidth);
|
|
81
92
|
if (boxWidth < 4) {
|
|
82
93
|
return [];
|
|
83
94
|
}
|
|
95
|
+
|
|
96
|
+
const targetRows = this.#targetRows(termWidth);
|
|
97
|
+
if (targetRows !== undefined && targetRows <= 0) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const targetContentRows = targetRows === undefined ? undefined : Math.max(0, targetRows - 2);
|
|
84
101
|
const dualContentWidth = boxWidth - 3; // 3 = │ + │ + │
|
|
85
|
-
const preferredLeftCol =
|
|
86
|
-
const minLeftCol =
|
|
87
|
-
const minRightCol =
|
|
102
|
+
const preferredLeftCol = 40;
|
|
103
|
+
const minLeftCol = 20; // wordmark plus Sayknow identity labels
|
|
104
|
+
const minRightCol = 24;
|
|
88
105
|
const modelPill = this.#pill(theme.icon.model || "model", this.modelName, "statusLineModel");
|
|
89
106
|
const providerPill = this.#pill(theme.icon.package || "provider", this.providerName, "statusLinePath");
|
|
90
107
|
const logoLines = this.#logoLines();
|
|
@@ -130,24 +147,23 @@ export class WelcomeComponent implements Component {
|
|
|
130
147
|
...identityTail,
|
|
131
148
|
];
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
const buildSeparator = (columnWidth: number): string =>
|
|
151
|
+
` ${theme.fg("dim", theme.boxRound.horizontal.repeat(Math.max(0, columnWidth - 2)))}`;
|
|
152
|
+
|
|
153
|
+
const rightColumnWidth = showRightColumn ? rightCol : leftCol;
|
|
154
|
+
const separator = buildSeparator(rightColumnWidth);
|
|
155
|
+
const changelogBudget = Math.max(1, Math.min(6, Math.floor((targetContentRows ?? 18) * 0.3)));
|
|
136
156
|
|
|
137
|
-
// Recent sessions content
|
|
138
157
|
const sessionLines: string[] = [];
|
|
139
158
|
if (this.recentSessions.length === 0) {
|
|
140
159
|
sessionLines.push(` ${theme.fg("dim", t("welcome.noSessions"))}`);
|
|
141
160
|
} else {
|
|
142
|
-
// Reserve width for the bullet prefix (" • ") and the trailing " (timeAgo)"
|
|
143
|
-
// so the relative time is never the part that gets truncated. The name
|
|
144
|
-
// absorbs whatever space is left.
|
|
145
161
|
const bulletPrefix = ` ${theme.md.bullet} `;
|
|
146
162
|
const prefixWidth = visibleWidth(bulletPrefix);
|
|
147
163
|
for (const session of this.recentSessions.slice(0, 3)) {
|
|
148
164
|
const timeSuffixRaw = ` (${session.timeAgo})`;
|
|
149
165
|
const timeWidth = visibleWidth(timeSuffixRaw);
|
|
150
|
-
const nameBudget = Math.max(1,
|
|
166
|
+
const nameBudget = Math.max(1, rightColumnWidth - prefixWidth - timeWidth);
|
|
151
167
|
const nameVis = visibleWidth(session.name);
|
|
152
168
|
const name = nameVis > nameBudget ? truncateToWidth(session.name, nameBudget) : session.name;
|
|
153
169
|
sessionLines.push(
|
|
@@ -156,12 +172,11 @@ export class WelcomeComponent implements Component {
|
|
|
156
172
|
}
|
|
157
173
|
}
|
|
158
174
|
|
|
159
|
-
// LSP servers content
|
|
160
175
|
const lspLines: string[] = [];
|
|
161
176
|
if (this.lspServers.length === 0) {
|
|
162
177
|
lspLines.push(` ${theme.fg("dim", t("welcome.noLsp"))}`);
|
|
163
178
|
} else {
|
|
164
|
-
for (const server of this.lspServers) {
|
|
179
|
+
for (const server of this.lspServers.slice(0, 4)) {
|
|
165
180
|
const icon =
|
|
166
181
|
server.status === "ready"
|
|
167
182
|
? theme.styledSymbol("status.success", "success")
|
|
@@ -179,8 +194,11 @@ export class WelcomeComponent implements Component {
|
|
|
179
194
|
return ` ${theme.fg("accent", cmd)}${pad}${theme.fg("muted", desc)}`;
|
|
180
195
|
};
|
|
181
196
|
|
|
182
|
-
// Right column
|
|
183
197
|
const rightLines = [
|
|
198
|
+
"",
|
|
199
|
+
` ${theme.bold(theme.fg("accent", "What's New"))}`,
|
|
200
|
+
...this.#whatsNewLines(rightColumnWidth, changelogBudget),
|
|
201
|
+
separator,
|
|
184
202
|
` ${theme.bold(theme.fg("accent", t("welcome.workflows")))}`,
|
|
185
203
|
wf("/deep-interview", t("welcome.wf.deepInterview")),
|
|
186
204
|
wf("/ralplan", t("welcome.wf.ralplan")),
|
|
@@ -210,7 +228,12 @@ export class WelcomeComponent implements Component {
|
|
|
210
228
|
"",
|
|
211
229
|
];
|
|
212
230
|
|
|
213
|
-
|
|
231
|
+
const contentRows =
|
|
232
|
+
targetContentRows ??
|
|
233
|
+
(showRightColumn ? Math.max(leftLines.length, rightLines.length) : leftLines.length + rightLines.length);
|
|
234
|
+
const outputRows = targetRows === undefined ? Math.max(3, contentRows + 2) : targetRows;
|
|
235
|
+
const bodyRows = Math.max(0, outputRows - 2);
|
|
236
|
+
|
|
214
237
|
const hChar = theme.boxRound.horizontal;
|
|
215
238
|
const h = theme.fg("dim", hChar);
|
|
216
239
|
const v = theme.fg("dim", theme.boxRound.vertical);
|
|
@@ -220,8 +243,6 @@ export class WelcomeComponent implements Component {
|
|
|
220
243
|
const br = theme.fg("dim", theme.boxRound.bottomRight);
|
|
221
244
|
|
|
222
245
|
const lines: string[] = [];
|
|
223
|
-
|
|
224
|
-
// Top border with embedded title
|
|
225
246
|
const title = ` ${APP_NAME} · Sayknow-CLI v${this.version} `;
|
|
226
247
|
const titlePrefixRaw = hChar.repeat(3);
|
|
227
248
|
const titleStyled = theme.fg("dim", titlePrefixRaw) + theme.fg("muted", title);
|
|
@@ -233,22 +254,32 @@ export class WelcomeComponent implements Component {
|
|
|
233
254
|
const afterTitle = titleSpace - titleVisLen;
|
|
234
255
|
lines.push(tl + titleStyled + theme.fg("dim", hChar.repeat(afterTitle)) + tr);
|
|
235
256
|
}
|
|
257
|
+
if (outputRows === 1) {
|
|
258
|
+
return lines;
|
|
259
|
+
}
|
|
236
260
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const right = this.#fitToWidth(
|
|
261
|
+
if (showRightColumn) {
|
|
262
|
+
const leftBlock = this.#fitBlock(leftLines, bodyRows, "center");
|
|
263
|
+
const rightBlock = this.#fitBlock(rightLines, bodyRows, "top");
|
|
264
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
265
|
+
const left = this.#fitToWidth(leftBlock[i] ?? "", leftCol);
|
|
266
|
+
const right = this.#fitToWidth(rightBlock[i] ?? "", rightCol);
|
|
243
267
|
lines.push(v + left + v + right + v);
|
|
244
|
-
} else {
|
|
245
|
-
lines.push(v + left + v);
|
|
246
268
|
}
|
|
247
|
-
}
|
|
248
|
-
// Bottom border
|
|
249
|
-
if (showRightColumn) {
|
|
250
269
|
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.boxSharp.teeUp) + h.repeat(rightCol) + br);
|
|
251
270
|
} else {
|
|
271
|
+
const compactLeftLines =
|
|
272
|
+
bodyRows < 14
|
|
273
|
+
? [
|
|
274
|
+
this.#centerText(theme.bold(theme.fg("accent", "SKC Forge")), leftCol),
|
|
275
|
+
this.#centerText(this.#loadingLine(), leftCol),
|
|
276
|
+
this.#centerText(modelPill, leftCol),
|
|
277
|
+
]
|
|
278
|
+
: leftLines;
|
|
279
|
+
const singleBlock = this.#fitBlock([...compactLeftLines, separator, ...rightLines], bodyRows, "top");
|
|
280
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
281
|
+
lines.push(v + this.#fitToWidth(singleBlock[i] ?? "", leftCol) + v);
|
|
282
|
+
}
|
|
252
283
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
253
284
|
}
|
|
254
285
|
|
|
@@ -274,6 +305,110 @@ export class WelcomeComponent implements Component {
|
|
|
274
305
|
}
|
|
275
306
|
return str + padding(width - visLen);
|
|
276
307
|
}
|
|
308
|
+
#targetRows(termWidth: number): number | undefined {
|
|
309
|
+
const viewportRows = this.options.getViewportRows?.();
|
|
310
|
+
if (typeof viewportRows !== "number" || !Number.isFinite(viewportRows) || viewportRows <= 0) {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
const reservedRows = Math.max(0, Math.floor(this.options.getReservedBottomRows?.(termWidth) ?? 0));
|
|
314
|
+
return Math.max(0, Math.floor(viewportRows) - reservedRows);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#loadingLine(): string {
|
|
318
|
+
const frames = theme.spinnerFrames;
|
|
319
|
+
const elapsed = this.#animStart == null ? 0 : performance.now() - this.#animStart;
|
|
320
|
+
const frame = frames.length > 0 ? (frames[Math.floor(elapsed / 100) % frames.length] ?? "*") : "*";
|
|
321
|
+
const label = this.#animStart == null ? "ready" : "warming workspace";
|
|
322
|
+
return `${theme.fg("warning", frame)} ${theme.fg("muted", label)}`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
#fitBlock(lines: string[], rows: number, align: "top" | "center"): string[] {
|
|
326
|
+
if (rows <= 0) return [];
|
|
327
|
+
const clipped =
|
|
328
|
+
lines.length > rows
|
|
329
|
+
? rows === 1
|
|
330
|
+
? [theme.fg("dim", " …")]
|
|
331
|
+
: [...lines.slice(0, rows - 1), theme.fg("dim", " …")]
|
|
332
|
+
: lines;
|
|
333
|
+
const missingRows = rows - clipped.length;
|
|
334
|
+
if (missingRows <= 0) return clipped;
|
|
335
|
+
const topPad = align === "center" ? Math.floor(missingRows / 2) : 0;
|
|
336
|
+
const bottomPad = missingRows - topPad;
|
|
337
|
+
return [...Array.from({ length: topPad }, () => ""), ...clipped, ...Array.from({ length: bottomPad }, () => "")];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#whatsNewLines(width: number, maxItems: number): string[] {
|
|
341
|
+
const changelog = this.options.changelogMarkdown?.trim();
|
|
342
|
+
if (!changelog) {
|
|
343
|
+
return [` ${theme.fg("dim", "Ready for your next prompt")}`];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const version = this.#latestChangelogVersion(changelog);
|
|
347
|
+
if (this.options.collapseChangelog) {
|
|
348
|
+
return [
|
|
349
|
+
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
350
|
+
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
351
|
+
];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const itemLimit = Math.max(1, Math.floor(maxItems));
|
|
355
|
+
const items = this.#changelogItems(changelog);
|
|
356
|
+
if (items.length === 0) {
|
|
357
|
+
return [
|
|
358
|
+
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
359
|
+
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
360
|
+
];
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const prefix = ` ${theme.md.bullet} `;
|
|
364
|
+
const textWidth = Math.max(1, width - visibleWidth(prefix));
|
|
365
|
+
const visibleItems = items.slice(0, itemLimit).map(item => {
|
|
366
|
+
const text = visibleWidth(item) > textWidth ? truncateToWidth(item, textWidth) : item;
|
|
367
|
+
return `${theme.fg("dim", prefix)}${theme.fg("muted", text)}`;
|
|
368
|
+
});
|
|
369
|
+
if (items.length > visibleItems.length) {
|
|
370
|
+
visibleItems.push(` ${theme.fg("dim", `… ${theme.bold("/changelog")} for full notes`)}`);
|
|
371
|
+
}
|
|
372
|
+
return visibleItems;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
#latestChangelogVersion(markdown: string): string {
|
|
376
|
+
const versionMatch = markdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
|
377
|
+
return versionMatch?.[1] ?? this.version;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#changelogItems(markdown: string): string[] {
|
|
381
|
+
const items: string[] = [];
|
|
382
|
+
let inFence = false;
|
|
383
|
+
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
384
|
+
const line = rawLine.trim();
|
|
385
|
+
if (line.startsWith("```")) {
|
|
386
|
+
inFence = !inFence;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (inFence || !line || /^#{1,6}\s+/.test(line) || /^-{3,}$/.test(line)) {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
const withoutBullet = line
|
|
393
|
+
.replace(/^[-*]\s+/, "")
|
|
394
|
+
.replace(/^\d+\.\s+/, "")
|
|
395
|
+
.replace(/^>\s*/, "");
|
|
396
|
+
const cleaned = this.#stripMarkdown(withoutBullet);
|
|
397
|
+
if (cleaned) items.push(cleaned);
|
|
398
|
+
}
|
|
399
|
+
return items;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
#stripMarkdown(text: string): string {
|
|
403
|
+
return text
|
|
404
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
405
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
|
406
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
407
|
+
.replace(/__([^_]+)__/g, "$1")
|
|
408
|
+
.replace(/\*([^*]+)\*/g, "$1")
|
|
409
|
+
.replace(/[_~]/g, "")
|
|
410
|
+
.trim();
|
|
411
|
+
}
|
|
277
412
|
|
|
278
413
|
#pill(icon: string, text: string, color: ThemeColor): string {
|
|
279
414
|
return `${theme.fg("borderMuted", "[")} ${theme.fg(color, icon)} ${theme.fg("muted", text)} ${theme.fg(
|
|
@@ -556,6 +556,29 @@ export class CommandController {
|
|
|
556
556
|
showMarkdownPanel(this.ctx, "Keyboard Shortcuts", hotkeys);
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
+
handleHelpCommand(): void {
|
|
560
|
+
const sessionNewKey = this.ctx.keybindings.getDisplayString("app.session.new") || "/new";
|
|
561
|
+
const markdown = [
|
|
562
|
+
"**Beginner actions**",
|
|
563
|
+
"",
|
|
564
|
+
"| Action | How |",
|
|
565
|
+
"|---|---|",
|
|
566
|
+
`| Start a fresh session | \`${sessionNewKey}\` or \`/new\` |`,
|
|
567
|
+
"| Resume another session | `/resume` |",
|
|
568
|
+
"| Show session details | `/session info` |",
|
|
569
|
+
"| Delete current session | `/session delete` |",
|
|
570
|
+
"| Select a model | `/model` or `Ctrl+L` |",
|
|
571
|
+
"| Show all shortcuts | `?` on an empty prompt or `/hotkeys` |",
|
|
572
|
+
"",
|
|
573
|
+
"**Finding commands**",
|
|
574
|
+
"",
|
|
575
|
+
"- Type `/` to browse slash commands, then use arrows and Enter/Tab.",
|
|
576
|
+
"- Type `#` to browse prompt actions like starting a session, pasting an image, or moving the cursor.",
|
|
577
|
+
"- Type `!` for shell commands and `$` for Python snippets.",
|
|
578
|
+
].join("\n");
|
|
579
|
+
showMarkdownPanel(this.ctx, "SKC Help", markdown);
|
|
580
|
+
}
|
|
581
|
+
|
|
559
582
|
handleToolsCommand(): void {
|
|
560
583
|
const tools = buildToolsMarkdown({ tools: this.ctx.session.agent.state.tools });
|
|
561
584
|
showMarkdownPanel(this.ctx, "Available Tools", tools);
|
|
@@ -1253,17 +1276,18 @@ export class CommandController {
|
|
|
1253
1276
|
async handleContributionPrepCommand(customInstructions?: string): Promise<void> {
|
|
1254
1277
|
this.ctx.editor.setText("");
|
|
1255
1278
|
try {
|
|
1256
|
-
const result = await this.ctx.session.prepareContributionPrep({ customInstructions, spawnWorker:
|
|
1279
|
+
const result = await this.ctx.session.prepareContributionPrep({ customInstructions, spawnWorker: false });
|
|
1257
1280
|
this.ctx.showStatus(
|
|
1258
1281
|
[
|
|
1259
1282
|
"Contribution prep artifacts written.",
|
|
1260
1283
|
`Manifest: ${result.manifestPath}`,
|
|
1261
1284
|
`Worker prompt: ${result.workerPromptPath}`,
|
|
1285
|
+
"Open the worker prompt from a separate terminal to keep this TUI session stable.",
|
|
1262
1286
|
].join("\n"),
|
|
1263
1287
|
);
|
|
1264
1288
|
this.ctx.chatContainer.addChild(
|
|
1265
1289
|
new Text(
|
|
1266
|
-
`${theme.fg("accent", `${theme.status.success} Contribution prep ready`)}\
|
|
1290
|
+
`${theme.fg("accent", `${theme.status.success} Contribution prep ready`)}\nWorker prompt: ${result.workerPromptPath}`,
|
|
1267
1291
|
1,
|
|
1268
1292
|
1,
|
|
1269
1293
|
),
|
|
@@ -3,6 +3,7 @@ import { calculatePromptTokens } from "@sayknow-cli/agent-core/compaction/compac
|
|
|
3
3
|
import type { AssistantMessage, ImageContent } from "@sayknow-cli/ai";
|
|
4
4
|
import { parseRateLimitReason } from "@sayknow-cli/ai";
|
|
5
5
|
import { type Component, Loader, TERMINAL, Text } from "@sayknow-cli/tui";
|
|
6
|
+
import { logger } from "@sayknow-cli/utils";
|
|
6
7
|
import { settings } from "../../config/settings";
|
|
7
8
|
import { AssistantMessageComponent } from "../../modes/components/assistant-message";
|
|
8
9
|
import {
|
|
@@ -15,16 +16,56 @@ import { ToolExecutionComponent } from "../../modes/components/tool-execution";
|
|
|
15
16
|
import { TtsrNotificationComponent } from "../../modes/components/ttsr-notification";
|
|
16
17
|
import { getSymbolTheme, theme } from "../../modes/theme/theme";
|
|
17
18
|
import type { InteractiveModeContext, TodoPhase } from "../../modes/types";
|
|
19
|
+
import { summaryFromMessage } from "../../notifications/helpers";
|
|
18
20
|
import type { PlanApprovalDetails } from "../../plan-mode/approved-plan";
|
|
19
21
|
import type { AgentSessionEvent } from "../../session/agent-session";
|
|
20
22
|
import { isSilentAbort, readPendingDisplayTag } from "../../session/messages";
|
|
21
23
|
import type { ResolveToolDetails } from "../../tools/resolve";
|
|
22
24
|
import { interruptHint } from "../shared";
|
|
23
25
|
import { buildAbortDisplayMessage } from "../utils/abort-message";
|
|
26
|
+
import { ringTerminalBell } from "../utils/terminal-bell";
|
|
24
27
|
|
|
25
28
|
type AgentSessionEventKind = AgentSessionEvent["type"];
|
|
26
29
|
|
|
27
30
|
const IRC_MESSAGE_VISIBLE_TTL_MS = 10_000;
|
|
31
|
+
const COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS = 10_000;
|
|
32
|
+
|
|
33
|
+
interface CompletionNotifyPayload {
|
|
34
|
+
type: "agent-turn-complete";
|
|
35
|
+
title: string;
|
|
36
|
+
body: string;
|
|
37
|
+
cwd: string;
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
sessionName?: string;
|
|
40
|
+
lastAssistantMessage?: string;
|
|
41
|
+
stopReason?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function completionNotifyShellCommand(command: string): string[] {
|
|
45
|
+
if (process.platform === "win32") {
|
|
46
|
+
return ["cmd.exe", "/d", "/s", "/c", command];
|
|
47
|
+
}
|
|
48
|
+
return ["/bin/sh", "-c", command];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function cleanNotificationEnvValue(value: string | undefined, max = 4000): string {
|
|
52
|
+
if (!value) return "";
|
|
53
|
+
return value.replaceAll("\0", "").slice(0, max);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildCompletionNotifyEnv(payload: CompletionNotifyPayload): Record<string, string> {
|
|
57
|
+
return {
|
|
58
|
+
SKC_NOTIFICATION_TYPE: payload.type,
|
|
59
|
+
SKC_NOTIFICATION_TITLE: cleanNotificationEnvValue(payload.title, 500),
|
|
60
|
+
SKC_NOTIFICATION_BODY: cleanNotificationEnvValue(payload.body),
|
|
61
|
+
SKC_NOTIFICATION_CWD: cleanNotificationEnvValue(payload.cwd, 2000),
|
|
62
|
+
SKC_NOTIFICATION_SESSION_ID: cleanNotificationEnvValue(payload.sessionId, 500),
|
|
63
|
+
SKC_NOTIFICATION_SESSION_NAME: cleanNotificationEnvValue(payload.sessionName, 500),
|
|
64
|
+
SKC_NOTIFICATION_LAST_ASSISTANT_MESSAGE: cleanNotificationEnvValue(payload.lastAssistantMessage),
|
|
65
|
+
SKC_NOTIFICATION_STOP_REASON: cleanNotificationEnvValue(payload.stopReason, 100),
|
|
66
|
+
SKC_NOTIFICATION_JSON: cleanNotificationEnvValue(JSON.stringify(payload), 8000),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
28
69
|
|
|
29
70
|
function friendlyRetryReason(errorMessage: string | undefined): string {
|
|
30
71
|
if (!errorMessage) return "";
|
|
@@ -97,6 +138,7 @@ export class EventController {
|
|
|
97
138
|
this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
|
|
98
139
|
this.ctx.retryEscapeHandler = undefined;
|
|
99
140
|
}
|
|
141
|
+
this.ctx.retryEscapePrimed = false;
|
|
100
142
|
if (this.ctx.retryLoader) {
|
|
101
143
|
this.ctx.retryLoader.stop();
|
|
102
144
|
this.ctx.retryLoader = undefined;
|
|
@@ -200,6 +242,7 @@ export class EventController {
|
|
|
200
242
|
this.ctx.retryLoader = undefined;
|
|
201
243
|
this.ctx.statusContainer.clear();
|
|
202
244
|
}
|
|
245
|
+
this.ctx.retryEscapePrimed = false;
|
|
203
246
|
this.#cancelIdleCompaction();
|
|
204
247
|
this.ctx.updateEditorBorderColor();
|
|
205
248
|
this.ctx.ensureLoadingAnimation();
|
|
@@ -727,6 +770,7 @@ export class EventController {
|
|
|
727
770
|
if (!this.ctx.retryEscapeHandler) {
|
|
728
771
|
this.ctx.retryEscapeHandler = this.ctx.editor.onEscape;
|
|
729
772
|
}
|
|
773
|
+
this.ctx.retryEscapePrimed = false;
|
|
730
774
|
let escPressed = false;
|
|
731
775
|
this.ctx.editor.onEscape = () => {
|
|
732
776
|
if (!escPressed) {
|
|
@@ -777,6 +821,7 @@ export class EventController {
|
|
|
777
821
|
this.ctx.retryLoader = undefined;
|
|
778
822
|
this.ctx.statusContainer.clear();
|
|
779
823
|
}
|
|
824
|
+
this.ctx.retryEscapePrimed = false;
|
|
780
825
|
if (!event.success) {
|
|
781
826
|
this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
782
827
|
}
|
|
@@ -857,8 +902,46 @@ export class EventController {
|
|
|
857
902
|
return lastAssistant?.usage ? calculatePromptTokens(lastAssistant.usage) : 0;
|
|
858
903
|
}
|
|
859
904
|
|
|
905
|
+
#runCompletionNotifyCommand(payload: CompletionNotifyPayload): void {
|
|
906
|
+
const command = settings.getGlobal("completion.notifyCommand")?.trim();
|
|
907
|
+
if (!command) return;
|
|
908
|
+
|
|
909
|
+
try {
|
|
910
|
+
const proc = Bun.spawn(completionNotifyShellCommand(command), {
|
|
911
|
+
cwd: payload.cwd,
|
|
912
|
+
env: {
|
|
913
|
+
...process.env,
|
|
914
|
+
...buildCompletionNotifyEnv(payload),
|
|
915
|
+
},
|
|
916
|
+
stdin: "ignore",
|
|
917
|
+
stdout: "ignore",
|
|
918
|
+
stderr: "ignore",
|
|
919
|
+
});
|
|
920
|
+
proc.unref();
|
|
921
|
+
const timer = setTimeout(() => {
|
|
922
|
+
try {
|
|
923
|
+
proc.kill();
|
|
924
|
+
} catch {}
|
|
925
|
+
}, COMPLETION_NOTIFY_COMMAND_TIMEOUT_MS);
|
|
926
|
+
timer.unref?.();
|
|
927
|
+
void proc.exited
|
|
928
|
+
.then(exitCode => {
|
|
929
|
+
clearTimeout(timer);
|
|
930
|
+
if (exitCode !== 0) {
|
|
931
|
+
logger.warn("completion notify command exited non-zero", { exitCode });
|
|
932
|
+
}
|
|
933
|
+
})
|
|
934
|
+
.catch(error => {
|
|
935
|
+
clearTimeout(timer);
|
|
936
|
+
logger.warn("completion notify command failed", { error: String(error) });
|
|
937
|
+
});
|
|
938
|
+
} catch (error) {
|
|
939
|
+
logger.warn("completion notify command failed to start", { error: String(error) });
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
860
943
|
sendCompletionNotification(): void {
|
|
861
|
-
|
|
944
|
+
const isBackgrounded = this.ctx.isBackgrounded !== false;
|
|
862
945
|
const notify = settings.get("completion.notify");
|
|
863
946
|
if (notify === "off") return;
|
|
864
947
|
|
|
@@ -869,9 +952,24 @@ export class EventController {
|
|
|
869
952
|
const last = this.ctx.session.getLastAssistantMessage?.();
|
|
870
953
|
if (last?.stopReason === "aborted" || last?.stopReason === "error") return;
|
|
871
954
|
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
|
|
955
|
+
const sessionName = this.ctx.sessionManager.getSessionName();
|
|
956
|
+
const title = sessionName ? `${sessionName}: Complete` : "Complete";
|
|
957
|
+
const summary = summaryFromMessage(last, 1000);
|
|
958
|
+
const body = summary ?? "Complete";
|
|
959
|
+
const sessionManager = this.ctx.sessionManager as { getCwd?: () => string; getSessionId?: () => string };
|
|
960
|
+
const payload: CompletionNotifyPayload = {
|
|
961
|
+
type: "agent-turn-complete",
|
|
962
|
+
title,
|
|
963
|
+
body,
|
|
964
|
+
cwd: sessionManager.getCwd?.() ?? settings.getCwd(),
|
|
965
|
+
sessionId: sessionManager.getSessionId?.(),
|
|
966
|
+
sessionName: sessionName || undefined,
|
|
967
|
+
lastAssistantMessage: summary,
|
|
968
|
+
stopReason: last?.stopReason,
|
|
969
|
+
};
|
|
970
|
+
ringTerminalBell("complete");
|
|
971
|
+
if (isBackgrounded) TERMINAL.sendNotification(title);
|
|
972
|
+
this.#runCompletionNotifyCommand(payload);
|
|
875
973
|
}
|
|
876
974
|
|
|
877
975
|
async handleBackgroundEvent(event: AgentSessionEvent): Promise<void> {
|
|
@@ -24,6 +24,7 @@ import { HookSelectorComponent } from "../../modes/components/hook-selector";
|
|
|
24
24
|
import { getAvailableThemesWithPaths, getThemeByName, setTheme, type Theme, theme } from "../../modes/theme/theme";
|
|
25
25
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
26
26
|
import { setSessionTerminalTitle, setTerminalTitle } from "../../utils/title-generator";
|
|
27
|
+
import { classifyHookSelectorBellEvent, ringTerminalBell } from "../utils/terminal-bell";
|
|
27
28
|
|
|
28
29
|
const MAX_WIDGET_LINES = 10;
|
|
29
30
|
const HOOK_SELECTOR_CHROME_ROWS = 7;
|
|
@@ -617,12 +618,15 @@ export class ExtensionUiController {
|
|
|
617
618
|
const listChromeRows = dialogOptions?.outline === true ? HOOK_SELECTOR_OUTLINE_ROWS : 0;
|
|
618
619
|
// Reserve rows for the inline custom-input editor so opening it doesn't
|
|
619
620
|
// push the scrollable title past the viewport into terminal scrollback.
|
|
620
|
-
const inlineInputRows =
|
|
621
|
+
const inlineInputRows =
|
|
622
|
+
dialogOptions?.customInput || dialogOptions?.clarificationInput ? HOOK_SELECTOR_INLINE_INPUT_ROWS : 0;
|
|
621
623
|
const availableTitleRows =
|
|
622
624
|
this.ctx.ui.terminal.rows - scrollOptionRows - listChromeRows - inlineInputRows - HOOK_SELECTOR_CHROME_ROWS;
|
|
623
625
|
const scrollTitleRows =
|
|
624
626
|
requestedTitleRows === undefined ? undefined : Math.max(1, Math.min(requestedTitleRows, availableTitleRows));
|
|
625
627
|
|
|
628
|
+
ringTerminalBell(classifyHookSelectorBellEvent(title));
|
|
629
|
+
|
|
626
630
|
this.ctx.hookSelector = new HookSelectorComponent(
|
|
627
631
|
title,
|
|
628
632
|
options,
|
|
@@ -657,7 +661,10 @@ export class ExtensionUiController {
|
|
|
657
661
|
tui: this.ctx.ui,
|
|
658
662
|
// Share the main prompt editor's autocomplete provider so the
|
|
659
663
|
// inline "Other (type your own)" editor supports `@` file links.
|
|
660
|
-
autocompleteProvider:
|
|
664
|
+
autocompleteProvider:
|
|
665
|
+
dialogOptions?.customInput || dialogOptions?.clarificationInput
|
|
666
|
+
? this.ctx.editor.getAutocompleteProvider()
|
|
667
|
+
: undefined,
|
|
661
668
|
outline: dialogOptions?.outline,
|
|
662
669
|
wrapFocused: dialogOptions?.wrapFocused,
|
|
663
670
|
scrollTitleRows,
|
|
@@ -673,6 +680,18 @@ export class ExtensionUiController {
|
|
|
673
680
|
},
|
|
674
681
|
}
|
|
675
682
|
: undefined,
|
|
683
|
+
clarificationInput: dialogOptions?.clarificationInput
|
|
684
|
+
? {
|
|
685
|
+
optionLabel: dialogOptions.clarificationInput.optionLabel,
|
|
686
|
+
allowEmpty: dialogOptions.clarificationInput.allowEmpty,
|
|
687
|
+
onSubmit: text => {
|
|
688
|
+
const optionLabel = dialogOptions.clarificationInput?.optionLabel;
|
|
689
|
+
this.hideHookSelector();
|
|
690
|
+
dialogOptions.clarificationInput?.onSubmit(text);
|
|
691
|
+
finish(optionLabel);
|
|
692
|
+
},
|
|
693
|
+
}
|
|
694
|
+
: undefined,
|
|
676
695
|
},
|
|
677
696
|
);
|
|
678
697
|
// Detach (not dispose) the reusable editor before mounting the transient hook UI, so the
|