@zenera/cli 1.1.27 → 1.1.29

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 (51) hide show
  1. package/README.md +127 -75
  2. package/dist/audit.d.ts +2 -2
  3. package/dist/audit.js +11 -4
  4. package/dist/candidates.d.ts +9 -0
  5. package/dist/candidates.js +17 -0
  6. package/dist/catalog.d.ts +10 -5
  7. package/dist/catalog.js +93 -12
  8. package/dist/commands/completion.d.ts +9 -0
  9. package/dist/commands/completion.js +70 -0
  10. package/dist/commands/index.js +2 -0
  11. package/dist/commands/init.js +1 -1
  12. package/dist/commands/key.js +142 -11
  13. package/dist/commands/meta.d.ts +3 -0
  14. package/dist/commands/meta.js +235 -62
  15. package/dist/commands/models.d.ts +12 -0
  16. package/dist/commands/models.js +79 -112
  17. package/dist/completion-install.d.ts +16 -0
  18. package/dist/completion-install.js +85 -0
  19. package/dist/completion.d.ts +9 -0
  20. package/dist/completion.js +93 -0
  21. package/dist/keys.d.ts +27 -4
  22. package/dist/keys.js +63 -3
  23. package/dist/liveness.js +73 -3
  24. package/dist/main.js +22 -0
  25. package/dist/meta/model.d.ts +1 -1
  26. package/dist/meta/model.js +1 -1
  27. package/dist/postinstall.d.ts +2 -0
  28. package/dist/postinstall.js +10 -0
  29. package/dist/sandbox.js +4 -3
  30. package/dist/term.d.ts +2 -0
  31. package/dist/term.js +2 -0
  32. package/dist/tui/links.d.ts +2 -0
  33. package/dist/tui/links.js +31 -7
  34. package/dist/tui/models.d.ts +8 -1
  35. package/dist/tui/models.js +25 -18
  36. package/dist/validate.js +2 -2
  37. package/package.json +8 -6
  38. package/postinstall.mjs +7 -0
  39. package/templates/completions/zen.bash +16 -0
  40. package/templates/completions/zen.fish +22 -0
  41. package/templates/completions/zen.zsh +28 -0
  42. package/templates/editor/.github/copilot-instructions.md +5 -4
  43. package/templates/editor/.github/prompts/spec-edit.prompt.md +250 -0
  44. package/templates/editor/.github/skills/zen-analyze-run/SKILL.md +5 -0
  45. package/templates/editor/.github/skills/zen-cli/SKILL.md +2 -2
  46. package/templates/editor/.github/skills/zen-cli/references/keys.md +27 -2
  47. package/templates/editor/.github/skills/zen-cli/references/models.md +22 -23
  48. package/dist/meta/finetune/guard.d.ts +0 -29
  49. package/dist/meta/finetune/guard.js +0 -104
  50. package/dist/meta.d.ts +0 -291
  51. package/dist/meta.js +0 -981
@@ -5,17 +5,20 @@ import { existsSync, readFileSync } from 'node:fs';
5
5
  import { join, resolve } from 'node:path';
6
6
  import { setTimeout as sleep } from 'node:timers/promises';
7
7
  import { parse } from "../args.js";
8
+ import { matching, projectNames } from "../candidates.js";
8
9
  import { loadProjectEnv } from "../env.js";
9
10
  import { copilotEventLog } from "../eventlog.js";
10
11
  import { ensureHome } from "../home.js";
11
- import { assertOwner, KeyStore, PROVIDERS } from "../keys.js";
12
+ import { assertOwner, isProvider, KeyStore, PROVIDERS } from "../keys.js";
12
13
  import { probeChat, probeModel } from "../liveness.js";
13
- import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, RECOMMENDED, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, writeMeta, } from "../meta/index.js";
14
+ import { answerBox, chooseModel, DATASET_DETAILS, DATASET_SUMMARY, DATASET_USAGE, datasetProjectFlag, DEFAULT_RESUMES, defaultRef, editorFiles, FINETUNE_DETAILS, FINETUNE_SUMMARY, FINETUNE_USAGE, finetuneProjectFlag, lastSession, launch, listPrompts, loadPrompt, locate, masked, misspelledProvider, MODEL_ENV, openLog, ORDER, PROMPT_DIR, providersWarning, readMeta, recordSession, RESUME_PROMPT, resumeDelayMs, runDataset, runFinetune, sameModel, sessionModel, sessionTotals, SOURCE_LABELS, startVertexRelay, tailSpans, Tally, terminalSink, transient, wire, writeMeta, } from "../meta/index.js";
14
15
  import { splitRef } from "../modelref.js";
