@gajae-code/coding-agent 0.7.10 → 0.8.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 +58 -0
- package/README.md +2 -2
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/config/keybindings.d.ts +11 -5
- package/dist/types/config/settings-schema.d.ts +25 -2
- package/dist/types/defaults/gjc-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/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/gjc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +5 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/components/status-line.d.ts +8 -0
- 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 +5 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +2 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/types.d.ts +2 -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/session/agent-session.d.ts +8 -5
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +7 -0
- package/dist/types/utils/cmux-workspace.d.ts +30 -1
- package/dist/types/web/search/index.d.ts +3 -1
- package/dist/types/web/search/provider.d.ts +9 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
- package/package.json +10 -10
- package/src/capability/mcp.ts +2 -0
- package/src/cli/update-cli.ts +24 -0
- package/src/cli/web-search-cli.ts +2 -5
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/config/keybindings.ts +14 -5
- package/src/config/mcp-schema.json +5 -1
- package/src/config/model-profiles.ts +16 -0
- package/src/config/settings-schema.ts +18 -2
- package/src/coordinator-mcp/server.ts +15 -3
- package/src/defaults/gjc/skills/ralplan/SKILL.md +16 -14
- package/src/defaults/gjc-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/gjc-runtime/launch-tmux.ts +164 -42
- package/src/gjc-runtime/ralplan-runtime.ts +22 -6
- package/src/gjc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/gjc-runtime/session-state-sidecar.ts +167 -24
- package/src/gjc-runtime/state-runtime.ts +8 -5
- package/src/gjc-runtime/state-writer.ts +5 -5
- package/src/gjc-runtime/team-runtime.ts +56 -51
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/main.ts +2 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/components/agent-dashboard.ts +1 -34
- package/src/modes/components/custom-editor.ts +26 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -48
- package/src/modes/components/model-selector.ts +64 -4
- package/src/modes/components/session-selector.ts +19 -9
- package/src/modes/components/settings-selector.ts +13 -2
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line.ts +215 -86
- 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 +171 -35
- package/src/modes/controllers/command-controller.ts +23 -0
- package/src/modes/controllers/event-controller.ts +4 -0
- package/src/modes/controllers/input-controller.ts +192 -39
- package/src/modes/controllers/selector-controller.ts +25 -28
- package/src/modes/interactive-mode.ts +36 -30
- package/src/modes/prompt-action-autocomplete.ts +23 -1
- package/src/modes/rpc/rpc-types.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +9 -3
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/index.ts +20 -1
- package/src/notifications/lifecycle-control-runtime.ts +12 -3
- package/src/notifications/telegram-daemon.ts +4 -4
- 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 +15 -1
- 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 +7 -6
- package/src/session/agent-session.ts +81 -18
- package/src/session/session-manager.ts +58 -18
- package/src/slash-commands/builtin-registry.ts +30 -10
- 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/utils/cmux-workspace.ts +134 -7
- package/src/utils/title-generator.ts +1 -1
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +118 -45
- package/src/web/search/provider.ts +65 -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/workflow/workflow-intent-diff.ts +199 -0
- package/dist/types/discovery/vscode.d.ts +0 -1
- package/src/discovery/vscode.ts +0 -105
|
@@ -14,6 +14,12 @@ export interface LspServerInfo {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export type WelcomeLogoMode = "unicode" | "square" | "ascii";
|
|
17
|
+
export interface WelcomeComponentOptions {
|
|
18
|
+
getViewportRows?: () => number | undefined;
|
|
19
|
+
getReservedBottomRows?: (termWidth: number) => number;
|
|
20
|
+
changelogMarkdown?: string;
|
|
21
|
+
collapseChangelog?: boolean;
|
|
22
|
+
}
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* GJC-native launch surface with compact command affordances, project
|
|
@@ -30,6 +36,7 @@ export class WelcomeComponent implements Component {
|
|
|
30
36
|
private recentSessions: RecentSession[] = [],
|
|
31
37
|
private lspServers: LspServerInfo[] = [],
|
|
32
38
|
private readonly logoMode: WelcomeLogoMode = "unicode",
|
|
39
|
+
private readonly options: WelcomeComponentOptions = {},
|
|
33
40
|
) {}
|
|
34
41
|
|
|
35
42
|
invalidate(): void {}
|
|
@@ -50,6 +57,11 @@ export class WelcomeComponent implements Component {
|
|
|
50
57
|
}
|
|
51
58
|
requestRender();
|
|
52
59
|
}, INTRO_TICK_MS);
|
|
60
|
+
this.#animTimer.unref?.();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
dispose(): void {
|
|
64
|
+
this.#stopAnimation();
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
#stopAnimation(): void {
|
|
@@ -74,15 +86,20 @@ export class WelcomeComponent implements Component {
|
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
render(termWidth: number): string[] {
|
|
77
|
-
|
|
78
|
-
const boxWidth = Math.max(0, termWidth - 2);
|
|
89
|
+
const boxWidth = Math.max(0, termWidth);
|
|
79
90
|
if (boxWidth < 4) {
|
|
80
91
|
return [];
|
|
81
92
|
}
|
|
93
|
+
|
|
94
|
+
const targetRows = this.#targetRows(termWidth);
|
|
95
|
+
if (targetRows !== undefined && targetRows <= 0) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
const targetContentRows = targetRows === undefined ? undefined : Math.max(0, targetRows - 2);
|
|
82
99
|
const dualContentWidth = boxWidth - 3; // 3 = │ + │ + │
|
|
83
|
-
const preferredLeftCol =
|
|
84
|
-
const minLeftCol =
|
|
85
|
-
const minRightCol =
|
|
100
|
+
const preferredLeftCol = 40;
|
|
101
|
+
const minLeftCol = 20; // logo mark plus GJC identity labels
|
|
102
|
+
const minRightCol = 24;
|
|
86
103
|
const modelPill = this.#pill(theme.icon.model || "model", this.modelName, "statusLineModel");
|
|
87
104
|
const providerPill = this.#pill(theme.icon.package || "provider", this.providerName, "statusLinePath");
|
|
88
105
|
const logoLines = this.#logoLines();
|
|
@@ -90,12 +107,12 @@ export class WelcomeComponent implements Component {
|
|
|
90
107
|
const leftMinContentWidth = Math.max(
|
|
91
108
|
minLeftCol,
|
|
92
109
|
logoMinWidth,
|
|
93
|
-
visibleWidth("
|
|
110
|
+
visibleWidth("GJC Forge"),
|
|
94
111
|
visibleWidth("shape · act · prove"),
|
|
95
112
|
visibleWidth(modelPill),
|
|
96
113
|
visibleWidth(providerPill),
|
|
97
114
|
);
|
|
98
|
-
const desiredLeftCol = Math.min(preferredLeftCol, Math.max(minLeftCol, Math.floor(dualContentWidth * 0.
|
|
115
|
+
const desiredLeftCol = Math.min(preferredLeftCol, Math.max(minLeftCol, Math.floor(dualContentWidth * 0.38)));
|
|
99
116
|
const dualLeftCol =
|
|
100
117
|
dualContentWidth >= minRightCol + 1
|
|
101
118
|
? Math.min(desiredLeftCol, dualContentWidth - minRightCol)
|
|
@@ -107,36 +124,36 @@ export class WelcomeComponent implements Component {
|
|
|
107
124
|
|
|
108
125
|
const logoColored = this.#currentLogoFrame(logoLines);
|
|
109
126
|
|
|
110
|
-
// Left column - centered content
|
|
111
127
|
const leftLines = [
|
|
112
128
|
"",
|
|
113
|
-
this.#centerText(theme.bold(theme.fg("accent", "
|
|
129
|
+
this.#centerText(theme.bold(theme.fg("accent", "GJC Forge")), leftCol),
|
|
114
130
|
this.#centerText(theme.fg("dim", "shape · act · prove"), leftCol),
|
|
115
131
|
"",
|
|
116
132
|
...logoColored.map(l => this.#centerText(l, leftCol)),
|
|
117
133
|
"",
|
|
134
|
+
this.#centerText(this.#loadingLine(), leftCol),
|
|
118
135
|
this.#centerText(modelPill, leftCol),
|
|
119
136
|
this.#centerText(providerPill, leftCol),
|
|
137
|
+
"",
|
|
120
138
|
];
|
|
121
139
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
140
|
+
const buildSeparator = (columnWidth: number): string =>
|
|
141
|
+
` ${theme.fg("dim", theme.boxRound.horizontal.repeat(Math.max(0, columnWidth - 2)))}`;
|
|
142
|
+
|
|
143
|
+
const rightColumnWidth = showRightColumn ? rightCol : leftCol;
|
|
144
|
+
const separator = buildSeparator(rightColumnWidth);
|
|
145
|
+
const changelogBudget = Math.max(1, Math.min(6, Math.floor((targetContentRows ?? 18) * 0.3)));
|
|
125
146
|
|
|
126
|
-
// Recent sessions content
|
|
127
147
|
const sessionLines: string[] = [];
|
|
128
148
|
if (this.recentSessions.length === 0) {
|
|
129
149
|
sessionLines.push(` ${theme.fg("dim", "No saved trails")}`);
|
|
130
150
|
} else {
|
|
131
|
-
// Reserve width for the bullet prefix (" • ") and the trailing " (timeAgo)"
|
|
132
|
-
// so the relative time is never the part that gets truncated. The name
|
|
133
|
-
// absorbs whatever space is left.
|
|
134
151
|
const bulletPrefix = ` ${theme.md.bullet} `;
|
|
135
152
|
const prefixWidth = visibleWidth(bulletPrefix);
|
|
136
153
|
for (const session of this.recentSessions.slice(0, 3)) {
|
|
137
154
|
const timeSuffixRaw = ` (${session.timeAgo})`;
|
|
138
155
|
const timeWidth = visibleWidth(timeSuffixRaw);
|
|
139
|
-
const nameBudget = Math.max(1,
|
|
156
|
+
const nameBudget = Math.max(1, rightColumnWidth - prefixWidth - timeWidth);
|
|
140
157
|
const nameVis = visibleWidth(session.name);
|
|
141
158
|
const name = nameVis > nameBudget ? truncateToWidth(session.name, nameBudget) : session.name;
|
|
142
159
|
sessionLines.push(
|
|
@@ -145,12 +162,11 @@ export class WelcomeComponent implements Component {
|
|
|
145
162
|
}
|
|
146
163
|
}
|
|
147
164
|
|
|
148
|
-
// LSP servers content
|
|
149
165
|
const lspLines: string[] = [];
|
|
150
166
|
if (this.lspServers.length === 0) {
|
|
151
167
|
lspLines.push(` ${theme.fg("dim", "No LSP servers")}`);
|
|
152
168
|
} else {
|
|
153
|
-
for (const server of this.lspServers) {
|
|
169
|
+
for (const server of this.lspServers.slice(0, 4)) {
|
|
154
170
|
const icon =
|
|
155
171
|
server.status === "ready"
|
|
156
172
|
? theme.styledSymbol("status.success", "success")
|
|
@@ -162,8 +178,11 @@ export class WelcomeComponent implements Component {
|
|
|
162
178
|
}
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
// Right column
|
|
166
181
|
const rightLines = [
|
|
182
|
+
"",
|
|
183
|
+
` ${theme.bold(theme.fg("accent", "What's New"))}`,
|
|
184
|
+
...this.#whatsNewLines(rightColumnWidth, changelogBudget),
|
|
185
|
+
separator,
|
|
167
186
|
` ${theme.bold(theme.fg("accent", "Flow keys"))}`,
|
|
168
187
|
` ${theme.fg("dim", "/")}${theme.fg("muted", " commands")} ${theme.fg("dim", "·")} ${theme.fg(
|
|
169
188
|
"dim",
|
|
@@ -187,7 +206,12 @@ export class WelcomeComponent implements Component {
|
|
|
187
206
|
"",
|
|
188
207
|
];
|
|
189
208
|
|
|
190
|
-
|
|
209
|
+
const contentRows =
|
|
210
|
+
targetContentRows ??
|
|
211
|
+
(showRightColumn ? Math.max(leftLines.length, rightLines.length) : leftLines.length + rightLines.length);
|
|
212
|
+
const outputRows = targetRows === undefined ? Math.max(3, contentRows + 2) : targetRows;
|
|
213
|
+
const bodyRows = Math.max(0, outputRows - 2);
|
|
214
|
+
|
|
191
215
|
const hChar = theme.boxRound.horizontal;
|
|
192
216
|
const h = theme.fg("dim", hChar);
|
|
193
217
|
const v = theme.fg("dim", theme.boxRound.vertical);
|
|
@@ -197,9 +221,7 @@ export class WelcomeComponent implements Component {
|
|
|
197
221
|
const br = theme.fg("dim", theme.boxRound.bottomRight);
|
|
198
222
|
|
|
199
223
|
const lines: string[] = [];
|
|
200
|
-
|
|
201
|
-
// Top border with embedded title
|
|
202
|
-
const title = ` ${APP_NAME} v${this.version} · GJC forge `;
|
|
224
|
+
const title = ` ${APP_NAME} v${this.version} · GJC Forge `;
|
|
203
225
|
const titlePrefixRaw = hChar.repeat(3);
|
|
204
226
|
const titleStyled = theme.fg("dim", titlePrefixRaw) + theme.fg("muted", title);
|
|
205
227
|
const titleVisLen = visibleWidth(titlePrefixRaw) + visibleWidth(title);
|
|
@@ -210,22 +232,32 @@ export class WelcomeComponent implements Component {
|
|
|
210
232
|
const afterTitle = titleSpace - titleVisLen;
|
|
211
233
|
lines.push(tl + titleStyled + theme.fg("dim", hChar.repeat(afterTitle)) + tr);
|
|
212
234
|
}
|
|
235
|
+
if (outputRows === 1) {
|
|
236
|
+
return lines;
|
|
237
|
+
}
|
|
213
238
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const right = this.#fitToWidth(
|
|
239
|
+
if (showRightColumn) {
|
|
240
|
+
const leftBlock = this.#fitBlock(leftLines, bodyRows, "center");
|
|
241
|
+
const rightBlock = this.#fitBlock(rightLines, bodyRows, "top");
|
|
242
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
243
|
+
const left = this.#fitToWidth(leftBlock[i] ?? "", leftCol);
|
|
244
|
+
const right = this.#fitToWidth(rightBlock[i] ?? "", rightCol);
|
|
220
245
|
lines.push(v + left + v + right + v);
|
|
221
|
-
} else {
|
|
222
|
-
lines.push(v + left + v);
|
|
223
246
|
}
|
|
224
|
-
}
|
|
225
|
-
// Bottom border
|
|
226
|
-
if (showRightColumn) {
|
|
227
247
|
lines.push(bl + h.repeat(leftCol) + theme.fg("dim", theme.boxSharp.teeUp) + h.repeat(rightCol) + br);
|
|
228
248
|
} else {
|
|
249
|
+
const compactLeftLines =
|
|
250
|
+
bodyRows < 14
|
|
251
|
+
? [
|
|
252
|
+
this.#centerText(theme.bold(theme.fg("accent", "GJC Forge")), leftCol),
|
|
253
|
+
this.#centerText(this.#loadingLine(), leftCol),
|
|
254
|
+
this.#centerText(modelPill, leftCol),
|
|
255
|
+
]
|
|
256
|
+
: leftLines;
|
|
257
|
+
const singleBlock = this.#fitBlock([...compactLeftLines, separator, ...rightLines], bodyRows, "top");
|
|
258
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
259
|
+
lines.push(v + this.#fitToWidth(singleBlock[i] ?? "", leftCol) + v);
|
|
260
|
+
}
|
|
229
261
|
lines.push(bl + h.repeat(leftCol) + br);
|
|
230
262
|
}
|
|
231
263
|
|
|
@@ -251,6 +283,110 @@ export class WelcomeComponent implements Component {
|
|
|
251
283
|
}
|
|
252
284
|
return str + padding(width - visLen);
|
|
253
285
|
}
|
|
286
|
+
#targetRows(termWidth: number): number | undefined {
|
|
287
|
+
const viewportRows = this.options.getViewportRows?.();
|
|
288
|
+
if (typeof viewportRows !== "number" || !Number.isFinite(viewportRows) || viewportRows <= 0) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
const reservedRows = Math.max(0, Math.floor(this.options.getReservedBottomRows?.(termWidth) ?? 0));
|
|
292
|
+
return Math.max(0, Math.floor(viewportRows) - reservedRows);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
#loadingLine(): string {
|
|
296
|
+
const frames = theme.spinnerFrames;
|
|
297
|
+
const elapsed = this.#animStart == null ? 0 : performance.now() - this.#animStart;
|
|
298
|
+
const frame = frames.length > 0 ? (frames[Math.floor(elapsed / 100) % frames.length] ?? "*") : "*";
|
|
299
|
+
const label = this.#animStart == null ? "ready" : "warming workspace";
|
|
300
|
+
return `${theme.fg("warning", frame)} ${theme.fg("muted", label)}`;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
#fitBlock(lines: string[], rows: number, align: "top" | "center"): string[] {
|
|
304
|
+
if (rows <= 0) return [];
|
|
305
|
+
const clipped =
|
|
306
|
+
lines.length > rows
|
|
307
|
+
? rows === 1
|
|
308
|
+
? [theme.fg("dim", " …")]
|
|
309
|
+
: [...lines.slice(0, rows - 1), theme.fg("dim", " …")]
|
|
310
|
+
: lines;
|
|
311
|
+
const missingRows = rows - clipped.length;
|
|
312
|
+
if (missingRows <= 0) return clipped;
|
|
313
|
+
const topPad = align === "center" ? Math.floor(missingRows / 2) : 0;
|
|
314
|
+
const bottomPad = missingRows - topPad;
|
|
315
|
+
return [...Array.from({ length: topPad }, () => ""), ...clipped, ...Array.from({ length: bottomPad }, () => "")];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
#whatsNewLines(width: number, maxItems: number): string[] {
|
|
319
|
+
const changelog = this.options.changelogMarkdown?.trim();
|
|
320
|
+
if (!changelog) {
|
|
321
|
+
return [` ${theme.fg("dim", "Ready for your next prompt")}`];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const version = this.#latestChangelogVersion(changelog);
|
|
325
|
+
if (this.options.collapseChangelog) {
|
|
326
|
+
return [
|
|
327
|
+
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
328
|
+
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const itemLimit = Math.max(1, Math.floor(maxItems));
|
|
333
|
+
const items = this.#changelogItems(changelog);
|
|
334
|
+
if (items.length === 0) {
|
|
335
|
+
return [
|
|
336
|
+
` ${theme.fg("muted", `Updated to v${version}`)}`,
|
|
337
|
+
` ${theme.fg("dim", `Use ${theme.bold("/changelog")} for details`)}`,
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const prefix = ` ${theme.md.bullet} `;
|
|
342
|
+
const textWidth = Math.max(1, width - visibleWidth(prefix));
|
|
343
|
+
const visibleItems = items.slice(0, itemLimit).map(item => {
|
|
344
|
+
const text = visibleWidth(item) > textWidth ? truncateToWidth(item, textWidth) : item;
|
|
345
|
+
return `${theme.fg("dim", prefix)}${theme.fg("muted", text)}`;
|
|
346
|
+
});
|
|
347
|
+
if (items.length > visibleItems.length) {
|
|
348
|
+
visibleItems.push(` ${theme.fg("dim", `… ${theme.bold("/changelog")} for full notes`)}`);
|
|
349
|
+
}
|
|
350
|
+
return visibleItems;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
#latestChangelogVersion(markdown: string): string {
|
|
354
|
+
const versionMatch = markdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
|
355
|
+
return versionMatch?.[1] ?? this.version;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
#changelogItems(markdown: string): string[] {
|
|
359
|
+
const items: string[] = [];
|
|
360
|
+
let inFence = false;
|
|
361
|
+
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
362
|
+
const line = rawLine.trim();
|
|
363
|
+
if (line.startsWith("```")) {
|
|
364
|
+
inFence = !inFence;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (inFence || !line || /^#{1,6}\s+/.test(line) || /^-{3,}$/.test(line)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const withoutBullet = line
|
|
371
|
+
.replace(/^[-*]\s+/, "")
|
|
372
|
+
.replace(/^\d+\.\s+/, "")
|
|
373
|
+
.replace(/^>\s*/, "");
|
|
374
|
+
const cleaned = this.#stripMarkdown(withoutBullet);
|
|
375
|
+
if (cleaned) items.push(cleaned);
|
|
376
|
+
}
|
|
377
|
+
return items;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#stripMarkdown(text: string): string {
|
|
381
|
+
return text
|
|
382
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
383
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
|
|
384
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
385
|
+
.replace(/__([^_]+)__/g, "$1")
|
|
386
|
+
.replace(/\*([^*]+)\*/g, "$1")
|
|
387
|
+
.replace(/[_~]/g, "")
|
|
388
|
+
.trim();
|
|
389
|
+
}
|
|
254
390
|
|
|
255
391
|
#pill(icon: string, text: string, color: ThemeColor): string {
|
|
256
392
|
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, "GJC 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);
|
|
@@ -138,6 +138,7 @@ export class EventController {
|
|
|
138
138
|
this.ctx.editor.onEscape = this.ctx.retryEscapeHandler;
|
|
139
139
|
this.ctx.retryEscapeHandler = undefined;
|
|
140
140
|
}
|
|
141
|
+
this.ctx.retryEscapePrimed = false;
|
|
141
142
|
if (this.ctx.retryLoader) {
|
|
142
143
|
this.ctx.retryLoader.stop();
|
|
143
144
|
this.ctx.retryLoader = undefined;
|
|
@@ -241,6 +242,7 @@ export class EventController {
|
|
|
241
242
|
this.ctx.retryLoader = undefined;
|
|
242
243
|
this.ctx.statusContainer.clear();
|
|
243
244
|
}
|
|
245
|
+
this.ctx.retryEscapePrimed = false;
|
|
244
246
|
this.#cancelIdleCompaction();
|
|
245
247
|
this.ctx.updateEditorBorderColor();
|
|
246
248
|
this.ctx.ensureLoadingAnimation();
|
|
@@ -768,6 +770,7 @@ export class EventController {
|
|
|
768
770
|
if (!this.ctx.retryEscapeHandler) {
|
|
769
771
|
this.ctx.retryEscapeHandler = this.ctx.editor.onEscape;
|
|
770
772
|
}
|
|
773
|
+
this.ctx.retryEscapePrimed = false;
|
|
771
774
|
let escPressed = false;
|
|
772
775
|
this.ctx.editor.onEscape = () => {
|
|
773
776
|
if (!escPressed) {
|
|
@@ -818,6 +821,7 @@ export class EventController {
|
|
|
818
821
|
this.ctx.retryLoader = undefined;
|
|
819
822
|
this.ctx.statusContainer.clear();
|
|
820
823
|
}
|
|
824
|
+
this.ctx.retryEscapePrimed = false;
|
|
821
825
|
if (!event.success) {
|
|
822
826
|
this.ctx.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
823
827
|
}
|