@gajae-code/coding-agent 0.7.7 → 0.7.9

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.
Files changed (96) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/README.md +14 -0
  3. package/dist/types/cli/local-provider-smoke.d.ts +59 -0
  4. package/dist/types/cli/notify-cli.d.ts +1 -1
  5. package/dist/types/cli/update-cli.d.ts +24 -1
  6. package/dist/types/commands/local-provider.d.ts +34 -0
  7. package/dist/types/config/keybindings.d.ts +1 -1
  8. package/dist/types/config/model-profile-activation.d.ts +19 -2
  9. package/dist/types/config/model-profiles.d.ts +1 -0
  10. package/dist/types/config/model-registry.d.ts +9 -0
  11. package/dist/types/config/models-config-schema.d.ts +5 -0
  12. package/dist/types/config/settings-schema.d.ts +45 -0
  13. package/dist/types/config/settings.d.ts +7 -0
  14. package/dist/types/coordinator-mcp/server.d.ts +8 -0
  15. package/dist/types/gjc-runtime/launch-tmux.d.ts +14 -5
  16. package/dist/types/gjc-runtime/team-runtime.d.ts +1 -1
  17. package/dist/types/gjc-runtime/tmux-common.d.ts +12 -0
  18. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +3 -1
  19. package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
  20. package/dist/types/modes/components/hook-input.d.ts +1 -0
  21. package/dist/types/modes/components/model-selector.d.ts +11 -0
  22. package/dist/types/modes/components/settings-selector.d.ts +4 -1
  23. package/dist/types/modes/components/status-line/types.d.ts +5 -7
  24. package/dist/types/modes/components/status-line.d.ts +7 -3
  25. package/dist/types/modes/controllers/extension-ui-controller.d.ts +3 -1
  26. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  27. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  28. package/dist/types/modes/interactive-mode.d.ts +3 -1
  29. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
  30. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +21 -1
  31. package/dist/types/modes/tmux-scroll.d.ts +18 -0
  32. package/dist/types/modes/types.d.ts +3 -1
  33. package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
  34. package/dist/types/notifications/daemon-paths.d.ts +9 -0
  35. package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
  36. package/dist/types/notifications/telegram-daemon.d.ts +1 -10
  37. package/dist/types/tools/ask.d.ts +24 -0
  38. package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
  39. package/package.json +7 -7
  40. package/src/cli/local-provider-smoke.ts +632 -0
  41. package/src/cli/notify-cli.ts +5 -3
  42. package/src/cli/update-cli.ts +155 -13
  43. package/src/cli.ts +19 -0
  44. package/src/commands/launch.ts +46 -1
  45. package/src/commands/local-provider.ts +62 -0
  46. package/src/commands/notify.ts +1 -1
  47. package/src/config/keybindings.ts +1 -1
  48. package/src/config/model-profile-activation.ts +92 -11
  49. package/src/config/model-profiles.ts +15 -2
  50. package/src/config/model-registry.ts +136 -6
  51. package/src/config/models-config-schema.ts +9 -0
  52. package/src/config/settings-schema.ts +48 -0
  53. package/src/config/settings.ts +11 -0
  54. package/src/coordinator-mcp/server.ts +280 -36
  55. package/src/deep-interview/render-middleware.ts +1 -0
  56. package/src/defaults/gjc/skills/deep-interview/SKILL.md +14 -6
  57. package/src/defaults/gjc/skills/ralplan/SKILL.md +14 -7
  58. package/src/gjc-runtime/deep-interview-runtime.ts +1 -1
  59. package/src/gjc-runtime/launch-tmux.ts +321 -38
  60. package/src/gjc-runtime/launch-worktree.ts +16 -2
  61. package/src/gjc-runtime/team-runtime.ts +87 -7
  62. package/src/gjc-runtime/tmux-common.ts +16 -0
  63. package/src/gjc-runtime/tmux-sessions.ts +13 -9
  64. package/src/gjc-runtime/ultragoal-guard.ts +11 -5
  65. package/src/internal-urls/docs-index.generated.ts +10 -9
  66. package/src/modes/components/agent-dashboard.ts +2 -1
  67. package/src/modes/components/custom-model-preset-wizard.ts +31 -192
  68. package/src/modes/components/hook-editor.ts +7 -4
  69. package/src/modes/components/hook-input.ts +4 -0
  70. package/src/modes/components/model-selector.ts +260 -12
  71. package/src/modes/components/session-observer-overlay.ts +3 -3
  72. package/src/modes/components/settings-selector.ts +506 -5
  73. package/src/modes/components/status-line/segments.ts +16 -19
  74. package/src/modes/components/status-line/types.ts +6 -2
  75. package/src/modes/components/status-line.ts +70 -35
  76. package/src/modes/controllers/event-controller.ts +98 -4
  77. package/src/modes/controllers/extension-ui-controller.ts +5 -0
  78. package/src/modes/controllers/input-controller.ts +15 -0
  79. package/src/modes/controllers/selector-controller.ts +142 -11
  80. package/src/modes/interactive-mode.ts +7 -2
  81. package/src/modes/prompt-action-autocomplete.ts +8 -0
  82. package/src/modes/shared/agent-wire/deep-interview-gate.ts +11 -4
  83. package/src/modes/tmux-scroll.ts +92 -0
  84. package/src/modes/types.ts +6 -1
  85. package/src/modes/utils/terminal-bell.ts +48 -0
  86. package/src/notifications/daemon-paths.ts +22 -0
  87. package/src/notifications/telegram-daemon-cli.ts +125 -13
  88. package/src/notifications/telegram-daemon.ts +6 -21
  89. package/src/prompts/system/system-prompt.md +5 -0
  90. package/src/sdk.ts +10 -1
  91. package/src/session/agent-session.ts +84 -6
  92. package/src/slash-commands/builtin-registry.ts +106 -13
  93. package/src/tools/ask.ts +11 -1
  94. package/src/tools/eval.ts +2 -2
  95. package/src/tools/render-utils.ts +2 -0
  96. package/src/tools/ultragoal-ask-guard.ts +39 -5
