@duckmind/dm-darwin-arm64 0.13.6 → 0.13.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.
Files changed (78) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +26 -2
  3. package/extensions/dm-phone/README.md +23 -0
  4. package/extensions/dm-phone/index.ts +12 -0
  5. package/extensions/dm-phone/node_modules/.package-lock.json +29 -0
  6. package/extensions/dm-phone/node_modules/ws/LICENSE +20 -0
  7. package/extensions/dm-phone/node_modules/ws/README.md +548 -0
  8. package/extensions/dm-phone/node_modules/ws/browser.js +8 -0
  9. package/extensions/dm-phone/node_modules/ws/index.js +22 -0
  10. package/extensions/dm-phone/node_modules/ws/lib/buffer-util.js +131 -0
  11. package/extensions/dm-phone/node_modules/ws/lib/constants.js +19 -0
  12. package/extensions/dm-phone/node_modules/ws/lib/event-target.js +292 -0
  13. package/extensions/dm-phone/node_modules/ws/lib/extension.js +203 -0
  14. package/extensions/dm-phone/node_modules/ws/lib/limiter.js +55 -0
  15. package/extensions/dm-phone/node_modules/ws/lib/permessage-deflate.js +528 -0
  16. package/extensions/dm-phone/node_modules/ws/lib/receiver.js +706 -0
  17. package/extensions/dm-phone/node_modules/ws/lib/sender.js +602 -0
  18. package/extensions/dm-phone/node_modules/ws/lib/stream.js +161 -0
  19. package/extensions/dm-phone/node_modules/ws/lib/subprotocol.js +62 -0
  20. package/extensions/dm-phone/node_modules/ws/lib/validation.js +152 -0
  21. package/extensions/dm-phone/node_modules/ws/lib/websocket-server.js +554 -0
  22. package/extensions/dm-phone/node_modules/ws/lib/websocket.js +1393 -0
  23. package/extensions/dm-phone/node_modules/ws/package.json +70 -0
  24. package/extensions/dm-phone/node_modules/ws/wrapper.mjs +21 -0
  25. package/extensions/dm-phone/package-lock.json +66 -0
  26. package/extensions/dm-phone/package.json +35 -0
  27. package/extensions/dm-phone/phone-session-pool.ts +8 -0
  28. package/extensions/dm-phone/public/app/attachments.js +233 -0
  29. package/extensions/dm-phone/public/app/autocomplete-controller.js +81 -0
  30. package/extensions/dm-phone/public/app/autocomplete.js +135 -0
  31. package/extensions/dm-phone/public/app/bindings.js +178 -0
  32. package/extensions/dm-phone/public/app/command-catalog.js +76 -0
  33. package/extensions/dm-phone/public/app/commands.js +370 -0
  34. package/extensions/dm-phone/public/app/constants.js +60 -0
  35. package/extensions/dm-phone/public/app/formatters.js +131 -0
  36. package/extensions/dm-phone/public/app/handlers.js +442 -0
  37. package/extensions/dm-phone/public/app/main.js +6 -0
  38. package/extensions/dm-phone/public/app/markdown.js +105 -0
  39. package/extensions/dm-phone/public/app/messages.js +418 -0
  40. package/extensions/dm-phone/public/app/sheet-actions.js +113 -0
  41. package/extensions/dm-phone/public/app/sheet-navigation.js +19 -0
  42. package/extensions/dm-phone/public/app/sheets-view.js +272 -0
  43. package/extensions/dm-phone/public/app/state.js +95 -0
  44. package/extensions/dm-phone/public/app/tool-rendering.js +562 -0
  45. package/extensions/dm-phone/public/app/transport.js +176 -0
  46. package/extensions/dm-phone/public/app/ui.js +409 -0
  47. package/extensions/dm-phone/public/app.js +1 -0
  48. package/extensions/dm-phone/public/icon.svg +15 -0
  49. package/extensions/dm-phone/public/index.html +147 -0
  50. package/extensions/dm-phone/public/manifest.webmanifest +17 -0
  51. package/extensions/dm-phone/public/styles.css +1139 -0
  52. package/extensions/dm-phone/public/sw.js +78 -0
  53. package/extensions/dm-phone/src/extension/phone-args.ts +121 -0
  54. package/extensions/dm-phone/src/extension/phone-paths.ts +250 -0
  55. package/extensions/dm-phone/src/extension/phone-quota.ts +188 -0
  56. package/extensions/dm-phone/src/extension/phone-runtime.ts +154 -0
  57. package/extensions/dm-phone/src/extension/phone-server-runtime.ts +1217 -0
  58. package/extensions/dm-phone/src/extension/phone-sessions.ts +139 -0
  59. package/extensions/dm-phone/src/extension/phone-static.ts +30 -0
  60. package/extensions/dm-phone/src/extension/phone-tailscale.ts +148 -0
  61. package/extensions/dm-phone/src/extension/phone-theme.ts +85 -0
  62. package/extensions/dm-phone/src/extension/register-phone-child-extension.ts +112 -0
  63. package/extensions/dm-phone/src/extension/register-phone-extension.ts +106 -0
  64. package/extensions/dm-phone/src/extension/types.ts +73 -0
  65. package/extensions/dm-phone/src/session-pool/parent-session-worker.ts +881 -0
  66. package/extensions/dm-phone/src/session-pool/session-pool.ts +470 -0
  67. package/extensions/dm-phone/src/session-pool/session-worker.ts +734 -0
  68. package/extensions/dm-phone/src/session-pool/types.ts +105 -0
  69. package/extensions/dm-phone/src/session-pool/utils.ts +23 -0
  70. package/extensions/dm-subagents/agent-management.ts +15 -6
  71. package/extensions/dm-subagents/agent-manager-detail.ts +12 -2
  72. package/extensions/dm-subagents/agent-manager-edit.ts +75 -23
  73. package/extensions/dm-subagents/agent-manager-list.ts +9 -2
  74. package/extensions/dm-subagents/agent-manager.ts +199 -11
  75. package/extensions/dm-subagents/agents.ts +315 -20
  76. package/extensions/dm-ultrathink/README.md +5 -0
  77. package/extensions/dm-ultrathink/src/naming.ts +75 -3
  78. package/package.json +1 -1
