@agentdocumentformat/cli 0.8.0 → 0.8.2

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 (34) hide show
  1. package/dist/adf.mjs +6 -6
  2. package/dist/chunks/{App-A5GUFCFJ.mjs → App-4AZZJNN2.mjs} +511 -406
  3. package/dist/chunks/{adf-schema-YJSGLMA4.mjs → adf-schema-AIPBQTR2.mjs} +9 -3
  4. package/dist/chunks/{chunk-AZYOXXVL.mjs → chunk-2Z7X3UUR.mjs} +11 -198
  5. package/dist/chunks/{chunk-4OGZFUKA.mjs → chunk-7JZXMDDF.mjs} +33 -1
  6. package/dist/chunks/{chunk-V2T6NZX2.mjs → chunk-AEAJDCVK.mjs} +1 -1
  7. package/dist/chunks/{chunk-NZO5O4VN.mjs → chunk-BR3AD5QZ.mjs} +7 -1
  8. package/dist/chunks/{chunk-WVTPPOKK.mjs → chunk-BS3USVEA.mjs} +14 -3
  9. package/dist/chunks/{chunk-JVPZIMXW.mjs → chunk-D7A77SNL.mjs} +1 -1
  10. package/dist/chunks/{chunk-LCMANRYY.mjs → chunk-E7LC6SHP.mjs} +1 -1
  11. package/dist/chunks/{chunk-LGL6ML37.mjs → chunk-F3WSCAJD.mjs} +1 -1
  12. package/dist/chunks/{chunk-6XQZGYMK.mjs → chunk-IMVMSU3A.mjs} +6 -2
  13. package/dist/chunks/{chunk-C4GA5SI4.mjs → chunk-KCIRFUZH.mjs} +2 -2
  14. package/dist/chunks/{chunk-YRMKSHW3.mjs → chunk-LVF2DRBR.mjs} +25 -12
  15. package/dist/chunks/{chunk-PRPTKMQW.mjs → chunk-RHBGCOS3.mjs} +2 -2
  16. package/dist/chunks/{chunk-WX6QCUWI.mjs → chunk-RJDOVMM4.mjs} +95 -12
  17. package/dist/chunks/{chunk-G6NBFYG5.mjs → chunk-SOSPQPEN.mjs} +189 -0
  18. package/dist/chunks/chunk-VENQBMOA.mjs +62 -0
  19. package/dist/chunks/{chunk-XT2BJUFC.mjs → chunk-YRBFIDLB.mjs} +33 -6
  20. package/dist/chunks/{claude-subscription-33BMZB6I.mjs → claude-subscription-EB4LAAVV.mjs} +3 -3
  21. package/dist/chunks/{cli-I47IFDHJ.mjs → cli-PCVFB6B4.mjs} +5 -5
  22. package/dist/chunks/{commands-E4EQUHE5.mjs → commands-KOMPXJZS.mjs} +3 -3
  23. package/dist/chunks/{daemon-FQ5BV3AP.mjs → daemon-HEQ3FUYH.mjs} +729 -111
  24. package/dist/chunks/{daemon-control-L3CLG63P.mjs → daemon-control-MKN7G5M3.mjs} +4 -4
  25. package/dist/chunks/{layout-EPJQNFAI.mjs → layout-ECWGJF6M.mjs} +2 -2
  26. package/dist/chunks/{open-TLGS2Z3Q.mjs → open-3OR6WT4T.mjs} +4 -4
  27. package/dist/chunks/{pipeline-executor-FD4FBWQA.mjs → pipeline-executor-DRYEEFDR.mjs} +4 -4
  28. package/dist/chunks/{remote-access-cli-XXAPQWH6.mjs → remote-access-cli-GLEEP7DI.mjs} +2 -2
  29. package/dist/chunks/sdk-OJOAF4DP.mjs +30014 -0
  30. package/dist/chunks/{store-ZRN2WCFB.mjs → store-55CQSAOX.mjs} +3 -2
  31. package/dist/chunks/{tui-SPSPDADM.mjs → tui-RAUVFOYY.mjs} +11 -11
  32. package/package.json +2 -2
  33. package/dist/chunks/sdk-5FD2ZP7N.mjs +0 -28422
  34. package/dist/chunks/{chunk-APRU6PHC.mjs → chunk-JZ7AQNPM.mjs} +3 -3
@@ -6,7 +6,7 @@ const __filename = __adfFileURLToPath(import.meta.url);
6
6
  const __dirname = __adfDirname(__filename);
7
7
  import {
8
8
  DaemonClient
9
- } from "./chunk-6XQZGYMK.mjs";
9
+ } from "./chunk-IMVMSU3A.mjs";
10
10
  import {
11
11
  auth_flow_exports
12
12
  } from "./chunk-BHY7QDCM.mjs";
@@ -20,38 +20,19 @@ import {
20
20
  readLayout,
21
21
  setMouse,
22
22
  setSidebarHidden
23
- } from "./chunk-APRU6PHC.mjs";
23
+ } from "./chunk-JZ7AQNPM.mjs";
24
24
  import {
25
- DaemonError,
26
- MAIN_LOOP,
27
- PROMPT_PREFILL_KEY,
28
- SERVER_STOPPED_TEXT,
29
- StoreProvider,
30
- askQuestion,
31
- emptyTranscript,
32
- findTracked,
33
- folderName,
34
- isAsyncInput,
35
- isBusyState,
36
- isCredentialsLocked,
37
- isTrackedKey,
38
- parseServer,
39
- servedText,
40
- serverBadge,
41
- serverText,
42
- shallowEqual,
43
- shortUrl,
44
- siteKind,
45
- siteOf,
46
- stoppedError,
47
- stoppedLabel,
48
- transcriptKey,
49
- useActions,
50
- useClient,
51
- useStore,
52
- useTuiSelector
53
- } from "./chunk-WX6QCUWI.mjs";
54
- import "./chunk-75DC5EWN.mjs";
25
+ isMouseGarbage,
26
+ lastRawInput,
27
+ newlineKey,
28
+ noteRawInput,
29
+ noteShiftEnter,
30
+ parseMouse,
31
+ superArrow,
32
+ terminalCaps,
33
+ useTerminalCaps,
34
+ writeTerminal
35
+ } from "./chunk-7XYNI52E.mjs";
55
36
  import {
56
37
  CHANNELS_OVERLAY,
57
38
  CHANNEL_ORDER,
@@ -123,13 +104,7 @@ import {
123
104
  validateCredentials,
124
105
  validateMcpForm,
125
106
  validateProvider
126
- } from "./chunk-AZYOXXVL.mjs";
127
- import {
128
- require_jsx_runtime
129
- } from "./chunk-7CV2SA3H.mjs";
130
- import {
131
- cjs
132
- } from "./chunk-SGUTYISF.mjs";
107
+ } from "./chunk-2Z7X3UUR.mjs";
133
108
  import {
134
109
  WELCOME_INTRO,
135
110
  WELCOME_OVERLAY,
@@ -156,8 +131,7 @@ import {
156
131
  guarded_fetch_exports,
157
132
  skills_catalog_schema_exports
158
133
  } from "./chunk-HBOFZRUS.mjs";
159
- import "./chunk-4OGZFUKA.mjs";
160
- import "./chunk-G6NBFYG5.mjs";
134
+ import "./chunk-SOSPQPEN.mjs";
161
135
  import {
162
136
  Box_default,
163
137
  Text,
@@ -176,28 +150,54 @@ import {
176
150
  useTheme
177
151
  } from "./chunk-NZLEPJ5M.mjs";
178
152
  import {
179
- isMouseGarbage,
180
- lastRawInput,
181
- newlineKey,
182
- noteRawInput,
183
- noteShiftEnter,
184
- parseMouse,
185
- superArrow,
186
- terminalCaps,
187
- useTerminalCaps,
188
- writeTerminal
189
- } from "./chunk-7XYNI52E.mjs";
153
+ DaemonError,
154
+ MAIN_LOOP,
155
+ PROMPT_PREFILL_KEY,
156
+ SERVER_STOPPED_TEXT,
157
+ StoreProvider,
158
+ askQuestion,
159
+ emptyTranscript,
160
+ findTracked,
161
+ folderName,
162
+ isAsyncInput,
163
+ isBusyState,
164
+ isCredentialsLocked,
165
+ isTrackedKey,
166
+ parseServer,
167
+ servedText,
168
+ serverBadge,
169
+ serverText,
170
+ shallowEqual,
171
+ shortUrl,
172
+ siteKind,
173
+ siteOf,
174
+ stoppedError,
175
+ stoppedLabel,
176
+ transcriptKey,
177
+ useActions,
178
+ useClient,
179
+ useStore,
180
+ useTuiSelector
181
+ } from "./chunk-RJDOVMM4.mjs";
182
+ import "./chunk-75DC5EWN.mjs";
183
+ import {
184
+ require_jsx_runtime
185
+ } from "./chunk-7CV2SA3H.mjs";
186
+ import {
187
+ cjs
188
+ } from "./chunk-SGUTYISF.mjs";
189
+ import "./chunk-7JZXMDDF.mjs";
190
190
  import {
191
191
  require_react
192
192
  } from "./chunk-6GJ4QHRI.mjs";
193
193
  import {
194
194
  ADF_SKILLS_REGISTRY_URL
195
- } from "./chunk-LCMANRYY.mjs";
195
+ } from "./chunk-E7LC6SHP.mjs";
196
196
  import "./chunk-UYY5M2PB.mjs";