@@ -25,6 +25,7 @@ import {
25
25
  matchesKey,
26
26
  padding,
27
27
  replaceTabs,
28
+ resolveTerminalColumns,
28
29
  Spacer,
29
30
  Text,
30
31
  truncateToWidth,
@@ -870,7 +871,7 @@ export class AgentDashboard extends Container {
870
871
  }
871
872
 
872
873
  #uiWidth(): number {
873
- return Math.max(40, process.stdout.columns ?? 100);
874
+ return Math.max(40, resolveTerminalColumns());
874
875
  }
875
876
 
876
877
  /** Rebuild layout and request a TUI render pass (for use after async state changes). */
@@ -1,20 +1,9 @@
1
1
  import { Container, Input, matchesKey, Spacer, Text, TruncatedText } from "@gajae-code/tui";
2
+ import { MODEL_PROFILE_NAME_PATTERN } from "../../config/model-registry";
2
3
  import type { ModelProfileConfig } from "../../config/models-config-schema";
3
4
  import { theme } from "../theme/theme";
4
5
  import { matchesAppInterrupt } from "../utils/keybinding-matchers";
5
6
  import { DynamicBorder } from "./dynamic-border";
6
-
7
- const PROFILE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
8
-
9
- type WizardStep = "name" | "display-name" | "provider" | "model" | "confirm";
10
-
11
- interface WizardState {
12
- name: string;
13
- displayName: string;
14
- provider: string;
15
- model: string;
16
- }
17
-
18
7
  export interface CustomModelPresetWizardSubmit {
19
8
  name: string;
20
9
  profile: ModelProfileConfig;
@@ -23,25 +12,21 @@ export interface CustomModelPresetWizardSubmit {
23
12
  export class CustomModelPresetWizardComponent extends Container {
24
13
  #contentContainer: Container;
25
14
  #input: Input | null = null;
26
- #step: WizardStep = "name";
27
- #selectedIndex = 0;
28
15
  #lastError: string | null = null;
29
- #state: WizardState = {
30
- name: "",
31
- displayName: "",
32
- provider: "",
33
- model: "",
34
- };
16
+ #name = "";
17
+ #snapshot: ModelProfileConfig;
35
18
  #onSubmit: (input: CustomModelPresetWizardSubmit) => void;
36
19
  #onCancel: () => void;
37
20
  #onRender: () => void;
38
21
 
39
22
  constructor(
23
+ snapshot: ModelProfileConfig,
40
24
  onSubmit: (input: CustomModelPresetWizardSubmit) => void,
41
25
  onCancel: () => void,
42
26
  onRender: () => void = () => {},
43
27
  ) {
44
28
  super();
29
+ this.#snapshot = snapshot;
45
30
  this.#onSubmit = onSubmit;
46
31
  this.#onCancel = onCancel;
47
32
  this.#onRender = onRender;
@@ -51,7 +36,7 @@ export class CustomModelPresetWizardComponent extends Container {
51
36
  this.addChild(new TruncatedText(theme.bold("Create custom model preset")));
52
37
  this.addChild(
53
38
  new TruncatedText(
54
- theme.fg("muted", " Save provider/model as a selectable profile. Secrets are not requested."),
39
+ theme.fg("muted", " Save the current default and explicit role models as a selectable profile."),
55
40
  0,
56
41
  0,
57
42
  ),
@@ -72,222 +57,76 @@ export class CustomModelPresetWizardComponent extends Container {
72
57
 
73
58
  handleInput(keyData: string): void {
74
59
  if (matchesAppInterrupt(keyData)) {
75
- if (this.#step === "name") {
76
- this.#onCancel();
77
- return;
78
- }
79
- this.#goBack();
60
+ this.#onCancel();
80
61
  return;
81
62
  }
82
63
 
83
64
  if (this.#input) {
84
65
  if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
85
- this.#saveInputAndProceed();
66
+ this.#saveInputAndSubmit();
86
67
  return;
87
68
  }
88
69
  this.#input.handleInput(keyData);
89
- return;
90
- }
91
-
92
- if (matchesKey(keyData, "up")) {
93
- this.#moveSelection(-1);
94
- return;
95
- }
96
- if (matchesKey(keyData, "down")) {
97
- this.#moveSelection(1);
98
- return;
99
- }
100
- if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
101
- this.#selectCurrentOption();
102
70
  }
103
71
  }
104
72
 
105
73
  #renderStep(): void {
106
74
  this.#contentContainer.clear();
107
75
  this.#input = null;
108
- switch (this.#step) {
109
- case "name":
110
- this.#renderInputStep(
111
- "Step 1: Preset id",
112
- "Enter a unique preset id:",
113
- this.#state.name,
114
- "e.g. my-fast-coder",
115
- );
116
- break;
117
- case "display-name":
118
- this.#renderInputStep(
119
- "Step 2: Display name",
120
- "Enter a display name:",
121
- this.#state.displayName,
122
- "e.g. My Fast Coder",
123
- );
124
- break;
125
- case "provider":
126
- this.#renderInputStep(
127
- "Step 3: Provider",
128
- "Enter the provider id:",
129
- this.#state.provider,
130
- "e.g. openai-codex, anthropic, my-oai",
131
- );
132
- break;
133
- case "model":
134
- this.#renderInputStep(
135
- "Step 4: Model",
136
- "Enter the model id or provider/model selector:",
137
- this.#state.model,
138
- "e.g. gpt-5.5:medium or my-oai/gpt-example:low",
139
- );
140
- break;
141
- case "confirm":
142
- this.#renderConfirmStep();
143
- break;
144
- }
145
- }
146
-
147
- #renderInputStep(title: string, prompt: string, value: string, hint: string): void {
148
- this.#contentContainer.addChild(new Text(theme.fg("accent", title)));
76
+ this.#contentContainer.addChild(new Text(theme.fg("accent", "Preset id")));
149
77
  this.#contentContainer.addChild(new Spacer(1));