@@ -3,7 +3,15 @@ import * as path from "node:path";
3
3
  import type { Theme } from "@mariozechner/pi-coding-agent";
4
4
  import type { Component, TUI } from "@mariozechner/pi-tui";
5
5
  import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
6
- import type { AgentConfig, ChainConfig } from "./agents.ts";
6
+ import {
7
+ buildBuiltinOverrideConfig,
8
+ discoverAgentsAll,
9
+ removeBuiltinAgentOverride,
10
+ saveBuiltinAgentOverride,
11
+ type AgentConfig,
12
+ type BuiltinAgentOverrideBase,
13
+ type ChainConfig,
14
+ } from "./agents.ts";
7
15
  import { serializeAgent } from "./agent-serializer.ts";
8
16
  import { TEMPLATE_ITEMS, type AgentTemplate, type TemplateItem } from "./agent-templates.ts";
9
17
  import { parseChain, serializeChain } from "./chain-serializer.ts";
@@ -11,7 +19,7 @@ import { renderList, handleListInput, type ListAgent, type ListState, type ListA
11
19
  import { createParallelState, handleParallelInput, renderParallel, formatParallelTitle, type ParallelState, type AgentOption } from "./agent-manager-parallel.ts";
12
20
  import { renderDetail, handleDetailInput, renderTaskInput, type DetailState, type DetailAction } from "./agent-manager-detail.ts";
13
21
  import { renderChainDetail, handleChainDetailInput, type ChainDetailAction, type ChainDetailState } from "./agent-manager-chain-detail.ts";
14
- import { createEditState, handleEditInput, renderEdit, type EditScreen, type EditState, type ModelInfo, type SkillInfo } from "./agent-manager-edit.ts";
22
+ import { createEditState, handleEditInput, renderEdit, type EditField, type EditScreen, type EditState, type ModelInfo, type SkillInfo } from "./agent-manager-edit.ts";
15
23
  import { createEditorState, ensureCursorVisible, getCursorDisplayPos, handleEditorInput, renderEditor, wrapText } from "./text-editor.ts";
16
24
  import type { TextEditorState } from "./text-editor.ts";
17
25
  import { loadRunsForAgent } from "./run-history.ts";
@@ -24,14 +32,39 @@ export type ManagerResult =
24
32
  | { action: "launch-chain"; chain: ChainConfig; task: string; skipClarify?: boolean }
25
33
  | undefined;
26
34
 
27
- export interface AgentData { builtin: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[]; chains: ChainConfig[]; userDir: string; projectDir: string | null; cwd: string; }
28
- type ManagerScreen = "list" | "detail" | "chain-detail" | "edit" | "edit-field" | "edit-prompt" | "task-input" | "confirm-delete" | "name-input" | "chain-edit" | "template-select" | "parallel-builder";
35
+ export interface AgentData { builtin: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[]; chains: ChainConfig[]; userDir: string; projectDir: string | null; userSettingsPath: string; projectSettingsPath: string | null; cwd: string; }
36
+ type ManagerScreen = "list" | "detail" | "chain-detail" | "edit" | "edit-field" | "edit-prompt" | "task-input" | "confirm-delete" | "name-input" | "chain-edit" | "template-select" | "parallel-builder" | "override-scope";
29
37
  interface AgentEntry { id: string; kind: "agent"; config: AgentConfig; isNew: boolean; }
30
38
  interface ChainEntry { id: string; kind: "chain"; config: ChainConfig; }
31
39
  interface NameInputState { mode: "new-agent" | "clone-agent" | "clone-chain" | "new-chain"; editor: TextEditorState; scope: "user" | "project"; allowProject: boolean; sourceId?: string; template?: AgentTemplate; error?: string; }
32
40
  interface StatusMessage { text: string; type: "error" | "info"; }
41
+ interface OverrideScopeState { selectedScope: "user" | "project"; allowProject: boolean; }
33
42
 
34
- function cloneConfig(config: AgentConfig): AgentConfig { return { ...config, tools: config.tools ? [...config.tools] : undefined, mcpDirectTools: config.mcpDirectTools ? [...config.mcpDirectTools] : undefined, skills: config.skills ? [...config.skills] : undefined, defaultReads: config.defaultReads ? [...config.defaultReads] : undefined, extraFields: config.extraFields ? { ...config.extraFields } : undefined }; }
43
+ const BUILTIN_OVERRIDE_FIELDS: EditField[] = ["model", "fallbackModels", "thinking", "tools", "skills", "prompt"];
44
+
45
+ function cloneConfig(config: AgentConfig): AgentConfig {
46
+ return {
47
+ ...config,
48
+ tools: config.tools ? [...config.tools] : undefined,
49
+ mcpDirectTools: config.mcpDirectTools ? [...config.mcpDirectTools] : undefined,
50
+ skills: config.skills ? [...config.skills] : undefined,
51
+ fallbackModels: config.fallbackModels ? [...config.fallbackModels] : undefined,
52
+ defaultReads: config.defaultReads ? [...config.defaultReads] : undefined,
53
+ extraFields: config.extraFields ? { ...config.extraFields } : undefined,
54
+ override: config.override
55
+ ? {
56
+ ...config.override,
57
+ base: {
58
+ ...config.override.base,
59
+ fallbackModels: config.override.base.fallbackModels ? [...config.override.base.fallbackModels] : undefined,
60
+ skills: config.override.base.skills ? [...config.override.base.skills] : undefined,
61
+ tools: config.override.base.tools ? [...config.override.base.tools] : undefined,
62
+ mcpDirectTools: config.override.base.mcpDirectTools ? [...config.override.base.mcpDirectTools] : undefined,
63
+ },
64
+ }
65
+ : undefined,
66
+ };
67
+ }
35
68
  function cloneChainConfig(config: ChainConfig): ChainConfig { return { ...config, steps: config.steps.map((step) => ({ ...step, reads: Array.isArray(step.reads) ? [...step.reads] : step.reads, skills: Array.isArray(step.skills) ? [...step.skills] : step.skills })), extraFields: config.extraFields ? { ...config.extraFields } : undefined }; }
36
69
  function slugTemplateName(name: string): string { return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, ""); }