197
197
  import "./chunk-2NAP2ZF7.mjs";
198
198
  import "./chunk-LKFHERRV.mjs";
199
- import "./chunk-V2T6NZX2.mjs";
200
- import "./chunk-WVTPPOKK.mjs";
199
+ import "./chunk-AEAJDCVK.mjs";
200
+ import "./chunk-BS3USVEA.mjs";
201
201
  import "./chunk-WJGPNHGX.mjs";
202
202
  import "./chunk-PAK5JCO2.mjs";
203
203
  import "./chunk-ZPB3HYM7.mjs";
@@ -205,7 +205,10 @@ import "./chunk-LTX74NU6.mjs";
205
205
  import {
206
206
  require_dist
207
207
  } from "./chunk-APHX6HSV.mjs";
208
- import "./chunk-NZO5O4VN.mjs";
208
+ import {
209
+ agent_commands_exports
210
+ } from "./chunk-VENQBMOA.mjs";
211
+ import "./chunk-BR3AD5QZ.mjs";
209
212
  import "./chunk-ZJKXHM4Q.mjs";
210
213
  import "./chunk-RIX34QHC.mjs";
211
214
  import {
@@ -217,11 +220,317 @@ import {
217
220
  var import_react60 = __toESM(require_react(), 1);
218
221
 
219
222
  // src/main/tui/app/shell-context.ts
220
- var import_react = __toESM(require_react(), 1), ShellContext = (0, import_react.createContext)(null);
223
+ var import_react = __toESM(require_react(), 1);
224
+
225
+ // src/shared/utils/skills-panel.ts
226
+ var skills_panel_exports = {};
227
+ __export(skills_panel_exports, {
228
+ MAX_CATALOG_SOURCES: () => MAX_CATALOG_SOURCES,
229
+ MAX_REGISTRY_BYTES: () => MAX_REGISTRY_BYTES,
230
+ MAX_SKILLS: () => MAX_SKILLS,
231
+ MAX_SKILL_FILE_BYTES: () => MAX_SKILL_FILE_BYTES,
232
+ SKILLS_REGISTRY_PATH: () => SKILLS_REGISTRY_PATH,
233
+ SKILLS_STATE_PATH: () => SKILLS_STATE_PATH,
234
+ SKILL_MANIFEST: () => SKILL_MANIFEST,
235
+ addCatalogSource: () => addCatalogSource,
236
+ catalogSourceLabel: () => catalogSourceLabel,
237
+ estimateTokens: () => estimateTokens,
238
+ filterCatalogEntries: () => filterCatalogEntries,
239
+ installedSkillNames: () => installedSkillNames,
240
+ isCatalogUrl: () => isCatalogUrl,
241
+ mergeCatalogResults: () => mergeCatalogResults,
242
+ mergeDisabledList: () => mergeDisabledList,
243
+ normalizeCatalogSources: () => normalizeCatalogSources,
244
+ parseSkillsRegistry: () => parseSkillsRegistry,
245
+ sanitizeDisplayText: () => sanitizeDisplayText,
246
+ skillProblems: () => skillProblems,
247
+ unindexedHint: () => unindexedHint
248
+ });
249
+ var SKILLS_REGISTRY_PATH = "skills-registry.json", SKILLS_STATE_PATH = "skills-state.json", SKILL_MANIFEST = /^skills\/([^/]+)\/SKILL\.md$/, MAX_SKILLS = 48, MAX_REGISTRY_BYTES = 32 * 1024, MAX_SKILL_FILE_BYTES = 256 * 1024;
250
+ function parseSkillsRegistry(text) {
251
+ if (!text) return null;
252
+ try {
253
+ let doc = JSON.parse(text);
254
+ if (doc?.schema !== 1 || typeof doc.skills != "object" || doc.skills === null) return null;
255
+ let entries = [];
256
+ for (let value of Object.values(doc.skills)) {
257
+ let skill = value;
258
+ typeof skill?.name != "string" || typeof skill?.path != "string" || entries.push({
259
+ name: skill.name,
260
+ description: typeof skill.description == "string" ? skill.description : void 0,
261
+ path: skill.path,
262
+ enabled: skill.enabled !== !1
263
+ });
264
+ }
265
+ let rejected = [];
266
+ if (Array.isArray(doc.rejected))
267
+ for (let raw of doc.rejected) {
268
+ let row4 = raw;
269
+ typeof row4?.path != "string" || typeof row4?.reason != "string" || rejected.push({ path: row4.path, reason: row4.reason, name: SKILL_MANIFEST.exec(row4.path)?.[1] ?? null });
270
+ }
271
+ return { entries: entries.sort((a, b) => a.name.localeCompare(b.name)), rejected };
272
+ } catch {
273
+ return null;
274
+ }
275
+ }
276
+ function mergeDisabledList(existingText, name, enabled) {
277
+ let doc = {};
278
+ if (existingText)
279
+ try {
280
+ let parsed = JSON.parse(existingText);
281
+ parsed && typeof parsed == "object" && !Array.isArray(parsed) && (doc = parsed);
282
+ } catch {
283
+ }
284
+ let current = Array.isArray(doc.disabled) ? doc.disabled.filter((entry) => typeof entry == "string") : [], next = new Set(current);
285
+ return enabled ? next.delete(name) : next.add(name), JSON.stringify({ ...doc, schema: 1, disabled: [...next].sort() }, null, 2) + `
286
+ `;
287
+ }
288
+ var UNSAFE_DISPLAY_CHARS = /[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g;
289
+ function sanitizeDisplayText(value) {
290
+ return value ? value.replace(UNSAFE_DISPLAY_CHARS, " ").replace(/\s+/g, " ").trim() : "";
291
+ }
292
+ function isCatalogUrl(value) {
293
+ let trimmed = value.trim();
294
+ if (!trimmed) return !1;
295
+ try {
296
+ return new URL(trimmed).protocol === "https:";
297
+ } catch {
298
+ return !1;
299
+ }
300
+ }
301
+ function estimateTokens(bytes) {
302
+ return Math.ceil(bytes / 4);
303
+ }
304
+ var MAX_CATALOG_SOURCES = 8;
305
+ function normalizeCatalogSources(stored) {
306
+ if (!Array.isArray(stored)) return [ADF_SKILLS_REGISTRY_URL];
307
+ let sources2 = [], seen = /* @__PURE__ */ new Set();
308
+ for (let raw of stored) {
309
+ if (typeof raw != "string") continue;
310
+ let url2 = raw.trim();
311
+ if (!(!isCatalogUrl(url2) || seen.has(url2)) && (seen.add(url2), sources2.push(url2), sources2.length >= MAX_CATALOG_SOURCES))
312
+ break;
313
+ }
314
+ return sources2;
315
+ }
316
+ function addCatalogSource(sources2, raw) {
317
+ let url2 = raw.trim();
318
+ return url2 ? isCatalogUrl(url2) ? sources2.includes(url2) ? { ok: !1, error: "That source is already listed." } : sources2.length >= MAX_CATALOG_SOURCES ? { ok: !1, error: `At most ${MAX_CATALOG_SOURCES} catalog sources.` } : { ok: !0, sources: [...sources2, url2] } : { ok: !1, error: "A catalog source must be an https:// URL." } : { ok: !1, error: "Enter a catalog URL." };
319
+ }
320
+ function catalogSourceLabel(url2, publisher) {
321
+ let named = sanitizeDisplayText(publisher);
322
+ if (named) return named;
323
+ try {
324
+ return new URL(url2).hostname;
325
+ } catch {
326
+ return url2;
327
+ }
328
+ }
329
+ function mergeCatalogResults(results) {
330
+ let merged = [], seen = /* @__PURE__ */ new Set();
331
+ for (let result of results) {
332
+ if (!result.ok) continue;
333
+ let sourceLabel = catalogSourceLabel(result.url, result.publisher);
334
+ for (let entry of result.entries)
335
+ seen.has(entry.name) || (seen.add(entry.name), merged.push({ ...entry, sourceUrl: result.url, sourceLabel }));
336
+ }
337
+ return merged.sort((a, b) => a.name.localeCompare(b.name));
338
+ }
339
+ function filterCatalogEntries(entries, query) {
340
+ let needle = query.trim().toLowerCase();
341
+ if (!needle) return entries;
342
+ let byName2 = [], byDescription = [];
343
+ for (let entry of entries)
344
+ entry.name.toLowerCase().includes(needle) ? byName2.push(entry) : entry.description.toLowerCase().includes(needle) && byDescription.push(entry);
345
+ return [...byName2, ...byDescription];
346
+ }
347
+ function installedSkillNames(files2) {
348
+ let names = /* @__PURE__ */ new Set();
349
+ for (let file of files2) {
350
+ let match = SKILL_MANIFEST.exec(file.path);
351
+ match && names.add(match[1]);
352
+ }
353
+ return names;
354
+ }
355
+ function skillProblems(installedNames, registry) {
356
+ if (!registry) return [];
357
+ let indexed = new Set(registry.entries.map((s) => s.name)), rows = /* @__PURE__ */ new Map();
358
+ for (let name of installedNames)
359
+ indexed.has(name) || rows.set(name, { label: name, path: `skills/${name}/SKILL.md`, reason: null, isPackage: !0 });
360
+ for (let rejection of registry.rejected) {
361
+ let key = rejection.name ?? rejection.path;
362
+ rejection.name && indexed.has(rejection.name) || rows.set(key, {
363
+ label: rejection.name ?? rejection.path,
364
+ path: rejection.path,
365
+ reason: rejection.reason,
366
+ isPackage: rejection.name !== null
367
+ });
368
+ }
369
+ return [...rows.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, row4]) => ({ key, ...row4 }));
370
+ }
371
+ function unindexedHint() {
372
+ return `not indexed \u2014 the folder name must be lowercase kebab-case and match the frontmatter name, description must be one line under 500 characters, and the file must be under ${Math.round(MAX_SKILL_FILE_BYTES / 1024)} KB. The catalog also stops at ${MAX_SKILLS} skills or ${Math.round(MAX_REGISTRY_BYTES / 1024)} KB, whichever comes first.`;
373
+ }
374
+
375
+ // src/main/tui/commands/registry.ts
376
+ function collectCommands(views, builtins = []) {
377
+ let commands3 = [], actions4 = [], conflicts = [], byName2 = /* @__PURE__ */ new Map(), actionIds = /* @__PURE__ */ new Set(), addCommand = (command, owner) => {
378
+ let withView = command.view ? command : { ...command, view: owner };
379
+ for (let name of [command.name, ...command.aliases ?? []]) {
380
+ let key = name.toLowerCase(), existing = byName2.get(key);
381
+ if (existing) {
382
+ conflicts.push(`/${key} from ${owner} is shadowed by ${existing.view ?? "builtin"}`);
383
+ continue;
384
+ }
385
+ byName2.set(key, withView);
386
+ }
387
+ commands3.push(withView);
388
+ }, addAction = (action, owner) => {
389
+ if (actionIds.has(action.id)) {
390
+ conflicts.push(`palette action ${action.id} from ${owner} is a duplicate`);
391
+ return;
392
+ }
393
+ actionIds.add(action.id), actions4.push(action.view ? action : { ...action, view: owner });
394
+ };
395
+ for (let builtin of builtins) {
396
+ for (let command of builtin.commands ?? []) addCommand(command, "builtin");
397
+ for (let action of builtin.actions ?? []) addAction(action, "builtin");
398
+ }
399
+ for (let view2 of views) {
400
+ for (let command of view2.commands ?? []) addCommand(command, view2.id);
401
+ for (let action of view2.actions ?? []) addAction(action, view2.id);
402
+ }
403
+ return {
404
+ commands: commands3,
405
+ actions: actions4,
406
+ conflicts,
407
+ find: (name) => byName2.get(name.toLowerCase())
408
+ };
409
+ }
410
+ function parseSlash(text) {
411
+ let trimmed = text.trimStart();
412
+ if (!trimmed.startsWith("/") || trimmed.startsWith("//")) return null;
413
+ let match = trimmed.slice(1).match(/^(\S*)\s*([\s\S]*)$/);
414
+ if (!match) return null;
415
+ let rest = match[2] ?? "";
416
+ return { name: match[1].toLowerCase(), args: splitArgs(rest), rest };
417
+ }
418
+ function splitArgs(text) {
419
+ let args = [], pattern = /"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g, m;
420
+ for (; m = pattern.exec(text); ) args.push(m[1] !== void 0 ? m[1].replace(/\\(.)/g, "$1") : m[2] ?? m[3]);
421
+ return args;
422
+ }
423
+ function createScope(store, exit) {
424
+ let state = store.getState(), selected = state.selectedAgentId, stoppedKey = isTrackedKey(selected) ? selected : null, agentId = stoppedKey ? null : selected;
425
+ return {
426
+ store,
427
+ actions: store.actions,
428
+ client: store.client,
429
+ state: () => store.getState(),
430
+ agentId,
431
+ stoppedKey,
432
+ loop: agentId ? state.selectedLoop[agentId] ?? MAIN_LOOP : MAIN_LOOP,
433
+ print: (text, level = "info") => store.actions.toast(text, level),
434
+ exit
435
+ };
436
+ }
437
+ async function runSlash(text, registry, store, exit) {
438
+ let parsed = parseSlash(text);
439
+ if (!parsed) return !1;
440
+ let scope = createScope(store, exit), command = registry.find(parsed.name);
441
+ if (!command)
442
+ return store.actions.toast(`Unknown command /${parsed.name} \u2014 /help lists commands`, "warn"), !0;
443
+ if (command.agentCommand && /[\r\n]/.test(text))
444
+ return store.actions.toast(`/${command.name}: agent commands must be one line`, "warn"), !0;
445
+ if (command.available && !command.available(scope))
446
+ return store.actions.toast(`/${command.name} is not available here`, "warn"), !0;
447
+ let ctx = { ...scope, args: parsed.args, rest: parsed.rest };
448
+ try {
449
+ await command.run(ctx);
450
+ } catch (err) {
451
+ store.actions.toast(`/${command.name}: ${err instanceof Error ? err.message : String(err)}`, "error");
452
+ }
453
+ return !0;
454
+ }
455
+ function completeSlash(text, registry, scope) {
456
+ let parsed = parseSlash(text);
457
+ if (!parsed) return [];
458
+ if (/\s/.test(text.trimStart().slice(1))) {
459
+ let command = registry.find(parsed.name);
460
+ return command?.complete ? command.complete(parsed.rest, scope).map((value) => ({ value: `/${command.name} ${value}`, command })) : [];
461
+ }
462
+ let seen = /* @__PURE__ */ new Set(), out = [];
463
+ for (let command of registry.commands) {
464
+ if (seen.has(command) || command.available && !command.available(scope)) continue;
465
+ [command.name, ...command.aliases ?? []].some((n) => n.startsWith(parsed.name)) && (seen.add(command), out.push({ value: `/${command.name}`, command }));
466
+ }
467
+ let exact = (c) => [c.name, ...c.aliases ?? []].some((n) => n.toLowerCase() === parsed.name);
468
+ return [...out.filter((o) => exact(o.command)), ...out.filter((o) => !exact(o.command))];
469
+ }
470
+
471
+ // src/main/tui/commands/agent-commands.ts
472
+ var { validateAgentCommands, parseAuthorizeToken } = cjs(agent_commands_exports), { sanitizeDisplayText: sanitizeDisplayText2 } = cjs(skills_panel_exports);
473
+ function validAgentCommands(value) {
474
+ if (!Array.isArray(value)) return [];
475
+ let invalid = new Set(validateAgentCommands(value).map((issue) => issue.index));
476
+ return value.slice(0, 64).filter((_, index) => !invalid.has(index));
477
+ }
478
+ function registered(ctx) {
479
+ return validAgentCommands(ctx.agentId ? ctx.state().agents[ctx.agentId]?.config?.commands : void 0);
480
+ }
481
+ async function run(ctx, name, args) {
482
+ if (!ctx.agentId) {
483
+ ctx.print(ctx.stoppedKey ? `Agent is off. Start it, then run /${name} again.` : "Select an agent first.", "warn");
484
+ return;
485
+ }
486
+ await ctx.actions.runAgentCommand(ctx.agentId, ctx.loop, name, args);
487
+ }
488
+ function namedArgs(rest) {
489
+ let match = rest.trimStart().match(/^(\S+)(?:\s+([\s\S]*))?$/);
490
+ return match ? { name: match[1], args: match[2] ?? "" } : void 0;
491
+ }
492
+ var agentCommand = {
493
+ name: "command",
494
+ args: "<name> [args]",
495
+ description: "Run an agent lambda command; add !authorize for one authorized run",
496
+ agentCommand: "*",
497
+ complete: (partial, scope) => registered(scope).filter((command) => command.name.startsWith(partial)).map((command) => command.name),
498
+ run: async (ctx) => {
499
+ let input = namedArgs(ctx.rest);
500
+ if (!input) {
501
+ ctx.print("Usage: /command <name> [args] [!authorize]", "warn");
502
+ return;
503
+ }
504
+ await run(ctx, input.name, input.args);
505
+ }
506
+ };
507
+ function withAgentCommands(base2, value) {
508
+ let dynamic = validAgentCommands(value).filter((command) => !base2.find(command.name)).map((command) => ({
509
+ name: command.name,
510
+ args: sanitizeDisplayText2(command.usage) || void 0,
511
+ description: `${sanitizeDisplayText2(command.description)}${command.locked ? " [locked]" : ""}`,
512
+ view: "chat",
513
+ agentCommand: command.name,
514
+ run: (ctx) => run(ctx, command.name, ctx.rest)
515
+ }));
516
+ return { ...base2, commands: [...base2.commands, ...dynamic], find: (name) => base2.find(name) ?? dynamic.find((command) => command.name === name.toLowerCase()) };
517
+ }
518
+ function isAuthorizedCommandInput(text, registry) {
519
+ let parsed = parseSlash(text), name = parsed && registry.find(parsed.name)?.agentCommand;
520
+ if (!parsed || !name) return !1;
521
+ let args = name === "*" ? namedArgs(parsed.rest)?.args : parsed.rest;
522
+ return args !== void 0 && parseAuthorizeToken(args).authorize;
523
+ }
524
+ function hasCommandArguments(rest) {
525
+ return parseAuthorizeToken(rest).args.length > 0;
526
+ }
527
+
528
+ // src/main/tui/app/shell-context.ts
529
+ var ShellContext = (0, import_react.createContext)(null);
221
530
  function useShell() {
222
- let value = (0, import_react.useContext)(ShellContext);
531
+ let value = (0, import_react.useContext)(ShellContext), commands3 = useTuiSelector((state) => state.agents[state.selectedAgentId ?? ""]?.config?.commands), registry = (0, import_react.useMemo)(() => value ? withAgentCommands(value.registry, commands3) : void 0, [value?.registry, commands3]);
223
532
  if (!value) throw new Error("useShell must be used inside the TUI shell");
224
- return value;
533
+ return { ...value, registry };
225
534
  }
226
535
 
227
536
  // src/main/tui/app/Shell.tsx
@@ -1594,151 +1903,57 @@ function AgentRow({ row: row4, selected, focused, width, unread }) {
1594
1903
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: !0, color: c.fg(selected ? theme2.color.accent : theme2.color.text), children: [
1595
1904
  " ",
1596
1905
  name
1597
- ] }),
1598
- stateText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(theme2.color.muted), children: [
1599
- " ",
1600
- stateText
1601
- ] }) : null,
1602
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(fill) }),
1603
- badges.map((badge, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: !0, color: c.fg(badge.startsWith(theme2.glyph.warn) ? theme2.color.warn : badge.startsWith(theme2.glyph.loop) ? theme2.color.loop : theme2.color.info), children: [
1604
- " ",
1605
- badge
1606
- ] }, badge))
1607
- ] });
1608
- }
1609
- function FolderRow({ row: row4, width }) {
1610
- let theme2 = useTheme(), count = `${row4.loaded}/${row4.total}`, name = truncate(folderName(row4.path), Math.max(3, width - displayWidth(count) - 1));
1611
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { wrap: "truncate-end", children: [
1612
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: !0, color: theme2.color.muted, children: name }),
1613
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(Math.max(1, width - displayWidth(name) - displayWidth(count))) }),
1614
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: theme2.color.dim, children: count })
1615
- ] });
1616
- }
1617
- function StoppedRow({ row: row4, selected, focused, width, errors, busy }) {
1618
- let theme2 = useTheme(), c = rowColors(theme2, selected, focused), { entry } = row4, label2 = busy ? busy === "starting" ? "starting" : "loading" : stoppedLabel(entry, errors), glyph = stoppedGlyph(theme2, entry, errors), nameRoom = Math.max(3, width - 4), name = truncate(entry.agent.name, nameRoom), stateRoom = nameRoom - displayWidth(name) - 1, stateText = stateRoom >= 4 ? truncate(label2, stateRoom) : "", labelColor = label2 === "needs review" ? theme2.color.warn : label2 === "load error" || label2 === "unreadable" ? theme2.color.error : theme2.color.dim;
1619
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { backgroundColor: c.bg, inverse: c.inverse, underline: c.underline, wrap: "truncate-end", children: [
1620
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
1621
- busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: c.fg(theme2.color.live) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(glyph.color), children: glyph.glyph }),
1622
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(selected ? theme2.color.accent : theme2.color.muted), bold: selected, children: [
1623
- " ",
1624
- name
1625
- ] }),
1626
- stateText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(labelColor), children: [
1627
- " ",
1628
- stateText
1629
- ] }) : null
1630
- ] });
1631
- }
1632
- function LoopRow({ row: row4, selected, focused, width, nextRun }) {
1633
- let theme2 = useTheme(), { loop, agent: agent2 } = row4, d = describeLoop(agent2, loop), c = rowColors(theme2, selected, focused), hint = d.kind === "running" ? d.label : d.kind === "disabled" ? "off" : nextRun !== void 0 ? formatIn(nextRun) : "", nameRoom = Math.max(3, width - 5 - (hint ? displayWidth(hint) + 1 : 0)), name = truncate(loop.info.name, nameRoom), fill = Math.max(0, width - 5 - displayWidth(name) - (hint ? displayWidth(hint) + 1 : 0)), nameColor = loop.info.enabled ? loop.info.isMain ? theme2.color.accent : theme2.color.loop : theme2.color.dim;
1634
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { backgroundColor: c.bg, inverse: c.inverse, underline: c.underline, wrap: "truncate-end", children: [
1635
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
1636
- d.kind === "running" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: c.fg(theme2.color.loop) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(d.kind === "disabled" ? theme2.color.dim : theme2.color.muted), children: d.kind === "disabled" ? "-" : theme2.glyph.ring }),
1637
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(nameColor), children: [
1638
- " ",
1639
- theme2.glyph.loop
1640
- ] }),
1641
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(nameColor), bold: selected, children: name }),
1642
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(fill) }),
1643
- hint ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(d.kind === "running" ? theme2.color.live : theme2.color.muted), children: [
1644
- " ",
1645
- hint
1646
- ] }) : null
1647
- ] });
1648
- }
1649
-
1650
- // src/main/tui/commands/registry.ts
1651
- function collectCommands(views, builtins = []) {
1652
- let commands3 = [], actions4 = [], conflicts = [], byName2 = /* @__PURE__ */ new Map(), actionIds = /* @__PURE__ */ new Set(), addCommand = (command, owner) => {
1653
- let withView = command.view ? command : { ...command, view: owner };
1654
- for (let name of [command.name, ...command.aliases ?? []]) {
1655
- let key = name.toLowerCase(), existing = byName2.get(key);
1656
- if (existing) {
1657
- conflicts.push(`/${key} from ${owner} is shadowed by ${existing.view ?? "builtin"}`);
1658
- continue;
1659
- }
1660
- byName2.set(key, withView);
1661
- }
1662
- commands3.push(withView);
1663
- }, addAction = (action, owner) => {
1664
- if (actionIds.has(action.id)) {
1665
- conflicts.push(`palette action ${action.id} from ${owner} is a duplicate`);
1666
- return;
1667
- }
1668
- actionIds.add(action.id), actions4.push(action.view ? action : { ...action, view: owner });
1669
- };
1670
- for (let builtin of builtins) {
1671
- for (let command of builtin.commands ?? []) addCommand(command, "builtin");
1672
- for (let action of builtin.actions ?? []) addAction(action, "builtin");
1673
- }
1674
- for (let view2 of views) {
1675
- for (let command of view2.commands ?? []) addCommand(command, view2.id);
1676
- for (let action of view2.actions ?? []) addAction(action, view2.id);
1677
- }
1678
- return {
1679
- commands: commands3,
1680
- actions: actions4,
1681
- conflicts,
1682
- find: (name) => byName2.get(name.toLowerCase())
1683
- };
1684
- }
1685
- function parseSlash(text) {
1686
- let trimmed = text.trimStart();
1687
- if (!trimmed.startsWith("/") || trimmed.startsWith("//")) return null;
1688
- let match = trimmed.slice(1).match(/^(\S*)\s*([\s\S]*)$/);
1689
- if (!match) return null;
1690
- let rest = match[2] ?? "";
1691
- return { name: match[1].toLowerCase(), args: splitArgs(rest), rest };
1692
- }
1693
- function splitArgs(text) {
1694
- let args = [], pattern = /"((?:[^"\\]|\\.)*)"|'([^']*)'|(\S+)/g, m;
1695
- for (; m = pattern.exec(text); ) args.push(m[1] !== void 0 ? m[1].replace(/\\(.)/g, "$1") : m[2] ?? m[3]);
1696
- return args;
1697
- }
1698
- function createScope(store, exit) {
1699
- let state = store.getState(), selected = state.selectedAgentId, stoppedKey = isTrackedKey(selected) ? selected : null, agentId = stoppedKey ? null : selected;
1700
- return {
1701
- store,
1702
- actions: store.actions,
1703
- client: store.client,
1704
- state: () => store.getState(),
1705
- agentId,
1706
- stoppedKey,
1707
- loop: agentId ? state.selectedLoop[agentId] ?? MAIN_LOOP : MAIN_LOOP,
1708
- print: (text, level = "info") => store.actions.toast(text, level),
1709
- exit
1710
- };
1906
+ ] }),
1907
+ stateText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(theme2.color.muted), children: [
1908
+ " ",
1909
+ stateText
1910
+ ] }) : null,
1911
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(fill) }),
1912
+ badges.map((badge, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { bold: !0, color: c.fg(badge.startsWith(theme2.glyph.warn) ? theme2.color.warn : badge.startsWith(theme2.glyph.loop) ? theme2.color.loop : theme2.color.info), children: [
1913
+ " ",
1914
+ badge
1915
+ ] }, badge))
1916
+ ] });
1711
1917
  }