15
16
  import { duration } from "../narrate.js";
16
17
  import * as Projects from "../projects.js";
17
18
  import { project as resolveProject } from "../resolve.js";
18
- import { bold, choose, CliError, credentialError, cyan, dim, EXIT, green, isInteractive, json, note, progress, readStdin, table, usageError, warn, write, writeAll, } from "../term.js";
19
+ import { TEMPLATES } from "../scaffold.js";
20
+ import { bold, choose, CliError, credentialError, cyan, dim, EXIT, green, isInteractive, json, note, pad, progress, readStdin, table, usageError, warn, write, writeAll, } from "../term.js";
21
+ import { wrap } from "../tui/wrap.js";
19
22
  import { appendUsage, LEDGER_ENV, ledgerPath, META_PROMPT_ENV, META_SESSION_ENV, } from "../usage.js";
20
23
  const USAGE = 'zen meta run [project] [prompt] [options]';
21
24
  const VERBS = new Set(['model', 'run', 'prompts', 'resume', 'dataset', 'finetune']);
@@ -55,15 +58,76 @@ function page(p) {
55
58
  write(line ? ` ${line}` : '');
56
59
  }
57
60
  }
61
+ /** Kept apart from `run` so that shell completion offers exactly what is parsed. */
62
+ const OPTIONS = {
63
+ project: { type: 'string' },
64
+ provider: { type: 'string' },
65
+ model: { type: 'string' },
66
+ prompt: { type: 'string', short: 'p' },
67
+ 'prompt-file': { type: 'string' },
68
+ agent: { type: 'string' },
69
+ effort: { type: 'string' },
70
+ share: { type: 'string' },
71
+ events: { type: 'string' },
72
+ resume: { type: 'string' },
73
+ continue: { type: 'boolean' },
74
+ 'session-id': { type: 'string' },
75
+ retries: { type: 'string' },
76
+ 'allow-all': { type: 'boolean' },
77
+ 'allow-tool': { type: 'string' },
78
+ ask: { type: 'boolean' },
79
+ 'add-dir': { type: 'string', multiple: true },
80
+ 'dry-run': { type: 'boolean' },
81
+ local: { type: 'boolean' },
82
+ pick: { type: 'boolean' },
83
+ clear: { type: 'boolean' },
84
+ force: { type: 'boolean' },
85
+ 'no-refresh': { type: 'boolean' },
86
+ };
87
+ const RUN_FORMS = [
88
+ ' zen meta run [project] "<question>" ask it something',
89
+ ' zen meta run [project] /<name> [words] run a stored prompt',
90
+ ' zen meta run [project] pick one of its stored prompts',
91
+ ];
92
+ const RUN_ARGUMENTS = [
93
+ 'Arguments:',
94
+ ' [project] Name of a project. Default: the one you are in.',
95
+ ' [prompt] Your question, in quotes. Or --prompt, or piped in when neither',
96
+ ' it nor --prompt-file is given.',
97
+ ' /<name> A prompt file in .github/prompts/<name>.prompt.md.',
98
+ ' [words] Appended to that prompt as a final line.',
99
+ ];
100
+ const RUN_OPTIONS = [
101
+ 'Options:',
102
+ ' --project <name|dir> Which project to work in. The agent is rooted there.',
103
+ ' --model <ref> Model for this run, e.g. vertex/gemini-3.8-flash.',
104
+ ' --provider <name> Force the key to use. Default: the model ref says.',
105
+ ' -p, --prompt <text> The question, in place of [prompt].',
106
+ ' Ignored with /<name>: put words after it instead.',
107
+ ' --prompt-file <file> Added after the question - or after /<name> and its',
108
+ ' words - past a blank line. Alone, it is the question.',
109
+ ' --agent <name> A .github/agents/<name>.agent.md.',
110
+ ' --effort <level> none, minimal, low, medium, high, xhigh or max.',
111
+ ' --allow-tool <list> Only these: read, shell(git:*), write(README.md).',
112
+ ' --ask Have it ask before each tool. Default: it does not.',
113
+ ' --add-dir <dir> Another directory it may touch. Repeatable.',
114
+ ' --resume <id> Continue a copilot session. --continue takes the last.',
115
+ ' --session-id <uuid> Start a new session under this id. Default: a random one.',
116
+ ` --retries <n> Resume after a rate limit or outage. Default ${DEFAULT_RESUMES}, 0 = off.`,
117
+ ' --share <file> Write the transcript to a markdown file.',
118
+ ' --events <file> Append what the agent does to this file as it does it:',
119
+ ' one JSON line per model call, tool call and message.',
120
+ ' --dry-run Print what would run, secrets masked, and stop.',
121
+ ' --no-refresh Leave the editor files as they are (parallel runs).',
122
+ ' --json Print one object: answer, answerFile, sessionId, exitCode, tokens.',
123
+ ];
58
124
  export const meta = {
59
125
  summary: 'Run the meta agent over this project, on your own keys.',
60
126
  usage: USAGE,
61
127
  banner: { head: 'Zenera', accent: 'Meta', subtitle: 'Meta Agent', hue: 'cyan' },
62
128
  details: [
63
129
  'Forms:',
64
- ' zen meta run [project] "<question>" ask it something',
65
- ' zen meta run [project] /<name> [words] run a stored prompt',
66
- ' zen meta run [project] pick one of its stored prompts',
130
+ ...RUN_FORMS,
67
131
  ' zen meta prompts [project] list the stored prompts',
68
132
  ' zen meta resume [project] [session] carry on a run that stopped',
69
133
  ' zen meta model [ref] show or set the model it uses',
@@ -71,33 +135,12 @@ export const meta = {
71
135
  ' zen meta finetune <verb> tune the project on those cases',
72
136
  '',
73
137
  'The project may come before the verb instead: `zen meta acme run`.',
74
- '`zen meta dataset --help` and `zen meta finetune --help` have their own pages.',
138
+ '`zen meta dataset --help`, `zen meta finetune --help` and `zen meta run --help`',
139
+ 'have their own pages; the last lists the stored prompts.',
75
140
  '',
76
- 'Arguments:',
77
- ' [project] Name of a project. Default: the one you are in.',
78
- ' [prompt] Your question, in quotes. Also --prompt, or piped in.',
79
- ' /<name> A prompt file in .github/prompts/<name>.prompt.md.',
80
- ' [words] Appended to that prompt as a final line.',
141
+ ...RUN_ARGUMENTS,
81
142
  '',
82
- 'Options:',
83
- ' --project <name|dir> Which project to work in. The agent is rooted there.',
84
- ' --model <ref> Model for this run, e.g. vertex/gemini-3.8-flash.',
85
- ' --provider <name> Force the key to use. Default: the model ref says.',
86
- ' --prompt-file <file> Append this file to the prompt, after any words.',
87
- ' --agent <name> A .github/agents/<name>.agent.md.',
88
- ' --effort <level> none, minimal, low, medium, high, xhigh or max.',
89
- ' --allow-tool <list> Only these: read, shell(git:*), write(README.md).',
90
- ' --ask Have it ask before each tool. Default: it does not.',
91
- ' --add-dir <dir> Another directory it may touch. Repeatable.',
92
- ' --resume <id> Continue a copilot session. --continue takes the last.',
93
- ' --session-id <uuid> Start a new session under this id. Default: a random one.',
94
- ` --retries <n> Resume after a rate limit or outage. Default ${DEFAULT_RESUMES}, 0 = off.`,
95
- ' --share <file> Write the transcript to a markdown file.',
96
- ' --events <file> Append what the agent does to this file as it does it:',
97
- ' one JSON line per model call, tool call and message.',
98
- ' --dry-run Print what would run, secrets masked, and stop.',
99
- ' --no-refresh Leave the editor files as they are (parallel runs).',
100
- ' --json Print one object: answer, answerFile, sessionId, exitCode, tokens.',
143
+ ...RUN_OPTIONS,
101
144
  '',
102
145
  'It always uses your own keys — `zen key add` — and never a coding-agent',
103
146
  'subscription. The answer goes to stdout and the progress to stderr, so',
@@ -131,6 +174,9 @@ export const meta = {
131
174
  'Setting one asks the provider a one-word question first, so a model that',
132
175
  'will not answer is never stored. --force skips that.',
133
176
  '',
177
+ '`zen meta model --pick` browses every model your keys reach: / filters,',
178
+ 't asks one a question, p takes it.',
179
+ '',
134
180
  'Examples:',
135
181
  ' zen meta run "what does this project do?"',
136
182
  ' zen meta run acme "review the last commit"',
@@ -144,9 +190,16 @@ export const meta = {
144
190
  ' zen meta model --pick',
145
191
  ' zen meta run --dry-run "hello"',
146
192
  ],
147
- help: (ctx) => {
193
+ help: async (ctx) => {
148
194
  const sub = subcommand(ctx.args);
149
- page(sub ? SUBCOMMANDS[sub.name].page : { ...meta, details: meta.details ?? [] });
195
+ if (sub) {
196
+ return page(SUBCOMMANDS[sub.name].page);
197
+ }
198
+ const words = ctx.args.filter((a) => !a.startsWith('-'));
199
+ if (words[0] === 'run' || words[1] === 'run') {
200
+ return await runHelp(ctx, words);
201
+ }
202
+ page({ ...meta, details: meta.details ?? [] });
150
203
  },
151
204
  run: async (ctx) => {
152
205
  // These own their flags, so they are routed before the meta agent's are parsed.
@@ -160,31 +213,7 @@ export const meta = {
160
213
  });
161
214
  return await run({ args, json: ctx.json, cwd: ctx.cwd }, found);
162
215
  }
163
- const { values, positionals } = parse(ctx.args, {
164
- project: { type: 'string' },
165
- provider: { type: 'string' },
166
- model: { type: 'string' },
167
- prompt: { type: 'string', short: 'p' },
168
- 'prompt-file': { type: 'string' },
169
- agent: { type: 'string' },
170
- effort: { type: 'string' },
171
- share: { type: 'string' },
172
- events: { type: 'string' },
173
- resume: { type: 'string' },
174
- continue: { type: 'boolean' },
175
- 'session-id': { type: 'string' },
176
- retries: { type: 'string' },
177
- 'allow-all': { type: 'boolean' },
178
- 'allow-tool': { type: 'string' },
179
- ask: { type: 'boolean' },
180
- 'add-dir': { type: 'string', multiple: true },
181
- 'dry-run': { type: 'boolean' },
182
- local: { type: 'boolean' },
183
- pick: { type: 'boolean' },
184
- clear: { type: 'boolean' },
185
- force: { type: 'boolean' },
186
- 'no-refresh': { type: 'boolean' },
187
- }, USAGE);
216
+ const { values, positionals } = parse(ctx.args, OPTIONS, USAGE);
188
217
  // `zen meta acme run` is typed at least as often as `zen meta run acme`,
189
218
  // and read literally it asks the agent the one-word question "run" —
190
219
  // a whole model call spent on a typo, with nothing on screen to say so.
@@ -216,6 +245,45 @@ export const meta = {
216
245
  throw usageError(first === undefined ? 'nothing to run' : 'a prompt goes through `run`', `try: zen meta run ${positionals.map(requote).join(' ') || '"what does this project do?"'}`);
217
246
  },
218
247
  };
248
+ // ---------------------------------------------------------------------------
249
+ // zen meta run --help
250
+ //
251
+ // The prompts are the reason to type `run`, so its page names them. The
252
+ // shipped ones come from the templates, which `run` refreshes into the project
253
+ // first; a project's own additions are read from the project.
254
+ // ---------------------------------------------------------------------------
255
+ async function runHelp(ctx, words) {
256
+ const at = ctx.args.indexOf('--project');
257
+ const flag = ctx.args.find((a) => a.startsWith('--project='))?.slice('--project='.length) ??
258
+ (at >= 0 ? ctx.args[at + 1] : undefined);
259
+ const named = flag ?? (words[1] === 'run' ? words[0] : words[1]);
260
+ const project = (named && !named.startsWith('/') ? await Projects.find(named) : undefined) ??
261
+ (await Projects.current(ctx.cwd));
262
+ const shipped = join(TEMPLATES, 'editor');
263
+ const ours = new Set(await listPrompts(shipped));
264
+ const theirs = project ? await listPrompts(project.dir) : [];
265
+ const names = [...new Set([...ours, ...theirs])].sort();
266
+ const found = await Promise.all(names.map((n) => loadPrompt(ours.has(n) ? shipped : project.dir, n)));
267
+ const lines = [
268
+ 'Forms:',
269
+ ...RUN_FORMS,
270
+ '',
271
+ project ? `Prompts in ${project.name}:` : 'Prompts:',
272
+ ];
273
+ const nameWidth = Math.max(...found.map((p) => p.name.length + 1));
274
+ const room = Math.max(30, Math.min(process.stdout.columns ?? 80, 100) - nameWidth - 6);
275
+ for (const p of found) {
276
+ const [first = '', ...more] = wrap(p.description ?? '', room);
277
+ lines.push(` ${pad(`/${p.name}`, nameWidth)} ${first}`.trimEnd());
278
+ lines.push(...more.map((l) => ` ${' '.repeat(nameWidth)} ${l}`));
279
+ }
280
+ lines.push('', ...RUN_ARGUMENTS, '', ...RUN_OPTIONS, '', 'Examples:', ' zen meta run "what does this project do?"', ' zen meta run acme /project-review', ' zen meta run acme /spec-sync-project agents/triage.md', ' zen meta run acme /analyze --json');
281
+ page({
282
+ usage: 'zen meta run [project] [/<name> [words] | "<question>"] [options]',
283
+ summary: 'Ask the meta agent a question, or run one of the stored prompts.',
284
+ details: lines,
285
+ });
286
+ }
219
287
  /** Words the shell would have split are shown back the way they were typed. */
220
288
  const requote = (s) => (/\s/.test(s) ? JSON.stringify(s) : s);
221
289
  // ---------------------------------------------------------------------------
@@ -327,6 +395,104 @@ async function pickPrompt(ctx, project) {
327
395
  return await choose('Which prompt?', names.map((n) => ({ label: `/${n}`, value: n })));
328
396
  }
329
397
  // ---------------------------------------------------------------------------
398
+ // Completing it
399
+ //
400
+ // `zen meta run acme /<Tab>` is where a terminal can show what an editor's menu
401
+ // shows. It reads the line the way `stored` does — a leading slash is a prompt,
402
+ // a bare word that names a project is the project — and it only ever reads:
403
+ // the editor files are not refreshed for the sake of a Tab.
404
+ // ---------------------------------------------------------------------------
405
+ async function findProject(name) {
406
+ try {
407
+ return await Projects.find(name);
408
+ }
409
+ catch {
410
+ return undefined;
411
+ }
412
+ }
413
+ async function promptsOf(project) {
414
+ if (!project) {
415
+ return [];
416
+ }
417
+ const names = await listPrompts(project.dir);
418
+ return await Promise.all(names.map(async (name) => ({
419
+ value: `/${name}`,
420
+ description: (await loadPrompt(project.dir, name).catch(() => undefined))?.description,
421
+ })));
422
+ }
423
+ /** `args` is what came after `meta`, without the word being completed. */
424
+ export async function completeMeta(args, cur, cwd) {
425
+ const words = [];
426
+ let flagged;
427
+ let waiting;
428
+ for (const arg of args) {
429
+ if (waiting) {
430
+ if (waiting === 'project') {
431
+ flagged = arg;
432
+ }
433
+ waiting = undefined;
434
+ }
435
+ else if (arg.startsWith('--')) {
436
+ const [flag, value] = arg.slice(2).split(/=(.*)/s);
437
+ if (value !== undefined) {
438
+ flagged = flag === 'project' ? value : flagged;
439
+ }
440
+ else if (OPTIONS[flag]?.type === 'string') {
441
+ waiting = flag;
442
+ }
443
+ }
444
+ else if (arg === '-p') {
445
+ waiting = 'prompt';
446
+ }
447
+ else if (!arg.startsWith('-')) {
448
+ words.push(arg);
449
+ }
450
+ }
451
+ // A flag's value is the flag's to name, and most of them are free text or a
452
+ // path — which is the shell's.
453
+ if (waiting === 'provider') {
454
+ return matching(PROVIDERS.map((value) => ({ value })), cur);
455
+ }
456
+ if (waiting === 'project') {
457
+ return matching(await projectNames(), cur);
458
+ }
459
+ if (waiting) {
460
+ return [];
461
+ }
462
+ const at = words.findIndex((w) => VERBS.has(w));
463
+ const named = at === 1 ? words[0] : undefined;
464
+ const verb = at === 0 || (named && (await findProject(named))) ? words[at] : undefined;
465
+ if (cur.startsWith('-')) {
466
+ return verb === 'dataset' || verb === 'finetune'
467
+ ? []
468
+ : matching(Object.keys(OPTIONS).map((flag) => ({ value: `--${flag}` })), cur);
469
+ }
470
+ if (!verb) {
471
+ const verbs = [...VERBS].map((value) => ({ value }));
472
+ if (words.length === 0) {
473
+ return matching([...verbs, ...(await projectNames())], cur);
474
+ }
475
+ return words.length === 1 && (await findProject(words[0])) ? matching(verbs, cur) : [];
476
+ }
477
+ const rest = words.slice(at + 1);
478
+ if (verb === 'prompts' || verb === 'resume') {
479
+ return flagged || named || rest.length > 0 ? [] : matching(await projectNames(), cur);
480
+ }
481
+ if (verb !== 'run' || rest.some((w) => w.startsWith('/'))) {
482
+ // Past the prompt's name, what follows is files and words.
483
+ return [];
484
+ }
485
+ const given = flagged ?? named ?? rest[0];
486
+ if (given === undefined) {
487
+ const here = await Projects.current(cwd).catch(() => undefined);
488
+ return matching([...(await promptsOf(here)), ...(await projectNames())], cur);
489
+ }
490
+ const project = await findProject(given);
491
+ const fromRest = !flagged && !named;
492
+ const free = rest.length - (fromRest && project ? 1 : 0);
493
+ return free > 0 ? [] : matching(await promptsOf(project), cur);
494
+ }
495
+ // ---------------------------------------------------------------------------
330
496
  // zen meta prompts [project]
331
497
  //
332
498
  // A slash command is only discoverable inside an editor, where a menu drops
@@ -728,7 +894,7 @@ function count(text, flag) {
728
894
  }
729
895
  function asProvider(name) {
730
896
  const owner = assertOwner(name);
731
- if (owner === 'exa') {
897
+ if (!isProvider(owner)) {
732
898
  throw usageError(`${name} is not a model provider`);
733
899
  }
734
900
  return owner;
@@ -898,8 +1064,15 @@ async function pickModel(ctx) {
898
1064
  if (known.length === 0) {
899
1065
  throw credentialError('no keys to choose from', 'add one: zen key add openai');
900
1066
  }
901
- const suggestions = [...new Set(known)].flatMap((p) => (RECOMMENDED[p] ?? []).map((id) => `${p}/${id}`));
902
- note(dim(bold('Models zen knows how to wire, for providers you hold a key for')));
903
- return await choose('Which model?', suggestions.map((ref) => ({ label: ref, value: ref })));
1067
+ const held = [...new Set(known)];
1068
+ const { browseModels } = await import("./models.js");
1069
+ const chosen = await browseModels({
1070
+ initial: held.length === 1 ? held[0] : undefined,
1071
+ role: 'chat',
1072
+ });
1073
+ if (!chosen) {
1074
+ throw usageError('nothing picked', 'name the model instead: zen meta model <provider>/<id>');
1075
+ }
1076
+ return `${chosen.provider}/${chosen.id}`;
904
1077
  }
905
1078
  //# sourceMappingURL=meta.js.map
@@ -1,3 +1,15 @@
1
+ import { type CatalogEntry, type Role } from '../catalog.ts';
1
2
  import type { Command } from '../command.ts';
3
+ import { type Provider } from '../keys.ts';
4
+ export interface BrowseRequest {
5
+ /** opens straight into this provider's listing */
6
+ initial?: Provider;
7
+ /** the role filter each listing opens on; tab still cycles */
8
+ role?: Role;
9
+ refresh?: boolean;
10
+ theme?: string;
11
+ }
12
+ /** The browser, for any command that wants a person to pick a model. */
13
+ export declare function browseModels(request?: BrowseRequest): Promise<CatalogEntry | undefined>;
2
14
  export declare const models: Command;
3
15
  //# sourceMappingURL=models.d.ts.map