150
78
  if (this.#lastError) {
151
79
  this.#contentContainer.addChild(new Text(theme.fg("error", this.#lastError), 0, 0));
152
80
  this.#contentContainer.addChild(new Spacer(1));
153
81
  }
154
- this.#contentContainer.addChild(new Text(prompt, 0, 0));
82
+ this.#contentContainer.addChild(new Text("Enter a unique preset id:", 0, 0));
155
83
  this.#contentContainer.addChild(new Spacer(1));
156
84
  this.#input = new Input();
157
- this.#input.setValue(value);
85
+ this.#input.setValue(this.#name);
158
86
  this.#contentContainer.addChild(this.#input);
159
87
  this.#contentContainer.addChild(new Spacer(1));
160
- this.#addHelp(hint);
161
- this.#addHelp("[Enter to continue, Esc to go back]");
88
+ this.#addSnapshotPreview();
89
+ this.#addHelp("e.g. my-fast-coder");
90
+ this.#addHelp("[Enter to create, Esc to cancel]");
162
91
  }
163
92
 
164
- #renderConfirmStep(): void {
165
- this.#contentContainer.addChild(new Text(theme.fg("accent", "Confirm custom preset")));
166
- this.#contentContainer.addChild(new Spacer(1));
167
- if (this.#lastError) {
168
- this.#contentContainer.addChild(new Text(theme.fg("error", this.#lastError), 0, 0));
169
- this.#contentContainer.addChild(new Spacer(1));
93
+ #addSnapshotPreview(): void {
94
+ this.#contentContainer.addChild(new Text(theme.fg("muted", "Snapshot:"), 0, 0));
95
+ for (const [role, selector] of Object.entries(this.#snapshot.model_mapping)) {
96
+ this.#contentContainer.addChild(new Text(` ${role}: ${selector}`, 0, 0));
170
97
  }
171
- this.#contentContainer.addChild(new Text(`Preset id: ${this.#state.name}`, 0, 0));
172
- this.#contentContainer.addChild(new Text(`Display name: ${this.#state.displayName}`, 0, 0));
173
- this.#contentContainer.addChild(new Text(`Provider: ${this.#state.provider}`, 0, 0));
174
- this.#contentContainer.addChild(new Text(`Default model: ${this.#selector()}`, 0, 0));
98
+ this.#contentContainer.addChild(new Text(` providers: ${this.#snapshot.required_providers.join(", ")}`, 0, 0));
175
99
  this.#contentContainer.addChild(new Spacer(1));
176
- this.#addOption(0, "Create preset");
177
- this.#addOption(1, "Go back");
178
- this.#addHelp("[↑↓ to navigate, Enter to select, Esc to go back]");
179
- }
180
-
181
- #addOption(index: number, label: string): void {
182
- const selected = index === this.#selectedIndex;
183
- const prefix = selected ? theme.fg("accent", `${theme.nav.cursor} `) : " ";
184
- this.#contentContainer.addChild(new Text(`${prefix}${selected ? theme.fg("accent", label) : label}`, 0, 0));
185
100
  }