1712
- async function runSlash(text, registry, store, exit) {
1713
- let parsed = parseSlash(text);
1714
- if (!parsed) return !1;
1715
- let scope = createScope(store, exit), command = registry.find(parsed.name);
1716
- if (!command)
1717
- return store.actions.toast(`Unknown command /${parsed.name} \u2014 /help lists commands`, "warn"), !0;
1718
- if (command.available && !command.available(scope))
1719
- return store.actions.toast(`/${command.name} is not available here`, "warn"), !0;
1720
- let ctx = { ...scope, args: parsed.args, rest: parsed.rest };
1721
- try {
1722
- await command.run(ctx);
1723
- } catch (err) {
1724
- store.actions.toast(`/${command.name}: ${err instanceof Error ? err.message : String(err)}`, "error");
1725
- }
1726
- return !0;
1918
+ function FolderRow({ row: row4, width }) {
1919
+ let theme2 = useTheme(), count = `${row4.loaded}/${row4.total}`, name = truncate(folderName(row4.path), Math.max(3, width - displayWidth(count) - 1));
1920
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { wrap: "truncate-end", children: [
1921
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { bold: !0, color: theme2.color.muted, children: name }),
1922
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(Math.max(1, width - displayWidth(name) - displayWidth(count))) }),
1923
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: theme2.color.dim, children: count })
1924
+ ] });
1727
1925
  }