37
70
  function nextSelectableIndex(items: TemplateItem[], current: number, direction: 1 | -1): number { let next = current + direction; while (next >= 0 && next < items.length && items[next]!.type === "separator") next += direction; if (next < 0 || next >= items.length) return current; return next; }
@@ -60,6 +93,8 @@ export class AgentManagerComponent implements Component {
60
93
  private taskBackScreen: ManagerScreen = "list";
61
94
  private templateCursor = 0;
62
95
  private statusMessage?: StatusMessage;
96
+ private overrideScopeState: OverrideScopeState | null = null;
97
+ private builtinOverrideScope: "user" | "project" | null = null;
63
98
  private nextId = 1;
64
99
  private tui: TUI;
65
100
  private theme: Theme;
@@ -86,15 +121,57 @@ export class AgentManagerComponent implements Component {
86
121
 
87
122
  private getAgentEntry(id: string | null): AgentEntry | undefined { if (!id) return undefined; return this.agents.find((entry) => entry.id === id); }
88
123
  private getChainEntry(id: string | null): ChainEntry | undefined { if (!id) return undefined; return this.chains.find((entry) => entry.id === id); }
89
- private listAgents(): ListAgent[] { const a = this.agents.map((entry) => ({ id: entry.id, name: entry.config.name, description: entry.config.description, model: entry.config.model, source: entry.config.source, kind: "agent" as const })); const c = this.chains.map((entry) => ({ id: entry.id, name: entry.config.name, description: entry.config.description, source: entry.config.source, kind: "chain" as const, stepCount: entry.config.steps.length })); return [...a, ...c]; }
124
+ private listAgents(): ListAgent[] { const a = this.agents.map((entry) => ({ id: entry.id, name: entry.config.name, description: entry.config.description, model: entry.config.model, source: entry.config.source, overrideScope: entry.config.override?.scope, kind: "agent" as const })); const c = this.chains.map((entry) => ({ id: entry.id, name: entry.config.name, description: entry.config.description, source: entry.config.source, kind: "chain" as const, stepCount: entry.config.steps.length })); return [...a, ...c]; }
90
125
  private clearStatus(): void { this.statusMessage = undefined; }
91
126
 
127
+ private resolveBuiltinOverrideBase(entry: AgentEntry): BuiltinAgentOverrideBase {
128
+ if (entry.config.override) return entry.config.override.base;
129
+ return {
130
+ model: entry.config.model,
131
+ fallbackModels: entry.config.fallbackModels ? [...entry.config.fallbackModels] : undefined,
132
+ thinking: entry.config.thinking,
133
+ systemPrompt: entry.config.systemPrompt,
134
+ skills: entry.config.skills ? [...entry.config.skills] : undefined,
135
+ tools: entry.config.tools ? [...entry.config.tools] : undefined,
136
+ mcpDirectTools: entry.config.mcpDirectTools ? [...entry.config.mcpDirectTools] : undefined,
137
+ };
138
+ }
139
+
140
+ private refreshAgentData(agentName?: string, chainName?: string): void {
141
+ this.agentData = { ...discoverAgentsAll(this.agentData.cwd), cwd: this.agentData.cwd };
142
+ this.nextId = 1;
143
+ this.loadEntries();
144
+ if (agentName) {
145
+ const entry = this.agents.find((candidate) => candidate.config.name === agentName);
146
+ this.currentAgentId = entry?.id ?? null;
147
+ }
148
+ if (chainName) {
149
+ const entry = this.chains.find((candidate) => candidate.config.name === chainName);
150
+ this.currentChainId = entry?.id ?? null;
151
+ }
152
+ }
153
+
92
154
  private removeAgentEntry(entry: AgentEntry): void { this.agents = this.agents.filter((e) => e.id !== entry.id); this.listState.selected = this.listState.selected.filter((id) => id !== entry.id); }
93
155
  private removeChainEntry(entry: ChainEntry): void { this.chains = this.chains.filter((e) => e.id !== entry.id); }
94
156
 
95
157
  private enterDetail(entry: AgentEntry): void { this.currentAgentId = entry.id; this.detailState = { resolved: true, scrollOffset: 0, recentRuns: loadRunsForAgent(entry.config.name).slice(0, 5) }; this.screen = "detail"; }
96
158
  private enterChainDetail(entry: ChainEntry): void { this.currentChainId = entry.id; this.chainDetailState = { scrollOffset: 0 }; this.screen = "chain-detail"; }
97
- private enterEdit(entry: AgentEntry): void { this.currentAgentId = entry.id; this.editState = createEditState(entry.config, entry.isNew, this.models, this.skills); this.screen = "edit"; }
159
+ private enterEdit(entry: AgentEntry): void { this.currentAgentId = entry.id; this.builtinOverrideScope = null; this.editState = createEditState(entry.config, entry.isNew, this.models, this.skills); this.screen = "edit"; }
160
+ private enterBuiltinOverrideScope(entry: AgentEntry): void {
161
+ this.currentAgentId = entry.id;
162
+ this.overrideScopeState = { selectedScope: this.agentData.projectSettingsPath ? "project" : "user", allowProject: Boolean(this.agentData.projectSettingsPath) };
163
+ this.screen = "override-scope";
164
+ }
165
+ private enterBuiltinOverrideEdit(entry: AgentEntry, scope: "user" | "project"): void {
166
+ this.currentAgentId = entry.id;
167
+ this.builtinOverrideScope = scope;
168
+ this.editState = createEditState(entry.config, false, this.models, this.skills, {
169
+ fields: BUILTIN_OVERRIDE_FIELDS,
170
+ title: `Builtin Override: ${entry.config.name} [${scope}]`,
171
+ overrideBase: this.resolveBuiltinOverrideBase(entry),
172
+ });
173
+ this.screen = "edit";
174
+ }
98
175
  private enterParallelBuilder(ids: string[]): void {
99
176
  const names = ids.map((id) => this.getAgentEntry(id)?.config.name).filter((n): n is string => Boolean(n));
100
177
  if (names.length === 0) return;
@@ -127,6 +204,27 @@ export class AgentManagerComponent implements Component {
127
204
 
128
205
  private saveEdit(): boolean {
129
206
  const edit = this.editState; if (!edit) return false; const entry = this.getAgentEntry(this.currentAgentId); if (!entry) return false;
207
+ if (entry.config.source === "builtin") {
208
+ const scope = entry.config.override?.scope ?? this.builtinOverrideScope;
209
+ if (!scope) { edit.error = "Choose where to store the override first."; return false; }
210
+ try {
211
+ const override = buildBuiltinOverrideConfig(this.resolveBuiltinOverrideBase(entry), edit.draft);
212
+ if (override) {
213
+ saveBuiltinAgentOverride(this.agentData.cwd, entry.config.name, scope, override);
214
+ } else {
215
+ removeBuiltinAgentOverride(this.agentData.cwd, entry.config.name, scope);
216
+ }
217
+ this.refreshAgentData(entry.config.name);
218
+ this.builtinOverrideScope = null;
219
+ this.editState = null;
220
+ const refreshed = this.getAgentEntry(this.currentAgentId);
221
+ if (refreshed) this.enterDetail(refreshed);
222
+ return true;
223
+ } catch (err) {
224
+ edit.error = err instanceof Error ? err.message : "Failed to save builtin override.";
225
+ return false;
226
+ }
227
+ }
130
228
  if (!edit.draft.name || !edit.draft.description) { edit.error = "Name and description are required."; return false; }
131
229
  let filePath = entry.config.filePath;
132
230
  if (entry.isNew) {
@@ -140,6 +238,24 @@ export class AgentManagerComponent implements Component {
140
238
  catch (err) { edit.error = err instanceof Error ? err.message : "Failed to save agent."; return false; }
141
239
  }
142
240
 
241
+ private removeBuiltinOverride(): boolean {
242
+ const edit = this.editState; if (!edit) return false; const entry = this.getAgentEntry(this.currentAgentId); if (!entry || entry.config.source !== "builtin") return false;
243
+ const scope = entry.config.override?.scope ?? this.builtinOverrideScope;
244
+ if (!scope) { edit.error = "No builtin override to remove."; return false; }
245
+ try {
246
+ removeBuiltinAgentOverride(this.agentData.cwd, entry.config.name, scope);
247
+ this.refreshAgentData(entry.config.name);
248
+ this.builtinOverrideScope = null;
249
+ this.editState = null;
250
+ const refreshed = this.getAgentEntry(this.currentAgentId);
251
+ if (refreshed) this.enterDetail(refreshed);
252
+ return true;
253
+ } catch (err) {
254
+ edit.error = err instanceof Error ? err.message : "Failed to remove builtin override.";
255
+ return false;
256
+ }
257
+ }
258
+
143
259
  private saveChainEdit(): boolean {
144
260
  const state = this.chainEditState; const entry = this.getChainEntry(this.currentChainId); if (!state || !entry) return false;
145
261
  try { const parsed = parseChain(state.editor.buffer, entry.config.source, entry.config.filePath); fs.writeFileSync(entry.config.filePath, serializeChain(parsed), "utf-8"); entry.config = parsed; state.error = undefined; return true; }
@@ -159,6 +275,46 @@ export class AgentManagerComponent implements Component {
159
275
  }
160
276
  }
161
277
 
278
+ private handleOverrideScopeInput(data: string): void {
279
+ const state = this.overrideScopeState;
280
+ const entry = this.getAgentEntry(this.currentAgentId);
281
+ if (!state || !entry) {
282
+ this.screen = "detail";
283
+ this.tui.requestRender();
284
+ return;
285
+ }
286
+
287
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
288
+ this.overrideScopeState = null;
289
+ this.enterDetail(entry);
290
+ this.tui.requestRender();
291
+ return;
292
+ }
293
+
294
+ if (matchesKey(data, "tab") || matchesKey(data, "up") || matchesKey(data, "down")) {
295
+ if (state.allowProject) state.selectedScope = state.selectedScope === "user" ? "project" : "user";
296
+ this.tui.requestRender();
297
+ return;
298
+ }
299
+
300
+ if (data === "u") {
301
+ state.selectedScope = "user";
302
+ this.tui.requestRender();
303
+ return;
304
+ }
305
+
306
+ if (data === "p" && state.allowProject) {
307
+ state.selectedScope = "project";
308
+ this.tui.requestRender();
309
+ return;
310
+ }
311
+
312
+ if (!matchesKey(data, "return")) return;
313
+ this.overrideScopeState = null;
314
+ this.enterBuiltinOverrideEdit(entry, state.selectedScope);
315
+ this.tui.requestRender();
316
+ }
317
+
162
318
  private handleNameInput(data: string): void {
163
319
  const state = this.nameInputState; if (!state) return; state.error = undefined;
164
320
  const canToggleScope = state.allowProject;
@@ -219,6 +375,27 @@ export class AgentManagerComponent implements Component {
219
375
  lines.push(renderFooter(" [enter] continue [esc] cancel ", w, this.theme)); return lines;
220
376
  }
221
377
 
378
+ private renderOverrideScope(w: number): string[] {
379
+ const state = this.overrideScopeState;
380
+ const entry = this.getAgentEntry(this.currentAgentId);
381
+ if (!state || !entry) return [];
382
+ const lines: string[] = [];
383
+ lines.push(renderHeader(` Create Override: ${entry.config.name} `, w, this.theme));
384
+ lines.push(row("", w, this.theme));
385
+ lines.push(row(` ${this.theme.fg("dim", "Where should this builtin override live?")}`, w, this.theme));
386
+ lines.push(row("", w, this.theme));
387
+ const userLine = state.selectedScope === "user" ? this.theme.fg("accent", "▸ user") : " user";
388
+ lines.push(row(` ${userLine}${this.theme.fg("dim", ` ${this.agentData.userSettingsPath}`)}`, w, this.theme));
389
+ if (state.allowProject) {
390
+ const projectPath = this.agentData.projectSettingsPath ?? ".dm/settings.json";
391
+ const projectLine = state.selectedScope === "project" ? this.theme.fg("accent", "▸ project") : " project";
392
+ lines.push(row(` ${projectLine}${this.theme.fg("dim", ` ${projectPath}`)}`, w, this.theme));
393
+ }
394
+ while (lines.length < 8) lines.push(row("", w, this.theme));
395
+ lines.push(renderFooter(" [enter] continue [↑↓/tab] choose [esc] cancel ", w, this.theme));
396
+ return lines;
397
+ }
398
+
222
399
  private renderTemplateSelect(w: number): string[] {
223
400
  const lines: string[] = []; lines.push(renderHeader(" Select Template ", w, this.theme)); lines.push(row("", w, this.theme));
224
401
  const innerW = w - 2; const viewport = 12; const start = Math.max(0, Math.min(this.templateCursor - Math.floor(viewport / 2), Math.max(0, TEMPLATE_ITEMS.length - viewport))); const visible = TEMPLATE_ITEMS.slice(start, start + viewport);
@@ -261,6 +438,7 @@ export class AgentManagerComponent implements Component {
261
438
  switch (this.screen) {
262
439
  case "list": { const action = handleListInput(this.listState, this.listAgents(), data); if (action) this.handleListAction(action); this.tui.requestRender(); return; }
263
440
  case "template-select": this.handleTemplateSelectInput(data); return;
441
+ case "override-scope": this.handleOverrideScopeInput(data); return;
264
442
  case "detail": {
265
443
  const entry = this.getAgentEntry(this.currentAgentId); if (!entry) { this.screen = "list"; this.tui.requestRender(); return; }
266
444
  const action = handleDetailInput(this.detailState, data); if (action) this.handleDetailAction(action, entry); this.tui.requestRender(); return;
@@ -337,6 +515,7 @@ export class AgentManagerComponent implements Component {
337
515
  if (!this.editState) { this.screen = "list"; this.tui.requestRender(); return; }
338
516
  const result = handleEditInput(this.screen as EditScreen, this.editState, data, this.overlayWidth, this.models, this.skills);
339
517
  if (result?.action === "discard") { this.handleEditDiscard(); return; }
518
+ if (result?.action === "delete") { this.removeBuiltinOverride(); this.tui.requestRender(); return; }
340
519
  if (result?.action === "save") { const ok = this.saveEdit(); if (ok) { const entry = this.getAgentEntry(this.currentAgentId); if (entry) this.enterDetail(entry); } this.tui.requestRender(); return; }
341
520
  if (result?.nextScreen) this.screen = result.nextScreen; this.tui.requestRender(); return;
342
521
  }
@@ -344,9 +523,9 @@ export class AgentManagerComponent implements Component {
344
523
  }
345
524
 
346
525
  private handleEditDiscard(): void {
347
- const entry = this.getAgentEntry(this.currentAgentId); if (!entry) { this.screen = "list"; this.editState = null; this.tui.requestRender(); return; }
348
- if (entry.isNew) { this.removeAgentEntry(entry); this.editState = null; this.screen = "list"; this.tui.requestRender(); return; }
349
- this.editState = null; this.enterDetail(entry); this.tui.requestRender();
526
+ const entry = this.getAgentEntry(this.currentAgentId); if (!entry) { this.screen = "list"; this.editState = null; this.builtinOverrideScope = null; this.tui.requestRender(); return; }
527
+ if (entry.isNew) { this.removeAgentEntry(entry); this.editState = null; this.builtinOverrideScope = null; this.screen = "list"; this.tui.requestRender(); return; }
528
+ this.editState = null; this.builtinOverrideScope = null; this.enterDetail(entry); this.tui.requestRender();
350
529
  }
351
530
 
352
531
  private isBuiltin(id: string): boolean { const a = this.getAgentEntry(id); return a?.config.source === "builtin"; }
@@ -365,7 +544,15 @@ export class AgentManagerComponent implements Component {
365
544
 
366
545
  private handleDetailAction(action: DetailAction, entry: AgentEntry): void {
367
546
  if (action.type === "back") { this.screen = "list"; return; }
368
- if (action.type === "edit") { if (entry.config.source === "builtin") { this.statusMessage = { text: "Builtin agents cannot be edited. Clone to user scope to override.", type: "error" }; this.screen = "list"; return; } this.enterEdit(entry); return; }
547
+ if (action.type === "edit") {
548
+ if (entry.config.source === "builtin") {
549
+ if (entry.config.override) this.enterBuiltinOverrideEdit(entry, entry.config.override.scope);
550
+ else this.enterBuiltinOverrideScope(entry);
551
+ return;
552
+ }
553
+ this.enterEdit(entry);
554
+ return;
555
+ }
369
556
  if (action.type === "launch") { this.enterTaskInput([entry.id], "detail"); return; }
370
557
  }
371
558
 
@@ -380,6 +567,7 @@ export class AgentManagerComponent implements Component {
380
567
  switch (this.screen) {
381
568
  case "list": return renderList(this.listState, this.listAgents(), w, this.theme, this.statusMessage);
382
569
  case "template-select": return this.renderTemplateSelect(w);
570
+ case "override-scope": return this.renderOverrideScope(w);
383
571
  case "detail": { const entry = this.getAgentEntry(this.currentAgentId); if (!entry) return renderList(this.listState, this.listAgents(), w, this.theme, this.statusMessage); return renderDetail(this.detailState, entry.config, this.agentData.cwd, w, this.theme); }
384
572
  case "chain-detail": { const entry = this.getChainEntry(this.currentChainId); if (!entry) return renderList(this.listState, this.listAgents(), w, this.theme, this.statusMessage); return renderChainDetail(this.chainDetailState, entry.config, w, this.theme); }
385
573
  case "edit": case "edit-field": case "edit-prompt": return this.editState ? renderEdit(this.screen as EditScreen, this.editState, w, this.theme) : [];