186
101
 
187
102
  #addHelp(text: string): void {
188
103
  this.#contentContainer.addChild(new Text(theme.fg("muted", text), 0, 0));
189
104
  }
190
105
 
191
- #saveInputAndProceed(): void {
106
+ #saveInputAndSubmit(): void {
192
107
  const value = this.#input?.getValue().trim() ?? "";
193
108
  if (!value) {
194
- this.#lastError = this.#emptyFieldMessage();
109
+ this.#lastError = "Preset id is required.";
195
110
  this.#renderStep();
196
111
  this.#onRender();
197
112
  return;
198
113
  }
199
- const validationError = this.#validateCurrentInput(value);
200
- if (validationError) {
201
- this.#lastError = validationError;
114
+ if (!MODEL_PROFILE_NAME_PATTERN.test(value)) {
115
+ this.#lastError = "Preset id must use lowercase letters, numbers, dots, underscores, or hyphens.";
202
116
  this.#renderStep();
203
117
  this.#onRender();
204
118
  return;
205
119
  }
120
+ this.#name = value;
206
121
  this.#lastError = null;
207
- if (this.#step === "name") {
208
- this.#state.name = value;
209
- this.#step = "display-name";
210
- } else if (this.#step === "display-name") {
211
- this.#state.displayName = value;
212
- this.#step = "provider";
213
- } else if (this.#step === "provider") {
214
- this.#state.provider = value;
215
- this.#step = "model";
216
- } else if (this.#step === "model") {
217
- this.#state.model = value;
218
- this.#step = "confirm";
219
- this.#selectedIndex = 0;
220
- }
221
- this.#renderStep();
222
- this.#onRender();
223
- }
224
-
225
- #emptyFieldMessage(): string {
226
- switch (this.#step) {
227
- case "name":
228
- return "Preset id is required.";
229
- case "display-name":
230
- return "Display name is required.";
231
- case "provider":
232
- return "Provider is required.";
233
- case "model":
234
- return "Model is required.";
235
- case "confirm":
236
- return "Value is required.";
237
- }
238
- }
239
-
240
- #validateCurrentInput(value: string): string | undefined {
241
- if (this.#step === "name" && !PROFILE_NAME_PATTERN.test(value)) {
242
- return "Preset id must use lowercase letters, numbers, dots, underscores, or hyphens.";
243
- }
244
- if (this.#step === "provider" && !PROFILE_NAME_PATTERN.test(value)) {
245
- return "Provider id must use lowercase letters, numbers, dots, underscores, or hyphens.";
246
- }
247
- if (this.#step === "model" && value.includes(",")) {
248
- return "Model must be one selector, not a comma-separated list.";
249
- }
250
- return undefined;
251
- }
252
-
253
- #selectCurrentOption(): void {
254
- if (this.#step !== "confirm") return;
255
- if (this.#selectedIndex === 0) {
256
- this.#onSubmit(this.#buildInput());
257
- return;
258
- }
259
- this.#goBack();
260
- }
261
-
262
- #buildInput(): CustomModelPresetWizardSubmit {
263
- return {
264
- name: this.#state.name,
122
+ this.#onSubmit({
123
+ name: value,
265
124
  profile: {
266
- required_providers: [this.#state.provider],
267
- display_name: this.#state.displayName,
268
- model_mapping: { default: this.#selector() },
125
+ ...this.#snapshot,
126
+ display_name: value,
127
+ model_mapping: { ...this.#snapshot.model_mapping },
128
+ required_providers: [...this.#snapshot.required_providers],
269
129
  },
270
- };
271
- }
272
-
273
- #selector(): string {
274
- return this.#state.model.includes("/") ? this.#state.model : `${this.#state.provider}/${this.#state.model}`;
275
- }
276
-
277
- #moveSelection(delta: number): void {
278
- this.#selectedIndex = (this.#selectedIndex + delta + 2) % 2;
279
- this.#renderStep();
280
- this.#onRender();
281
- }
282
-
283
- #goBack(): void {
284
- if (this.#step === "display-name") this.#step = "name";
285
- else if (this.#step === "provider") this.#step = "display-name";
286
- else if (this.#step === "model") this.#step = "provider";
287
- else if (this.#step === "confirm") this.#step = "model";
288
- this.#selectedIndex = 0;
289
- this.#lastError = null;
290
- this.#renderStep();
291
- this.#onRender();
130
+ });
292
131
  }
293
132
  }
@@ -96,14 +96,17 @@ export class HookEditorComponent extends Container {
96
96
  return;
97
97
  }
98
98
 
99
- // Submit on plain Enter encodings. On Windows, raw LF is reserved for terminal
100
- // newline mappings (Shift+Enter/Ctrl+J/Ctrl+Enter); plain Enter reports CR.
101
- if (!isWindowsRawLfNewlineInput(keyData) && (matchesKey(keyData, "enter") || matchesKey(keyData, "return"))) {
99
+ // Submit on Enter and Ctrl+Enter encodings. On Windows, raw LF is reserved for
100
+ // terminal newline mappings (Shift+Enter/Ctrl+J); plain Enter reports CR.
101
+ if (
102
+ !isWindowsRawLfNewlineInput(keyData) &&
103
+ (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || matchesKey(keyData, "ctrl+enter"))
104
+ ) {
102
105
  this.#onSubmitCallback(this.#editor.getText());
103
106
  return;
104
107
  }
105
108
 
106
- // Let Editor handle modified newline-producing variants (Shift+Enter, Ctrl+Enter, Alt+Enter, etc.)
109
+ // Let Editor handle newline-producing variants (Shift+Enter, Ctrl+J, Alt+Enter, etc.)
107
110
  this.#editor.handleInput(keyData);
108
111
  }
109
112
 
@@ -8,6 +8,7 @@ import { CountdownTimer } from "./countdown-timer";
8
8
  import { DynamicBorder } from "./dynamic-border";
9
9
 
10
10
  export interface HookInputOptions {
11
+ initialValue?: string;
11
12
  tui?: TUI;
12
13
  timeout?: number;
13
14
  onTimeout?: () => void;
@@ -54,6 +55,9 @@ export class HookInputComponent extends Container {
54
55
  }
55
56
 
56
57
  this.#input = new Input();
58
+ if (opts?.initialValue) {
59
+ this.#input.setValue(opts.initialValue);
60
+ }
57
61
  this.addChild(this.#input);
58
62
  this.addChild(new Spacer(1));
59
63
  this.addChild(new Text(theme.fg("dim", "enter submit esc cancel"), 1, 0));