1728
- function completeSlash(text, registry, scope) {
1729
- let parsed = parseSlash(text);
1730
- if (!parsed) return [];
1731
- if (/\s/.test(text.trimStart().slice(1))) {
1732
- let command = registry.find(parsed.name);
1733
- return command?.complete ? command.complete(parsed.rest, scope).map((value) => ({ value: `/${command.name} ${value}`, command })) : [];
1734
- }
1735
- let seen = /* @__PURE__ */ new Set(), out = [];
1736
- for (let command of registry.commands) {
1737
- if (seen.has(command) || command.available && !command.available(scope)) continue;
1738
- [command.name, ...command.aliases ?? []].some((n) => n.startsWith(parsed.name)) && (seen.add(command), out.push({ value: `/${command.name}`, command }));
1739
- }
1740
- let exact = (c) => [c.name, ...c.aliases ?? []].some((n) => n.toLowerCase() === parsed.name);
1741
- return [...out.filter((o) => exact(o.command)), ...out.filter((o) => !exact(o.command))];
1926
+ function StoppedRow({ row: row4, selected, focused, width, errors, busy }) {
1927
+ let theme2 = useTheme(), c = rowColors(theme2, selected, focused), { entry } = row4, label2 = busy ? busy === "starting" ? "starting" : "loading" : stoppedLabel(entry, errors), glyph = stoppedGlyph(theme2, entry, errors), nameRoom = Math.max(3, width - 4), name = truncate(entry.agent.name, nameRoom), stateRoom = nameRoom - displayWidth(name) - 1, stateText = stateRoom >= 4 ? truncate(label2, stateRoom) : "", labelColor = label2 === "needs review" ? theme2.color.warn : label2 === "load error" || label2 === "unreadable" ? theme2.color.error : theme2.color.dim;
1928
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { backgroundColor: c.bg, inverse: c.inverse, underline: c.underline, wrap: "truncate-end", children: [
1929
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
1930
+ busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: c.fg(theme2.color.live) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(glyph.color), children: glyph.glyph }),
1931
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(selected ? theme2.color.accent : theme2.color.muted), bold: selected, children: [
1932
+ " ",
1933
+ name
1934
+ ] }),
1935
+ stateText ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(labelColor), children: [
1936
+ " ",
1937
+ stateText
1938
+ ] }) : null
1939
+ ] });
1940
+ }
1941
+ function LoopRow({ row: row4, selected, focused, width, nextRun }) {
1942
+ let theme2 = useTheme(), { loop, agent: agent2 } = row4, d = describeLoop(agent2, loop), c = rowColors(theme2, selected, focused), hint = d.kind === "running" ? d.label : d.kind === "disabled" ? "off" : nextRun !== void 0 ? formatIn(nextRun) : "", nameRoom = Math.max(3, width - 5 - (hint ? displayWidth(hint) + 1 : 0)), name = truncate(loop.info.name, nameRoom), fill = Math.max(0, width - 5 - displayWidth(name) - (hint ? displayWidth(hint) + 1 : 0)), nameColor = loop.info.enabled ? loop.info.isMain ? theme2.color.accent : theme2.color.loop : theme2.color.dim;
1943
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { backgroundColor: c.bg, inverse: c.inverse, underline: c.underline, wrap: "truncate-end", children: [
1944
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }),
1945
+ d.kind === "running" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, { color: c.fg(theme2.color.loop) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(d.kind === "disabled" ? theme2.color.dim : theme2.color.muted), children: d.kind === "disabled" ? "-" : theme2.glyph.ring }),
1946
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(nameColor), children: [
1947
+ " ",
1948
+ theme2.glyph.loop
1949
+ ] }),
1950
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { color: c.fg(nameColor), bold: selected, children: name }),
1951
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " ".repeat(fill) }),
1952
+ hint ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: c.fg(d.kind === "running" ? theme2.color.live : theme2.color.muted), children: [
1953
+ " ",
1954
+ hint
1955
+ ] }) : null
1956
+ ] });
1742
1957
  }
