@gajae-code/coding-agent 0.11.6 → 0.11.8
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 +92 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-activation.d.ts +4 -3
- package/dist/types/config/model-profile-contract.d.ts +38 -0
- package/dist/types/config/settings-schema.d.ts +70 -7
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +20 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/main.d.ts +32 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -2
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +4 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +26 -1
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +18 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +189 -4
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +1 -1
- package/dist/types/sdk/host/query/handlers.d.ts +9 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/startup-capability.d.ts +3 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +30 -5
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +32 -9
- package/dist/types/session/internal/managed-session-storage.d.ts +27 -10
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/session-manager.d.ts +25 -4
- package/dist/types/session/session-storage.d.ts +18 -0
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +94 -0
- package/dist/types/tools/ask.d.ts +454 -174
- package/dist/types/tools/fetch.d.ts +5 -8
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -8
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/scripts/run-test-manifest.ts +4 -4
- package/src/capability/index.ts +50 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +2 -92
- package/src/cli/notify-cli.ts +5 -38
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +52 -9
- package/src/commands/daemon.ts +22 -5
- package/src/commands/sdk.ts +44 -11
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-activation.ts +7 -18
- package/src/config/model-profile-contract.ts +161 -0
- package/src/config/model-profiles.ts +18 -0
- package/src/config/model-registry.ts +11 -1
- package/src/config/settings-schema.ts +73 -7
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +2 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +42 -1
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/py/executor.ts +30 -11
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/gjc-runtime/launch-tmux.ts +1 -1
- package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/gjc-runtime/ralplan-runtime.ts +75 -3
- package/src/gjc-runtime/repository-binding.ts +267 -0
- package/src/gjc-runtime/session-state-sidecar.ts +25 -9
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
- package/src/gjc-runtime/tmux-sessions.ts +45 -6
- package/src/gjc-runtime/ultragoal-guard.ts +93 -15
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +103 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +456 -31
- package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
- package/src/gjc-runtime/workflow-manifest.ts +23 -0
- package/src/internal-urls/docs-index.generated.ts +12 -10
- package/src/internal-urls/local-protocol.ts +64 -21
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +111 -37
- package/src/modes/DESIGN.md +5 -0
- package/src/modes/acp/acp-agent.ts +50 -16
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/model-selector.ts +73 -11
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/tool-status-header.ts +25 -4
- package/src/modes/components/welcome.ts +22 -18
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +97 -54
- package/src/modes/controllers/event-controller.ts +10 -0
- package/src/modes/controllers/input-controller.ts +38 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +149 -63
- package/src/modes/interactive-mode.ts +57 -34
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +4 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +5 -3
- package/src/prompts/agents/architect.md +0 -1
- package/src/prompts/agents/critic.md +0 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -4
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/json-rpc.ts +23 -9
- package/src/runtime-mcp/manager.ts +51 -53
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/lifecycle.ts +185 -36
- package/src/sdk/bus/chat-daemon-control.ts +19 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +9 -1
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/index.ts +244 -70
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +68 -20
- package/src/sdk/bus/prompt-reconciliation.ts +239 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/telegram-daemon-contract.ts +18 -3
- package/src/sdk/bus/telegram-daemon-control.ts +147 -35
- package/src/sdk/bus/telegram-daemon.ts +2463 -450
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +1 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +2 -6
- package/src/sdk/host/query/handlers.ts +70 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +58 -0
- package/src/sdk/protocol/operation-registry.ts +18 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +31 -5
- package/src/sdk/startup-capability.ts +9 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +292 -154
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/internal/managed-session-scope.ts +1163 -137
- package/src/session/internal/managed-session-storage.ts +397 -234
- package/src/session/internal/native-publish-outcome.ts +266 -0
- package/src/session/session-manager.ts +620 -403
- package/src/session/session-storage.ts +128 -11
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +30 -1
- package/src/setup/provider-presets.json +2 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/gjc-command.ts +2 -2
- package/src/task/index.ts +121 -15
- package/src/task/receipt.ts +3 -0
- package/src/task/types.ts +34 -3
- package/src/tools/ask.ts +199 -89
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +13 -131
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +4 -0
- package/src/tools/read.ts +2 -0
- package/src/tools/render-utils.ts +7 -1
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
|
@@ -49,7 +49,7 @@ export const APP_ACTION_METADATA: readonly ActionMetadata[] = [
|
|
|
49
49
|
action("app.clipboard.copyPrompt", "Copy prompt", "Clipboard", ["composer"]),
|
|
50
50
|
action("app.session.new", "New session", "Session", ["composer"]),
|
|
51
51
|
action("app.session.tree", "Session tree", "Session", ["composer"]),
|
|
52
|
-
action("app.session.fork", "
|
|
52
|
+
action("app.session.fork", "Branch from message", "Session", ["composer"]),
|
|
53
53
|
action("app.session.resume", "Resume session", "Session", ["composer"]),
|
|
54
54
|
action("app.session.observe", "Observe sessions", "Session", ["composer"]),
|
|
55
55
|
action("app.session.dashboard", "Show sessions dashboard", "Session", ["composer"]),
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { type Component, Container, Markdown, Spacer, Text, type TUI } from "@gajae-code/tui";
|
|
2
|
+
import {
|
|
3
|
+
BTW_MAX_ANSWER_UTF8_BYTES,
|
|
4
|
+
BTW_MAX_CONTEXT_TURNS,
|
|
5
|
+
sanitizeBtwError,
|
|
6
|
+
truncateUtf8,
|
|
7
|
+
} from "../../session/btw-contract";
|
|
2
8
|
import { replaceTabs } from "../../tools/render-utils";
|
|
3
9
|
import { getMarkdownTheme, theme } from "../theme/theme";
|
|
4
10
|
import { DynamicBorder } from "./dynamic-border";
|
|
@@ -10,12 +16,21 @@ interface BtwPanelComponentOptions {
|
|
|
10
16
|
tui: TUI;
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
interface BtwTurn {
|
|
20
|
+
question: string;
|
|
21
|
+
answer: string;
|
|
22
|
+
state: BtwPanelState;
|
|
23
|
+
errorMessage?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
13
26
|
export class BtwPanelComponent extends Container {
|
|
14
27
|
#question: string;
|
|
15
28
|
#tui: TUI;
|
|
16
29
|
#state: BtwPanelState = "running";
|
|
17
30
|
#answer = "";
|
|
18
31
|
#errorMessage: string | undefined;
|
|
32
|
+
#completedTurns: BtwTurn[] = [];
|
|
33
|
+
#streamingContent = new Container();
|
|
19
34
|
#closed = false;
|
|
20
35
|
|
|
21
36
|
constructor(options: BtwPanelComponentOptions) {
|
|
@@ -25,16 +40,29 @@ export class BtwPanelComponent extends Container {
|
|
|
25
40
|
this.#rebuild();
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
beginTurn(question: string): void {
|
|
44
|
+
if (this.#closed) return;
|
|
45
|
+
if (this.#state === "complete") {
|
|
46
|
+
this.#completedTurns.push(this.#currentTurn());
|
|
47
|
+
if (this.#completedTurns.length > BTW_MAX_CONTEXT_TURNS) this.#completedTurns.shift();
|
|
48
|
+
}
|
|
49
|
+
this.#question = question;
|
|
50
|
+
this.#answer = "";
|
|
51
|
+
this.#errorMessage = undefined;
|
|
52
|
+
this.#state = "running";
|
|
53
|
+
this.#rebuild();
|
|
54
|
+
}
|
|
55
|
+
|
|
28
56
|
appendText(delta: string): void {
|
|
29
57
|
if (!delta || this.#closed) return;
|
|
30
|
-
this.#answer
|
|
31
|
-
this.#
|
|
58
|
+
this.#answer = truncateUtf8(this.#answer + delta, BTW_MAX_ANSWER_UTF8_BYTES);
|
|
59
|
+
this.#updateStreamingContent();
|
|
32
60
|
}
|
|
33
61
|
|
|
34
62
|
setAnswer(text: string): void {
|
|
35
63
|
if (this.#closed) return;
|
|
36
|
-
this.#answer = text;
|
|
37
|
-
this.#
|
|
64
|
+
this.#answer = truncateUtf8(text, BTW_MAX_ANSWER_UTF8_BYTES);
|
|
65
|
+
this.#updateStreamingContent();
|
|
38
66
|
}
|
|
39
67
|
|
|
40
68
|
markComplete(): void {
|
|
@@ -54,21 +82,33 @@ export class BtwPanelComponent extends Container {
|
|
|
54
82
|
markError(message: string): void {
|
|
55
83
|
if (this.#closed) return;
|
|
56
84
|
this.#state = "error";
|
|
57
|
-
this.#
|
|
85
|
+
this.#answer = "";
|
|
86
|
+
this.#errorMessage = sanitizeBtwError(message);
|
|
58
87
|
this.#rebuild();
|
|
59
88
|
}
|
|
60
89
|
|
|
61
90
|
close(): void {
|
|
62
91
|
this.#closed = true;
|
|
92
|
+
this.#question = "";
|
|
93
|
+
this.#completedTurns = [];
|
|
94
|
+
this.#answer = "";
|
|
95
|
+
this.#errorMessage = undefined;
|
|
96
|
+
this.#streamingContent.clear();
|
|
97
|
+
this.clear();
|
|
63
98
|
}
|
|
64
99
|
|
|
65
100
|
#rebuild(): void {
|
|
66
101
|
this.clear();
|
|
67
102
|
this.addChild(new DynamicBorder(str => theme.fg("dim", str)));
|
|
68
103
|
this.addChild(new Spacer(1));
|
|
104
|
+
for (const turn of this.#completedTurns) {
|
|
105
|
+
this.addChild(this.#turnComponent(turn));
|
|
106
|
+
this.addChild(new Spacer(1));
|
|
107
|
+
}
|
|
69
108
|
this.addChild(new Text(theme.fg("accent", replaceTabs(this.#question)), 1, 0));
|
|
70
109
|
this.addChild(new Spacer(1));
|
|
71
|
-
this
|
|
110
|
+
this.#updateStreamingContent(false);
|
|
111
|
+
this.addChild(this.#streamingContent);
|
|
72
112
|
this.addChild(new Spacer(1));
|
|
73
113
|
this.addChild(new Text(this.#footerLine(), 1, 0));
|
|
74
114
|
this.addChild(new Spacer(1));
|
|
@@ -76,29 +116,55 @@ export class BtwPanelComponent extends Container {
|
|
|
76
116
|
this.#tui.requestRender();
|
|
77
117
|
}
|
|
78
118
|
|
|
119
|
+
#updateStreamingContent(requestRender = true): void {
|
|
120
|
+
this.#streamingContent.clear();
|
|
121
|
+
this.#streamingContent.addChild(this.#contentComponent(this.#state, this.#answer, this.#errorMessage));
|
|
122
|
+
if (requestRender) this.#tui.requestRender();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#currentTurn(): BtwTurn {
|
|
126
|
+
return {
|
|
127
|
+
question: this.#question,
|
|
128
|
+
answer: this.#answer,
|
|
129
|
+
state: this.#state,
|
|
130
|
+
errorMessage: this.#errorMessage,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#turnComponent(turn: BtwTurn): Component {
|
|
135
|
+
const container = new Container();
|
|
136
|
+
container.addChild(new Text(theme.fg("accent", replaceTabs(turn.question)), 1, 0));
|
|
137
|
+
container.addChild(new Spacer(1));
|
|
138
|
+
container.addChild(this.#contentComponent(turn.state, turn.answer, turn.errorMessage));
|
|
139
|
+
return container;
|
|
140
|
+
}
|
|
141
|
+
|
|
79
142
|
#footerLine(): string {
|
|
80
143
|
switch (this.#state) {
|
|
81
144
|
case "running":
|
|
82
|
-
return theme.fg("muted", "Esc cancel /btw");
|
|
145
|
+
return theme.fg("muted", "Esc cancel /btw and return to main chat");
|
|
83
146
|
case "complete":
|
|
84
|
-
return theme.fg("muted", "Esc
|
|
147
|
+
return theme.fg("muted", "Type a follow-up · Esc return to main chat");
|
|
85
148
|
case "aborted":
|
|
86
|
-
return theme.fg(
|
|
149
|
+
return theme.fg(
|
|
150
|
+
"warning",
|
|
151
|
+
`${theme.status.warning} Cancelled · Type a follow-up · Esc return to main chat`,
|
|
152
|
+
);
|
|
87
153
|
case "error":
|
|
88
|
-
return theme.fg("error", `${theme.status.error} Error · Esc
|
|
154
|
+
return theme.fg("error", `${theme.status.error} Error · Type a follow-up · Esc return to main chat`);
|
|
89
155
|
}
|
|
90
156
|
}
|
|
91
157
|
|
|
92
|
-
#contentComponent(): Component {
|
|
93
|
-
if (
|
|
94
|
-
return new Text(theme.fg("error", replaceTabs(
|
|
158
|
+
#contentComponent(state: BtwPanelState, answer: string, errorMessage: string | undefined): Component {
|
|
159
|
+
if (state === "error") {
|
|
160
|
+
return new Text(theme.fg("error", replaceTabs(errorMessage ?? "Unknown error")), 1, 0);
|
|
95
161
|
}
|
|
96
|
-
const text = replaceTabs(
|
|
162
|
+
const text = replaceTabs(answer).trim();
|
|
97
163
|
if (!text) {
|
|
98
|
-
const waiting =
|
|
99
|
-
this.#state === "running" ? `${theme.status.pending} Waiting for response…` : "No text returned.";
|
|
164
|
+
const waiting = state === "running" ? `${theme.status.pending} Waiting for response…` : "No text returned.";
|
|
100
165
|
return new Text(theme.fg("dim", waiting), 1, 0);
|
|
101
166
|
}
|
|
167
|
+
if (state === "running") return new Text(text, 1, 0);
|
|
102
168
|
return new Markdown(text, 1, 0, getMarkdownTheme());
|
|
103
169
|
}
|
|
104
170
|
}
|
|
@@ -35,6 +35,7 @@ import type { Settings } from "../../config/settings";
|
|
|
35
35
|
import { type ThemeColor, theme } from "../../modes/theme/theme";
|
|
36
36
|
import { formatModelOnboardingInlineHint } from "../../setup/model-onboarding-guidance";
|
|
37
37
|
import { formatClampedModelSelector, getThinkingLevelMetadata, parseThinkingLevel } from "../../thinking";
|
|
38
|
+
import { getConfiguredImageModel } from "../../tools/image-gen";
|
|
38
39
|
import { getTabBarTheme } from "../shared";
|
|
39
40
|
import { DynamicBorder } from "./dynamic-border";
|
|
40
41
|
|
|
@@ -118,6 +119,9 @@ export type ModelSelectorSelection =
|
|
|
118
119
|
| {
|
|
119
120
|
kind: "deleteProfile";
|
|
120
121
|
profileName: string;
|
|
122
|
+
}
|
|
123
|
+
| {
|
|
124
|
+
kind: "imageGeneration";
|
|
121
125
|
};
|
|
122
126
|
|
|
123
127
|
interface PendingThinkingChoice {
|
|
@@ -183,13 +187,18 @@ interface PresetBrowseRow {
|
|
|
183
187
|
kind: "browse";
|
|
184
188
|
}
|
|
185
189
|
|
|
190
|
+
interface PresetImageGenerationRow {
|
|
191
|
+
kind: "imageGeneration";
|
|
192
|
+
}
|
|
193
|
+
|
|
186
194
|
type PresetLandingRow =
|
|
187
195
|
| PresetGroupRow
|
|
188
196
|
| PresetProfileRow
|
|
189
197
|
| PresetCreateRow
|
|
190
198
|
| PresetCreateUnavailableRow
|
|
191
199
|
| PresetAlreadySavedRow
|
|
192
|
-
| PresetBrowseRow
|
|
200
|
+
| PresetBrowseRow
|
|
201
|
+
| PresetImageGenerationRow;
|
|
193
202
|
|
|
194
203
|
// Stable logical identity for a preset landing row, independent of its current
|
|
195
204
|
// list position. Used to relocate the cursor after the expanded group changes so
|
|
@@ -208,6 +217,8 @@ function presetRowIdentity(row: PresetLandingRow): string {
|
|
|
208
217
|
return "createUnavailable";
|
|
209
218
|
case "alreadySaved":
|
|
210
219
|
return `alreadySaved:${row.profile.name}`;
|
|
220
|
+
case "imageGeneration":
|
|
221
|
+
return "imageGeneration";
|
|
211
222
|
}
|
|
212
223
|
}
|
|
213
224
|
|
|
@@ -334,6 +345,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
334
345
|
#selectedActionIndex: number = 0;
|
|
335
346
|
#pendingThinkingChoice?: PendingThinkingChoice;
|
|
336
347
|
#selectedThinkingIndex: number = 0;
|
|
348
|
+
#assignmentState: "idle" | "assigning" = "idle";
|
|
349
|
+
#closeAfterAssignment = false;
|
|
337
350
|
|
|
338
351
|
// Preset landing state
|
|
339
352
|
#viewMode: ModelSelectorViewMode = "presets";
|
|
@@ -962,6 +975,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
962
975
|
} else {
|
|
963
976
|
rows.push({ kind: "createUnavailable", label: "Select a model before creating a custom preset" });
|
|
964
977
|
}
|
|
978
|
+
rows.push({ kind: "imageGeneration" });
|
|
965
979
|
rows.push({ kind: "browse" });
|
|
966
980
|
return rows;
|
|
967
981
|
}
|
|
@@ -1047,7 +1061,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
1047
1061
|
selected.kind === "browse" ||
|
|
1048
1062
|
selected.kind === "create" ||
|
|
1049
1063
|
selected.kind === "createUnavailable" ||
|
|
1050
|
-
selected.kind === "alreadySaved"
|
|
1064
|
+
selected.kind === "alreadySaved" ||
|
|
1065
|
+
selected.kind === "imageGeneration"
|
|
1051
1066
|
)
|
|
1052
1067
|
return;
|
|
1053
1068
|
if (this.#expandedPresetProviderId === selected.groupId) return;
|
|
@@ -1063,7 +1078,8 @@ export class ModelSelectorComponent extends Container {
|
|
|
1063
1078
|
selected.kind === "browse" ||
|
|
1064
1079
|
selected.kind === "create" ||
|
|
1065
1080
|
selected.kind === "createUnavailable" ||
|
|
1066
|
-
selected.kind === "alreadySaved"
|
|
1081
|
+
selected.kind === "alreadySaved" ||
|
|
1082
|
+
selected.kind === "imageGeneration"
|
|
1067
1083
|
)
|
|
1068
1084
|
return;
|
|
1069
1085
|
if (this.#expandedPresetProviderId !== selected.groupId) return;
|
|
@@ -1162,6 +1178,16 @@ export class ModelSelectorComponent extends Container {
|
|
|
1162
1178
|
this.#listContainer.addChild(new Text(`${prefix}${selected ? theme.fg("accent", label) : label}`, 0, 0));
|
|
1163
1179
|
continue;
|
|
1164
1180
|
}
|
|
1181
|
+
if (row.kind === "imageGeneration") {
|
|
1182
|
+
const config = getConfiguredImageModel();
|
|
1183
|
+
const current =
|
|
1184
|
+
config && config.provider !== "auto"
|
|
1185
|
+
? `${config.provider}${config.model ? ` (${config.model})` : ""}`
|
|
1186
|
+
: "Auto";
|
|
1187
|
+
const label = `Image Generation: ${current}`;
|
|
1188
|
+
this.#listContainer.addChild(new Text(`${prefix}${selected ? theme.fg("accent", label) : label}`, 0, 0));
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1165
1191
|
if (row.kind === "group") {
|
|
1166
1192
|
const authenticated = this.#isPresetGroupUsable(row.profiles);
|
|
1167
1193
|
const mark = this.#providerAuthPending ? "…" : authenticated ? "✓" : "✗";
|
|
@@ -1469,6 +1495,12 @@ export class ModelSelectorComponent extends Container {
|
|
|
1469
1495
|
}
|
|
1470
1496
|
|
|
1471
1497
|
handleInput(keyData: string): void {
|
|
1498
|
+
if (this.#assignmentState === "assigning") {
|
|
1499
|
+
if (getKeybindings().matches(keyData, "tui.select.cancel")) {
|
|
1500
|
+
this.#closeAfterAssignment = true;
|
|
1501
|
+
}
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1472
1504
|
if (this.#pendingThinkingChoice) {
|
|
1473
1505
|
this.#handleThinkingMenuInput(keyData);
|
|
1474
1506
|
return;
|
|
@@ -1646,6 +1678,10 @@ export class ModelSelectorComponent extends Container {
|
|
|
1646
1678
|
this.#switchToModelMode();
|
|
1647
1679
|
return;
|
|
1648
1680
|
}
|
|
1681
|
+
if (row.kind === "imageGeneration") {
|
|
1682
|
+
this.#onSelectCallback({ kind: "imageGeneration" });
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1649
1685
|
if (row.kind === "group") {
|
|
1650
1686
|
// A group is a list of alternative presets; only surface a login hint
|
|
1651
1687
|
// when none of its members are usable. A partially-usable group stays
|
|
@@ -1833,24 +1869,50 @@ export class ModelSelectorComponent extends Container {
|
|
|
1833
1869
|
? item.selector
|
|
1834
1870
|
: formatModelSelectorValue(item.selector, selectedThinkingLevel);
|
|
1835
1871
|
|
|
1836
|
-
|
|
1837
|
-
for (const targetRole of roles ?? [role]) {
|
|
1838
|
-
this.#roles[targetRole] = { model: item.model, thinkingLevel: selectedThinkingLevel };
|
|
1839
|
-
}
|
|
1840
|
-
|
|
1841
|
-
// Notify caller (for updating agent state if needed)
|
|
1842
|
-
this.#onSelectCallback({
|
|
1872
|
+
const selection: Extract<ModelSelectorSelection, { kind: "assignment" }> = {
|
|
1843
1873
|
kind: "assignment",
|
|
1844
1874
|
model: item.model,
|
|
1845
1875
|
role,
|
|
1846
1876
|
roles,
|
|
1847
1877
|
thinkingLevel: selectedThinkingLevel,
|
|
1848
1878
|
selector: selectorValue,
|
|
1849
|
-
}
|
|
1879
|
+
};
|
|
1880
|
+
if (this.#isTrackedSingleAssignment(selection)) {
|
|
1881
|
+
void this.#handleTrackedAssignment(selection);
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// Update local state for UI
|
|
1886
|
+
for (const targetRole of roles ?? [role]) {
|
|
1887
|
+
this.#roles[targetRole] = { model: item.model, thinkingLevel: selectedThinkingLevel };
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// Notify caller (for updating agent state if needed)
|
|
1891
|
+
this.#onSelectCallback(selection);
|
|
1850
1892
|
|
|
1851
1893
|
// Update list to show new badges
|
|
1852
1894
|
this.#updateList();
|
|
1853
1895
|
}
|
|
1896
|
+
#isTrackedSingleAssignment(selection: Extract<ModelSelectorSelection, { kind: "assignment" }>): boolean {
|
|
1897
|
+
return selection.role !== null && selection.roles === undefined;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
async #handleTrackedAssignment(selection: Extract<ModelSelectorSelection, { kind: "assignment" }>): Promise<void> {
|
|
1901
|
+
if (this.#assignmentState !== "idle") return;
|
|
1902
|
+
this.#assignmentState = "assigning";
|
|
1903
|
+
this.#tui.requestRender();
|
|
1904
|
+
try {
|
|
1905
|
+
await Promise.resolve(this.#onSelectCallback(selection));
|
|
1906
|
+
} catch {
|
|
1907
|
+
// The controller reports tracked assignment failures before rethrowing.
|
|
1908
|
+
} finally {
|
|
1909
|
+
this.#assignmentState = "idle";
|
|
1910
|
+
const shouldClose = this.#closeAfterAssignment;
|
|
1911
|
+
this.#closeAfterAssignment = false;
|
|
1912
|
+
if (shouldClose) this.#onCancelCallback();
|
|
1913
|
+
else this.#tui.requestRender();
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1854
1916
|
|
|
1855
1917
|
getSearchInput(): Input {
|
|
1856
1918
|
return this.#searchInput;
|
|
@@ -122,7 +122,7 @@ export class PlanPreviewOverlay extends Container {
|
|
|
122
122
|
? `${this.#inputKind === "notes" ? "Notes" : `Comment for source line ${this.#sourceLine}`} (Enter save, Esc cancel): ${this.#input.getValue()}`
|
|
123
123
|
: this.#focus === "sourceSelect"
|
|
124
124
|
? `Source line ${this.#sourceLine}/${this.lines.length || 1} j/k:select c:comment Esc:done`
|
|
125
|
-
: `${this.#scroll + 1}-${Math.min(this.#scroll + height, body.length)}/${body.length || 1} s:source line n:notes ${this.options.externalEditorKey ? `${this.options.externalEditorKey
|
|
125
|
+
: `${this.#scroll + 1}-${Math.min(this.#scroll + height, body.length)}/${body.length || 1} s:source line n:notes ${this.options.externalEditorKey ? `${this.options.externalEditorKey}:edit ` : ""}Tab:actions PgUp/PgDn:page`;
|
|
126
126
|
|
|
127
127
|
const rendered = [
|
|
128
128
|
new DynamicBorder().render(width)[0] ?? "",
|
|
@@ -60,6 +60,7 @@ type WizardStep =
|
|
|
60
60
|
*/
|
|
61
61
|
export interface MCPAddWizardOAuthResult {
|
|
62
62
|
credentialId: string;
|
|
63
|
+
tokenEndpoint: string;
|
|
63
64
|
clientId?: string;
|
|
64
65
|
clientSecret?: string;
|
|
65
66
|
}
|
|
@@ -122,6 +123,7 @@ export class MCPAddWizard extends Container {
|
|
|
122
123
|
#onCancelCallback: () => void;
|
|
123
124
|
#onOAuthCallback:
|
|
124
125
|
| ((
|
|
126
|
+
endpointUrl: string,
|
|
125
127
|
authUrl: string,
|
|
126
128
|
tokenUrl: string,
|
|
127
129
|
clientId: string,
|
|
@@ -141,6 +143,7 @@ export class MCPAddWizard extends Container {
|
|
|
141
143
|
onComplete: (name: string, config: MCPServerConfig, scope: Scope) => void,
|
|
142
144
|
onCancel: () => void,
|
|
143
145
|
onOAuth?: (
|
|
146
|
+
endpointUrl: string,
|
|
144
147
|
authUrl: string,
|
|
145
148
|
tokenUrl: string,
|
|
146
149
|
clientId: string,
|
|
@@ -1189,6 +1192,7 @@ export class MCPAddWizard extends Container {
|
|
|
1189
1192
|
try {
|
|
1190
1193
|
// Call OAuth handler
|
|
1191
1194
|
const oauthResult = await this.#onOAuthCallback(
|
|
1195
|
+
this.#state.url,
|
|
1192
1196
|
this.#state.oauthAuthUrl,
|
|
1193
1197
|
this.#state.oauthTokenUrl,
|
|
1194
1198
|
this.#state.oauthClientId,
|
|
@@ -1200,6 +1204,7 @@ export class MCPAddWizard extends Container {
|
|
|
1200
1204
|
// Store credential ID + any dynamically-registered client credentials,
|
|
1201
1205
|
// so the final mcp.json entry persists everything needed for refresh.
|
|
1202
1206
|
this.#state.oauthCredentialId = oauthResult.credentialId;
|
|
1207
|
+
this.#state.oauthTokenUrl = oauthResult.tokenEndpoint;
|
|
1203
1208
|
if (oauthResult.clientId) this.#state.oauthClientId = oauthResult.clientId;
|
|
1204
1209
|
if (oauthResult.clientSecret) this.#state.oauthClientSecret = oauthResult.clientSecret;
|
|
1205
1210
|
|
|
@@ -3,8 +3,12 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { type Component, truncateToWidth, visibleWidth } from "@gajae-code/tui";
|
|
4
4
|
import { formatCount, getProjectDir } from "@gajae-code/utils";
|
|
5
5
|
import { $ } from "bun";
|
|
6
|
-
import
|
|
7
|
-
|
|
6
|
+
import {
|
|
7
|
+
type AppKeybinding,
|
|
8
|
+
KEYBINDINGS,
|
|
9
|
+
type KeybindingsManager,
|
|
10
|
+
type KeyDisplayContext,
|
|
11
|
+
} from "../../config/keybindings";
|
|
8
12
|
import { settings } from "../../config/settings";
|
|
9
13
|
import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../config/settings-schema";
|
|
10
14
|
import { theme } from "../../modes/theme/theme";
|
|
@@ -46,6 +50,7 @@ export interface StatusLineSettings {
|
|
|
46
50
|
previewHighlightSegment?: StatusLineSegmentId;
|
|
47
51
|
showHookStatus?: boolean;
|
|
48
52
|
showSkillHud?: boolean;
|
|
53
|
+
showActionHints?: boolean;
|
|
49
54
|
sessionAccent?: boolean;
|
|
50
55
|
maxRows?: number;
|
|
51
56
|
}
|
|
@@ -55,6 +60,7 @@ export interface StatusLineComponentOptions {
|
|
|
55
60
|
actionRegistry?: ActionRegistry<void>;
|
|
56
61
|
getKeybindings?: () => KeybindingsManager;
|
|
57
62
|
focusDomain?: FocusDomain;
|
|
63
|
+
keyDisplayContext?: KeyDisplayContext;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
export interface StatusLineActionHint {
|
|
@@ -92,6 +98,7 @@ export function getAvailableActionHints(
|
|
|
92
98
|
getKeybindings: (() => KeybindingsManager) | undefined,
|
|
93
99
|
width: number,
|
|
94
100
|
domain: FocusDomain = "composer",
|
|
101
|
+
keyDisplayContext?: KeyDisplayContext,
|
|
95
102
|
): StatusLineActionHint[] {
|
|
96
103
|
if (!actionRegistry || !getKeybindings || width <= 0) return [];
|
|
97
104
|
const keybindings = getKeybindings();
|
|
@@ -108,7 +115,9 @@ export function getAvailableActionHints(
|
|
|
108
115
|
if (!(bindingId in KEYBINDINGS)) continue;
|
|
109
116
|
const keys = keybindings.getKeys(bindingId);
|
|
110
117
|
if (keys.length === 0) continue;
|
|
111
|
-
const content =
|
|
118
|
+
const content =
|
|
119
|
+
theme.fg("dim", keybindings.getDisplayString(bindingId, keyDisplayContext)) +
|
|
120
|
+
theme.fg("muted", ` ${action.title}`);
|
|
112
121
|
const nextWidth = visibleWidth(content) + (selected.length === 0 ? 0 : 3);
|
|
113
122
|
if (used + nextWidth > width) break;
|
|
114
123
|
selected.push({ id: action.id, content });
|
|
@@ -160,6 +169,7 @@ export class StatusLineComponent implements Component {
|
|
|
160
169
|
#actionRegistry: ActionRegistry<void> | undefined;
|
|
161
170
|
#getKeybindings: (() => KeybindingsManager) | undefined;
|
|
162
171
|
#focusDomain: FocusDomain;
|
|
172
|
+
#keyDisplayContext: KeyDisplayContext | undefined;
|
|
163
173
|
|
|
164
174
|
#resolvedSettingsCache:
|
|
165
175
|
| (Required<Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">> &
|
|
@@ -199,6 +209,7 @@ export class StatusLineComponent implements Component {
|
|
|
199
209
|
separator: settings.get("statusLine.separator"),
|
|
200
210
|
showHookStatus: settings.get("statusLine.showHookStatus"),
|
|
201
211
|
showSkillHud: settings.get("statusLine.showSkillHud"),
|
|
212
|
+
showActionHints: settings.get("statusLine.showActionHints"),
|
|
202
213
|
segmentOptions: settings.getGroup("statusLine").segmentOptions,
|
|
203
214
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
204
215
|
maxRows: settings.get("statusLine.maxRows"),
|
|
@@ -207,6 +218,7 @@ export class StatusLineComponent implements Component {
|
|
|
207
218
|
this.#actionRegistry = options.actionRegistry;
|
|
208
219
|
this.#getKeybindings = options.getKeybindings;
|
|
209
220
|
this.#focusDomain = options.focusDomain ?? "composer";
|
|
221
|
+
this.#keyDisplayContext = options.keyDisplayContext;
|
|
210
222
|
}
|
|
211
223
|
|
|
212
224
|
updateSettings(settings: StatusLineSettings): void {
|
|
@@ -791,7 +803,16 @@ export class StatusLineComponent implements Component {
|
|
|
791
803
|
}
|
|
792
804
|
|
|
793
805
|
const right: string[] = [];
|
|
794
|
-
const actionHints =
|
|
806
|
+
const actionHints =
|
|
807
|
+
effectiveSettings.showActionHints === false
|
|
808
|
+
? []
|
|
809
|
+
: getAvailableActionHints(
|
|
810
|
+
this.#actionRegistry,
|
|
811
|
+
this.#getKeybindings,
|
|
812
|
+
width,
|
|
813
|
+
this.#focusDomain,
|
|
814
|
+
this.#keyDisplayContext,
|
|
815
|
+
);
|
|
795
816
|
for (const segId of effectiveSettings.rightSegments) {
|
|
796
817
|
const rendered = renderSegment(segId, ctx);
|
|
797
818
|
if (rendered.visible && rendered.content) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Component, padding, TERMINAL, truncateToWidth, visibleWidth } from "@gajae-code/tui";
|
|
2
2
|
import { APP_NAME } from "@gajae-code/utils";
|
|
3
3
|
import { formatBuildLabel } from "../../build-metadata";
|
|
4
|
+
import { formatKeyHint, type KeyDisplayContext } from "../../config/keybindings";
|
|
4
5
|
import { type ThemeColor, theme } from "../../modes/theme/theme";
|
|
5
6
|
|
|
6
7
|
export interface RecentSession {
|
|
@@ -21,26 +22,28 @@ export interface WelcomeComponentOptions {
|
|
|
21
22
|
changelogMarkdown?: string;
|
|
22
23
|
collapseChangelog?: boolean;
|
|
23
24
|
buildLabel?: string;
|
|
25
|
+
keyDisplayContext?: KeyDisplayContext;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const WELCOME_STATIC_RIGHT_ROWS_EXCLUDING_DYNAMIC_SECTIONS = 9;
|
|
27
29
|
const DEFAULT_WHATS_NEW_ROWS = 3;
|
|
28
30
|
const MAX_WHATS_NEW_ROWS = 12;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
];
|
|
31
|
+
|
|
32
|
+
function flowKeyItems(context: KeyDisplayContext): ReadonlyArray<{ key: string; label: string }> {
|
|
33
|
+
const newlineKey = context.platform === "win32" ? "alt+enter" : "ctrl+j";
|
|
34
|
+
return [
|
|
35
|
+
{ key: "/", label: "commands" },
|
|
36
|
+
{ key: "#", label: "actions" },
|
|
37
|
+
{ key: "!", label: "shell" },
|
|
38
|
+
{ key: "$", label: "python" },
|
|
39
|
+
{ key: "?", label: "keymap" },
|
|
40
|
+
{ key: "ctrl+l", label: "model" },
|
|
41
|
+
{ key: "shift+tab", label: "reasoning" },
|
|
42
|
+
{ key: "tab", label: "complete" },
|
|
43
|
+
{ key: newlineKey, label: "newline" },
|
|
44
|
+
{ key: "ctrl+c", label: "clear" },
|
|
45
|
+
];
|
|
46
|
+
}
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
49
|
* GJC-native launch surface with compact command affordances, project
|
|
@@ -325,7 +328,8 @@ export class WelcomeComponent implements Component {
|
|
|
325
328
|
}
|
|
326
329
|
|
|
327
330
|
#flowKeyItemText(item: { key: string; label: string }): string {
|
|
328
|
-
|
|
331
|
+
const context = this.options.keyDisplayContext ?? { platform: process.platform };
|
|
332
|
+
return `${theme.fg("dim", formatKeyHint(item.key, context))}${theme.fg("muted", ` ${item.label}`)}`;
|
|
329
333
|
}
|
|
330
334
|
|
|
331
335
|
#flowKeyRows(width: number): string[] {
|
|
@@ -333,7 +337,7 @@ export class WelcomeComponent implements Component {
|
|
|
333
337
|
const separator = ` ${theme.fg("dim", "·")} `;
|
|
334
338
|
const rows: string[] = [];
|
|
335
339
|
let current = "";
|
|
336
|
-
for (const item of
|
|
340
|
+
for (const item of flowKeyItems(this.options.keyDisplayContext ?? { platform: process.platform })) {
|
|
337
341
|
const segment = this.#flowKeyItemText(item);
|
|
338
342
|
const next = current ? `${current}${separator}${segment}` : segment;
|
|
339
343
|
if (current && visibleWidth(next) > contentWidth) {
|
|
@@ -352,7 +356,7 @@ export class WelcomeComponent implements Component {
|
|
|
352
356
|
const rowLimit = Math.max(1, Math.floor(maxRows));
|
|
353
357
|
if (rows.length <= rowLimit) return rows;
|
|
354
358
|
if (rowLimit === 1) {
|
|
355
|
-
const firstItem =
|
|
359
|
+
const firstItem = flowKeyItems(this.options.keyDisplayContext ?? { platform: process.platform })[0];
|
|
356
360
|
const firstSegment = firstItem ? this.#flowKeyItemText(firstItem) : theme.fg("dim", "keys");
|
|
357
361
|
return [this.#fitToWidth(` ${firstSegment} ${theme.fg("dim", "· … ")}${theme.bold("/help")}`, width)];
|
|
358
362
|
}
|