1743
1958
 
1744
1959
  // src/main/tui/app/StatusBar.tsx
@@ -1919,6 +2134,8 @@ function insertIntoPrompt(text) {
1919
2134
  return promptApi.current ? (promptApi.current.insert(text), !0) : !1;
1920
2135
  }
1921
2136
  function slashEnterAction(value, command) {
2137
+ if (command.agentCommand && command.agentCommand !== "*")
2138
+ return command.args?.includes("<") && !hasCommandArguments(parseSlash(value)?.rest ?? "") ? { insert: `${value} ` } : { run: value };
1922
2139
  if (/[\\/]$/.test(value)) return { insert: value };
1923
2140
  let required = command.args?.match(/<[^>]+>/g)?.length ?? 0;
1924
2141
  return (parseSlash(value)?.args.length ?? 0) >= required ? { run: value } : { insert: `${value} ` };
@@ -2010,39 +2227,45 @@ function Prompt({ width, focused }) {
2010
2227
  ] }) : null
2011
2228
  ] }, item.key)) }) : null,
2012
2229
  badge ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ComposerTop, { width, focused, badge }) : null,
2013
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { borderStyle: theme2.ascii ? "classic" : "round", borderTop: !badge, borderColor: focused ? theme2.color.borderFocus : theme2.color.border, paddingX: 1, width, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2014
- TextInput,
2015
- {
2016
- value,
2017
- onChange: (next) => {
2018
- setValue(next), setMenuIndex(0), setDismissed(!1);
2019
- },
2020
- onSubmit: submit,
2021
- placeholder: placeholder3,
2022
- history,
2023
- focused,
2024
- apiRef: promptApi,
2025
- onKey: (_input, key, api) => {
2026
- if (!menuOpen) return !1;
2027
- if (key.escape)
2028
- return setDismissed(!0), !0;
2029
- if (key.upArrow)
2030
- return setMenuIndex((i) => (i - 1 + menu.length) % menu.length), !0;
2031
- if (key.downArrow)
2032
- return setMenuIndex((i) => (i + 1) % menu.length), !0;
2033
- if (key.tab) {
2034
- let next = menu[highlighted].accept();
2035
- return api.setValue(next.value, next.cursor), !0;
2036
- }
2037
- if (key.return && !key.shift && !key.meta) {
2038
- if (api.value !== value) return !1;
2039
- let item = menu[highlighted], outcome = menuEnter(api.value, item.accept(), item.command);
2040
- return "insert" in outcome ? (api.setValue(outcome.insert, outcome.cursor), !0) : (api.setValue(outcome.submit), !1);
2230
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { borderStyle: theme2.ascii ? "classic" : "round", borderTop: !badge, borderColor: focused ? theme2.color.borderFocus : theme2.color.border, paddingX: 1, width, flexDirection: "column", children: [
2231
+ isAuthorizedCommandInput(value, registry) && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { color: theme2.color.warn, wrap: "truncate-end", children: [
2232
+ theme2.glyph.warn,
2233
+ " Authorized run"
2234
+ ] }),
2235
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2236
+ TextInput,
2237
+ {
2238
+ value,
2239
+ onChange: (next) => {
2240
+ setValue(next), setMenuIndex(0), setDismissed(!1);
2241
+ },
2242
+ onSubmit: submit,
2243
+ placeholder: placeholder3,
2244
+ history,
2245
+ focused,
2246
+ apiRef: promptApi,
2247
+ onKey: (_input, key, api) => {
2248
+ if (!menuOpen) return !1;
2249
+ if (key.escape)
2250
+ return setDismissed(!0), !0;
2251
+ if (key.upArrow)
2252
+ return setMenuIndex((i) => (i - 1 + menu.length) % menu.length), !0;
2253
+ if (key.downArrow)
2254
+ return setMenuIndex((i) => (i + 1) % menu.length), !0;
2255
+ if (key.tab) {
2256
+ let next = menu[highlighted].accept();
2257
+ return api.setValue(next.value, next.cursor), !0;
2258
+ }
2259
+ if (key.return && !key.shift && !key.meta) {
2260
+ if (api.value !== value) return !1;
2261
+ let item = menu[highlighted], outcome = menuEnter(api.value, item.accept(), item.command);
2262
+ return "insert" in outcome ? (api.setValue(outcome.insert, outcome.cursor), !0) : (api.setValue(outcome.submit), !1);
2263
+ }
2264
+ return !1;
2041
2265
  }
2042
- return !1;
2043
2266
  }
2044
- }
2045
- ) })
2267
+ )
2268
+ ] })
2046
2269
  ] });
2047
2270
  }
2048
2271
 
@@ -3703,7 +3926,7 @@ function itemHeight(item, width, flags) {
3703
3926
  case "notice":
3704
3927
  return 1;
3705
3928
  case "context":
3706
- return flags.expanded ? 1 + wrappedHeight(item.text || " ", w - 2) + 1 : 1;
3929
+ return flags.expanded ? 1 + wrappedHeight(item.text || (item.ownerCommand?.status === "running" ? "Running\u2026" : item.ownerCommand ? "(empty)" : " "), w - 2) + (item.ownerCommand?.status === "denied" ? wrappedHeight("Add !authorize to allow restricted calls for one run.", w - 2) : 0) + 1 : 1;
3707
3930
  case "error":
3708
3931
  return wrappedHeight(item.text, w - 2) + 1;
3709
3932
  }
@@ -3745,6 +3968,9 @@ ${answer}` : askQuestion(item.input);
3745
3968
  return item.answer ? `${item.question}
3746
3969
 
3747
3970
  ${item.answer}` : item.question;
3971
+ case "context":
3972
+ return item.ownerCommand ? `${item.ownerCommand.command} [${item.ownerCommand.status}${item.ownerCommand.authorized ? ", authorized" : ""}]
3973
+ ${item.text}` : item.text;
3748
3974
  default:
3749
3975
  return item.text;
3750
3976
  }
@@ -5558,7 +5784,7 @@ async function editText(text, options = {}) {
5558
5784
  var schemaParse = null;
5559
5785
  async function loadSchema() {
5560
5786
  if (schemaParse) return schemaParse;
5561
- let schema = cjs(await import("./adf-schema-YJSGLMA4.mjs")).AgentConfigSchema;
5787
+ let schema = cjs(await import("./adf-schema-AIPBQTR2.mjs")).AgentConfigSchema;
5562
5788
  return schemaParse = (value) => schema.safeParse(value), schemaParse;
5563
5789
  }
5564
5790
  function changedKeysBetween(a, b) {
@@ -5807,8 +6033,8 @@ function TemplatesDialog({ overlay, close: closeOverlay, width, height }) {
5807
6033
  }
5808
6034
  await act("Save notes", { kind: "notes", id }, () => store.client.updateTemplate(id, { description, warning })) && (await refresh2(id), done(`Notes of ${label2(id)} saved`, { kind: "detail", id }));
5809
6035
  }, edit = async (id, what, back) => {
5810
- let suspend = (run) => suspendTerminal(async () => {
5811
- await run();
6036
+ let suspend = (run2) => suspendTerminal(async () => {
6037
+ await run2();
5812
6038
  });
5813
6039
  (what === "instructions" ? await editTemplateInstructions(store.actions, suspend, id) : what === "config" ? await editTemplateConfig(store.actions, suspend, id) : await editTemplateSeedFile(store.actions, suspend, id, what)) === "saved" && await refresh2(id), setStep(back);
5814
6040
  }, removeExtra = async (id, path) => {
@@ -6042,156 +6268,6 @@ function TemplatesDialog({ overlay, close: closeOverlay, width, height }) {
6042
6268
  // src/main/tui/skills/SkillsDialog.tsx
6043
6269
  var import_react23 = __toESM(require_react(), 1);
6044
6270
 
6045
- // src/shared/utils/skills-panel.ts
6046
- var skills_panel_exports = {};
6047
- __export(skills_panel_exports, {
6048
- MAX_CATALOG_SOURCES: () => MAX_CATALOG_SOURCES,
6049
- MAX_REGISTRY_BYTES: () => MAX_REGISTRY_BYTES,
6050
- MAX_SKILLS: () => MAX_SKILLS,
6051
- MAX_SKILL_FILE_BYTES: () => MAX_SKILL_FILE_BYTES,
6052
- SKILLS_REGISTRY_PATH: () => SKILLS_REGISTRY_PATH,
6053
- SKILLS_STATE_PATH: () => SKILLS_STATE_PATH,
6054
- SKILL_MANIFEST: () => SKILL_MANIFEST,
6055
- addCatalogSource: () => addCatalogSource,
6056
- catalogSourceLabel: () => catalogSourceLabel,
6057
- estimateTokens: () => estimateTokens,
6058
- filterCatalogEntries: () => filterCatalogEntries,
6059
- installedSkillNames: () => installedSkillNames,
6060
- isCatalogUrl: () => isCatalogUrl,
6061
- mergeCatalogResults: () => mergeCatalogResults,
6062
- mergeDisabledList: () => mergeDisabledList,
6063
- normalizeCatalogSources: () => normalizeCatalogSources,
6064
- parseSkillsRegistry: () => parseSkillsRegistry,
6065
- sanitizeDisplayText: () => sanitizeDisplayText,
6066
- skillProblems: () => skillProblems,
6067
- unindexedHint: () => unindexedHint
6068
- });
6069
- var SKILLS_REGISTRY_PATH = "skills-registry.json", SKILLS_STATE_PATH = "skills-state.json", SKILL_MANIFEST = /^skills\/([^/]+)\/SKILL\.md$/, MAX_SKILLS = 48, MAX_REGISTRY_BYTES = 32 * 1024, MAX_SKILL_FILE_BYTES = 256 * 1024;
6070
- function parseSkillsRegistry(text) {
6071
- if (!text) return null;
6072
- try {
6073
- let doc = JSON.parse(text);
6074
- if (doc?.schema !== 1 || typeof doc.skills != "object" || doc.skills === null) return null;
6075
- let entries = [];
6076
- for (let value of Object.values(doc.skills)) {
6077
- let skill = value;
6078
- typeof skill?.name != "string" || typeof skill?.path != "string" || entries.push({
6079
- name: skill.name,
6080
- description: typeof skill.description == "string" ? skill.description : void 0,
6081
- path: skill.path,
6082
- enabled: skill.enabled !== !1
6083
- });
6084
- }
6085
- let rejected = [];
6086
- if (Array.isArray(doc.rejected))
6087
- for (let raw of doc.rejected) {
6088
- let row4 = raw;
6089
- typeof row4?.path != "string" || typeof row4?.reason != "string" || rejected.push({ path: row4.path, reason: row4.reason, name: SKILL_MANIFEST.exec(row4.path)?.[1] ?? null });
6090
- }
6091
- return { entries: entries.sort((a, b) => a.name.localeCompare(b.name)), rejected };
6092
- } catch {
6093
- return null;
6094
- }
6095
- }
6096
- function mergeDisabledList(existingText, name, enabled) {
6097
- let doc = {};
6098
- if (existingText)
6099
- try {
6100
- let parsed = JSON.parse(existingText);
6101
- parsed && typeof parsed == "object" && !Array.isArray(parsed) && (doc = parsed);
6102
- } catch {
6103
- }
6104
- let current = Array.isArray(doc.disabled) ? doc.disabled.filter((entry) => typeof entry == "string") : [], next = new Set(current);
6105
- return enabled ? next.delete(name) : next.add(name), JSON.stringify({ ...doc, schema: 1, disabled: [...next].sort() }, null, 2) + `
6106
- `;
6107
- }
6108
- var UNSAFE_DISPLAY_CHARS = /[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/g;
6109
- function sanitizeDisplayText(value) {
6110
- return value ? value.replace(UNSAFE_DISPLAY_CHARS, " ").replace(/\s+/g, " ").trim() : "";
6111
- }
6112
- function isCatalogUrl(value) {
6113
- let trimmed = value.trim();
6114
- if (!trimmed) return !1;
6115
- try {
6116
- return new URL(trimmed).protocol === "https:";
6117
- } catch {
6118
- return !1;
6119
- }
6120
- }
6121
- function estimateTokens(bytes) {
6122
- return Math.ceil(bytes / 4);
6123
- }
6124
- var MAX_CATALOG_SOURCES = 8;
6125
- function normalizeCatalogSources(stored) {
6126
- if (!Array.isArray(stored)) return [ADF_SKILLS_REGISTRY_URL];
6127
- let sources2 = [], seen = /* @__PURE__ */ new Set();
6128
- for (let raw of stored) {
6129
- if (typeof raw != "string") continue;
6130
- let url2 = raw.trim();
6131
- if (!(!isCatalogUrl(url2) || seen.has(url2)) && (seen.add(url2), sources2.push(url2), sources2.length >= MAX_CATALOG_SOURCES))
6132
- break;
6133
- }
6134
- return sources2;
6135
- }
6136
- function addCatalogSource(sources2, raw) {
6137
- let url2 = raw.trim();
6138
- return url2 ? isCatalogUrl(url2) ? sources2.includes(url2) ? { ok: !1, error: "That source is already listed." } : sources2.length >= MAX_CATALOG_SOURCES ? { ok: !1, error: `At most ${MAX_CATALOG_SOURCES} catalog sources.` } : { ok: !0, sources: [...sources2, url2] } : { ok: !1, error: "A catalog source must be an https:// URL." } : { ok: !1, error: "Enter a catalog URL." };
6139
- }
6140
- function catalogSourceLabel(url2, publisher) {
6141
- let named = sanitizeDisplayText(publisher);
6142
- if (named) return named;
6143
- try {
6144
- return new URL(url2).hostname;
6145
- } catch {
6146
- return url2;
6147
- }
6148
- }
6149
- function mergeCatalogResults(results) {
6150
- let merged = [], seen = /* @__PURE__ */ new Set();
6151
- for (let result of results) {
6152
- if (!result.ok) continue;
6153
- let sourceLabel = catalogSourceLabel(result.url, result.publisher);
6154
- for (let entry of result.entries)
6155
- seen.has(entry.name) || (seen.add(entry.name), merged.push({ ...entry, sourceUrl: result.url, sourceLabel }));
6156
- }
6157
- return merged.sort((a, b) => a.name.localeCompare(b.name));
6158
- }
6159
- function filterCatalogEntries(entries, query) {
6160
- let needle = query.trim().toLowerCase();
6161
- if (!needle) return entries;
6162
- let byName2 = [], byDescription = [];
6163
- for (let entry of entries)
6164
- entry.name.toLowerCase().includes(needle) ? byName2.push(entry) : entry.description.toLowerCase().includes(needle) && byDescription.push(entry);
6165
- return [...byName2, ...byDescription];
6166
- }
6167
- function installedSkillNames(files2) {
6168
- let names = /* @__PURE__ */ new Set();
6169
- for (let file of files2) {
6170
- let match = SKILL_MANIFEST.exec(file.path);
6171
- match && names.add(match[1]);
6172
- }
6173
- return names;
6174
- }
6175
- function skillProblems(installedNames, registry) {
6176
- if (!registry) return [];
6177
- let indexed = new Set(registry.entries.map((s) => s.name)), rows = /* @__PURE__ */ new Map();
6178
- for (let name of installedNames)
6179
- indexed.has(name) || rows.set(name, { label: name, path: `skills/${name}/SKILL.md`, reason: null, isPackage: !0 });
6180
- for (let rejection of registry.rejected) {
6181
- let key = rejection.name ?? rejection.path;
6182
- rejection.name && indexed.has(rejection.name) || rows.set(key, {
6183
- label: rejection.name ?? rejection.path,
6184
- path: rejection.path,
6185
- reason: rejection.reason,
6186
- isPackage: rejection.name !== null
6187
- });
6188
- }
6189
- return [...rows.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([key, row4]) => ({ key, ...row4 }));
6190
- }
6191
- function unindexedHint() {
6192
- return `not indexed \u2014 the folder name must be lowercase kebab-case and match the frontmatter name, description must be one line under 500 characters, and the file must be under ${Math.round(MAX_SKILL_FILE_BYTES / 1024)} KB. The catalog also stops at ${MAX_SKILLS} skills or ${Math.round(MAX_REGISTRY_BYTES / 1024)} KB, whichever comes first.`;
6193
- }
6194
-
6195
6271
  // src/shared/utils/skill-preview.ts
6196
6272
  var skill_preview_exports = {};
6197
6273
  __export(skill_preview_exports, {
@@ -6716,7 +6792,7 @@ var {
6716
6792
  installedSkillNames: installedSkillNames2,
6717
6793
  isCatalogUrl: isCatalogUrl2,
6718
6794
  parseSkillsRegistry: parseSkillsRegistry2,
6719
- sanitizeDisplayText: sanitizeDisplayText2,
6795
+ sanitizeDisplayText: sanitizeDisplayText3,
6720
6796
  skillProblems: skillProblems2
6721
6797
  } = cjs(skills_panel_exports), { splitSkillDocument: splitSkillDocument2 } = cjs(skill_preview_exports), { SKILL_NAME } = cjs(skills_catalog_schema_exports), SKILLS_OVERLAY = "skills", looksLikePath = (value) => /^(\.{1,2}([\\/]|$)|[\\/]|~([\\/]|$)|[A-Za-z]:[\\/])/.test(value) || /[\\/]/.test(value) || /\.md$/i.test(value);
6722
6798
  function parseAddTarget2(raw) {
@@ -6769,7 +6845,7 @@ function settleOverrides(overrides, registryText) {
6769
6845
  function tokenLabel(tokens) {
6770
6846
  return tokens == null ? "" : tokens >= 1e3 ? `~${(tokens / 1e3).toFixed(tokens >= 1e4 ? 0 : 1)}k tok` : `~${tokens} tok`;
6771
6847
  }
6772
- var safe = (value) => sanitizeDisplayText2(value), packageKey = (source) => source.type === "catalog" ? `catalog:${source.entry.sourceUrl}#${source.entry.name}` : source.type === "url" ? `url:${source.url}` : `disk:${source.path}`, frontmatterField = (manifest, key) => splitSkillDocument2(manifest).fields.find((f) => f.key === key)?.value, isSkillName = (value) => !!value && SKILL_NAME.test(value);
6848
+ var safe = (value) => sanitizeDisplayText3(value), packageKey = (source) => source.type === "catalog" ? `catalog:${source.entry.sourceUrl}#${source.entry.name}` : source.type === "url" ? `url:${source.url}` : `disk:${source.path}`, frontmatterField = (manifest, key) => splitSkillDocument2(manifest).fields.find((f) => f.key === key)?.value, isSkillName = (value) => !!value && SKILL_NAME.test(value);
6773
6849
  function manifestProblem(manifest) {
6774
6850
  return Buffer.byteLength(manifest, "utf-8") > MAX_SKILL_FILE_BYTES2 ? `SKILL.md is over ${Math.round(MAX_SKILL_FILE_BYTES2 / 1024)} KB, the indexer would reject it` : manifest.slice(0, 8192).includes("\0") ? "SKILL.md is not text" : null;
6775
6851
  }
@@ -6869,10 +6945,10 @@ async function readSkillFile(client, agentId, path) {
6869
6945
  }
6870
6946
  var stateQueues = /* @__PURE__ */ new Map();
6871
6947
  function enqueue(agentId, task) {
6872
- let run = (stateQueues.get(agentId) ?? Promise.resolve()).then(task, task);
6873
- return stateQueues.set(agentId, run.then(() => {
6948
+ let run2 = (stateQueues.get(agentId) ?? Promise.resolve()).then(task, task);
6949
+ return stateQueues.set(agentId, run2.then(() => {
6874
6950
  }, () => {
6875
- })), run;
6951
+ })), run2;
6876
6952
  }
6877
6953
  function setSkillMuted(client, agentId, name, enabled) {
6878
6954
  return enqueue(agentId, async () => {
@@ -7127,7 +7203,7 @@ function SkillsDialog({ overlay, close, width, height }) {
7127
7203
  actions: store.actions,
7128
7204
  agentId,
7129
7205
  agentLabel: who,
7130
- suspend: (run) => suspendTerminal(run)
7206
+ suspend: (run2) => suspendTerminal(run2)
7131
7207
  }, { kind: "file", path }), bump();
7132
7208
  }, install = async (pkg2, key) => {
7133
7209
  if (read().installs[key]?.status === "installing") return;
@@ -10106,6 +10182,34 @@ function ItemBody({ item, width, expanded, showThinking, queued }) {
10106
10182
  item.text
10107
10183
  ] });
10108
10184
  case "context": {
10185
+ if (item.ownerCommand) {
10186
+ let command = item.ownerCommand, tone = command.status === "running" ? theme2.color.tool : command.status === "ok" ? theme2.color.success : theme2.color.error;
10187
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", width: w, children: [
10188
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", children: [
10189
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: tone, children: command.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Spinner, { color: tone }) : command.status === "ok" ? g.check : g.cross }),
10190
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { bold: !0, color: theme2.color.tool, children: [
10191
+ " You ran ",
10192
+ command.command
10193
+ ] }),
10194
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { color: tone, children: [
10195
+ " [",
10196
+ command.status === "denied" ? "permission denied" : command.status,
10197
+ "]"
10198
+ ] }),
10199
+ command.authorized && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: theme2.color.warn, children: " [authorized]" }),
10200
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { color: theme2.color.dim, children: [
10201
+ " ",
10202
+ item.unrecorded ? "[not recorded]" : item.deferred ? "[recording after current step]" : "",
10203
+ " ",
10204
+ expanded ? g.expanded : `${g.collapsed} Enter`
10205
+ ] })
10206
+ ] }),
10207
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
10208
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { wrap: "wrap", color: theme2.color.muted, children: item.text || (command.status === "running" ? "Running\u2026" : "(empty)") }),
10209
+ command.status === "denied" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { wrap: "wrap", color: theme2.color.warn, children: "Add !authorize to allow restricted calls for one run." })
10210
+ ] })
10211
+ ] });
10212
+ }
10109
10213
  let label2 = item.category === "compaction" ? "compacted history summary" : `context ${g.sep} ${item.category}`;
10110
10214
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", width: w, children: [
10111
10215
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.dim, children: [
@@ -10597,6 +10701,7 @@ function stepLoop(ctx, delta) {
10597
10701
  }
10598
10702
  var chatCommands = {
10599
10703
  commands: [
10704
+ agentCommand,
10600
10705
  {
10601
10706
  name: "interrupt",
10602
10707
  aliases: ["abort"],
@@ -11753,7 +11858,7 @@ function FilesTab(props) {
11753
11858
  actions: actions4,
11754
11859
  agentId,
11755
11860
  agentLabel: agentLabel5,
11756
- suspend: (run) => suspendTerminal(run)
11861
+ suspend: (run2) => suspendTerminal(run2)
11757
11862
  }, target2, { create }) === "written") {
11758
11863
  let key = targetKey(target2);
11759
11864
  invalidate(key), await reloadList(), reveal(key);
@@ -12410,8 +12515,8 @@ var { CronExpressionParser } = cjs(cronParserNs), DEFAULT_NEW_LOOP_TOOLS = ["loo
12410
12515
  function validateLoopName(name, existing = []) {
12411
12516
  return name ? name === MAIN_LOOP ? '"main" is the implicit host loop' : LOOP_NAME_PATTERN.test(name) ? existing.includes(name) ? `A loop named "${name}" already exists` : null : "1-32 chars: lowercase letters, digits, _ or -, starting with a letter or digit" : "Name is required";
12412
12517
  }
12413
- function validateGoal(goal) {
12414
- return goal.trim() ? goal.length > LOOP_GOAL_MAX_CHARS ? `Goal is ${goal.length} chars; the limit is ${LOOP_GOAL_MAX_CHARS}` : null : "Goal is required: it becomes the loop's whole system instruction";
12518
+ function validateGoal(goal, autostart = !1) {
12519
+ return !goal.trim() && autostart ? "Autostart needs a goal: add one or turn autostart off" : goal.length > LOOP_GOAL_MAX_CHARS ? `Goal is ${goal.length} chars; the limit is ${LOOP_GOAL_MAX_CHARS}` : null;
12415
12520
  }
12416
12521
  function hostLoopTools(config2) {
12417
12522
  if (!config2 || !Array.isArray(config2.tools)) return null;
@@ -13315,12 +13420,12 @@ function draftFromValues(v) {
13315
13420
  function LoopWizard({ overlay, close, width, height }) {
13316
13421
  let props = overlay.props ?? {}, theme2 = useTheme(), store = useStore(), actions4 = useActions(), agent2 = useAgent(props.agentId), loops2 = useLoops(props.agentId) ?? [], timers = useAgentTimers(props.agentId), { suspendTerminal } = use_app_default(), config2 = agent2?.config, host = hostLoopTools(config2), existing = props.mode === "edit" ? loops2.find((l) => l.info.name === props.name)?.info.config ?? void 0 : void 0, provider = config2?.model?.provider, models = useModelList(props.agentId, provider), takenNames = loops2.map((l) => l.info.name), initialTemplate = props.mode === "create" ? findTemplate(props.template) : void 0, [step, setStep] = (0, import_react43.useState)(props.mode === "create" && !initialTemplate ? "template" : "form"), [template, setTemplate] = (0, import_react43.useState)(initialTemplate), [values, setValues] = (0, import_react43.useState)(() => initialValues2(props, existing, initialTemplate, host, takenNames)), [touched, setTouched] = (0, import_react43.useState)(!1), [outcome, setOutcome] = (0, import_react43.useState)(null), dialogWidth = Math.max(40, Math.min(width - 4, 96)), inner = dialogWidth - 4, bodyRows = Math.max(6, height - 8), label2 = agent2 ? agentName3(store.getState(), props.agentId) : props.agentId, wanted = Array.isArray(values.tools) ? values.tools : [], options = toolOptions(host, [...wanted, ...existing?.tools ?? [], ...template?.tools ?? []]), draft = draftFromValues(values), loopName = props.mode === "edit" ? props.name ?? "" : String(values.name ?? ""), schedule = scheduleToTimer(draft, loopName), existingTimers = timers.data ? timersForLoop(timers.data, loopName) : [], errors = (0, import_react43.useMemo)(() => {
13317
13422
  let e = {};
13318
- props.mode === "create" && (e.name = validateLoopName(String(values.name ?? ""), takenNames) ?? void 0), e.goal = validateGoal(String(values.goal ?? "")) ?? void 0;
13423
+ props.mode === "create" && (e.name = validateLoopName(String(values.name ?? ""), takenNames) ?? void 0), e.goal = validateGoal(String(values.goal ?? ""), !!values.autostart) ?? void 0;
13319
13424
  let compact2 = String(values.compact ?? "").trim();
13320
13425
  return compact2 && !(Number.isInteger(Number(compact2)) && Number(compact2) > 0) && (e.compact = "Whole number of tokens, or empty to inherit"), String(values.model ?? "").trim() && !String(values.provider ?? "").trim() && (e.provider = "Provider id is required with a model"), schedule.error && (e.scheduleValue = schedule.error), e;
13321
13426
  }, [values, schedule.error, props.mode, existing]), hasErrors = Object.values(errors).some(Boolean), fields = [
13322
13427
  { kind: "text", key: "name", label: "Name", placeholder: "e.g. consolidator", readOnly: props.mode === "edit", hint: "lowercase letters, digits, _ -; cannot be renamed later" },
13323
- { kind: "text", key: "goal", label: "Goal", multiline: !0, rows: 5, editor: !0, placeholder: "What this loop works on. Becomes its whole system instruction.", hint: "Enter newline \xB7 Ctrl+O open in $EDITOR \xB7 \u2193 next field" },
13428
+ { kind: "text", key: "goal", label: "Goal", multiline: !0, rows: 5, editor: !0, placeholder: "Optional. What this loop works on; added to its instructions.", hint: "Enter newline \xB7 Ctrl+O open in $EDITOR \xB7 \u2193 next field" },
13324
13429
  { kind: "checklist", key: "tools", label: "Tools", rows: 7, options: options.map((o) => ({ value: o.name, note: o.unavailable ? "not granted by host" : void 0 })), hint: host ? "restricted to tools the host has enabled; loop_compact/loop_clear are added by the runtime" : "host tool list unknown: the daemon validates on save" },
13325
13430
  { kind: "combo", key: "model", label: "Model", options: models.data?.models ?? [], placeholder: `inherit (${config2?.model?.model_id ?? "host model"})`, hint: models.data?.error ? `models: ${models.data.error}` : `\u2190\u2192 pick a ${provider ?? ""} model, type any id, empty = inherit` },
13326
13431
  { kind: "text", key: "provider", label: "Provider", hidden: !String(values.model ?? "").trim(), hint: "provider id for the override (another configured provider is allowed)" },
@@ -13346,10 +13451,10 @@ function LoopWizard({ overlay, close, width, height }) {
13346
13451
  }
13347
13452
  setStep("review");
13348
13453
  }, createInput = () => {
13349
- let model2 = String(values.model ?? "").trim(), compact2 = String(values.compact ?? "").trim();
13454
+ let model2 = String(values.model ?? "").trim(), compact2 = String(values.compact ?? "").trim(), goal = String(values.goal ?? "").trim();
13350
13455
  return {
13351
13456
  name: String(values.name),
13352
- goal: String(values.goal).trim(),
13457
+ ...goal ? { goal } : {},
13353
13458
  tools: wanted,
13354
13459
  enabled: !!values.enabled,
13355
13460
  autostart: !!values.autostart,
@@ -13459,7 +13564,7 @@ function initialValues2(props, existing, template, host, taken) {
13459
13564
  if (props.mode === "edit" && existing)
13460
13565
  return {
13461
13566
  name: existing.name,
13462
- goal: existing.goal,
13567
+ goal: existing.goal ?? "",
13463
13568
  tools: [...existing.tools ?? []],
13464
13569
  model: existing.model?.model_id ?? "",
13465
13570
  provider: existing.model?.provider ?? "",
@@ -13513,7 +13618,7 @@ function TemplatePicker({ width, height, onPick, onCancel }) {
13513
13618
  );
13514
13619
  }
13515
13620
  function CreateReview({ input, schedulePreview, width, rows }) {
13516
- let theme2 = useTheme(), goalLines = input.goal.split(`
13621
+ let theme2 = useTheme(), goalLines = (input.goal || "(none)").split(`
13517
13622
  `), goalRows = Math.max(2, Math.min(goalLines.length, rows - 12)), line = (k, v) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(Text, { wrap: "truncate-end", children: [
13518
13623
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(Text, { color: theme2.color.muted, children: k.padEnd(14) }),
13519
13624
  /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(Text, { color: theme2.color.text, children: truncate(v, width - 14) })
@@ -14824,8 +14929,8 @@ function settingRows(config2, extras = {}) {
14824
14929
 
14825
14930
  // src/main/tui/views/inspect/settings-ops.ts
14826
14931
  var queue = Promise.resolve();
14827
- function serial(run) {
14828
- let next = queue.then(run, run);
14932
+ function serial(run2) {
14933
+ let next = queue.then(run2, run2);
14829
14934
  return queue = next.catch(() => {
14830
14935
  }), next;
14831
14936
  }
@@ -16836,9 +16941,9 @@ function IdentityTab2({ width, height, focused }) {
16836
16941
  ] });
16837
16942
  }
16838
16943
  function NetworkTab(props) {
16839
- let store = useStore(), actions4 = useActions(), ask = async (title, message, run, done, reload) => {
16944
+ let store = useStore(), actions4 = useActions(), ask = async (title, message, run2, done, reload) => {
16840
16945
  if (!await actions4.confirm({ title, message, danger: !0 })) return;
16841
- await actions4.run(title, run) !== void 0 && actions4.toast(done, "success"), reload();
16946
+ await actions4.run(title, run2) !== void 0 && actions4.toast(done, "success"), reload();
16842
16947
  };
16843
16948
  return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
16844
16949
  ReportTab,