skyloom 1.15.4 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cli/command_args.d.ts +74 -0
  2. package/dist/cli/command_args.d.ts.map +1 -0
  3. package/dist/cli/command_args.js +129 -0
  4. package/dist/cli/command_args.js.map +1 -0
  5. package/dist/cli/loom.d.ts +20 -0
  6. package/dist/cli/loom.d.ts.map +1 -1
  7. package/dist/cli/loom.js +202 -24
  8. package/dist/cli/loom.js.map +1 -1
  9. package/dist/cli/loom_chat.d.ts.map +1 -1
  10. package/dist/cli/loom_chat.js +39 -0
  11. package/dist/cli/loom_chat.js.map +1 -1
  12. package/dist/core/agent.js +2 -2
  13. package/dist/core/agent.js.map +1 -1
  14. package/dist/core/security.d.ts.map +1 -1
  15. package/dist/core/security.js +1 -0
  16. package/dist/core/security.js.map +1 -1
  17. package/dist/core/tool_router.d.ts.map +1 -1
  18. package/dist/core/tool_router.js +11 -3
  19. package/dist/core/tool_router.js.map +1 -1
  20. package/dist/tools/builtin.d.ts.map +1 -1
  21. package/dist/tools/builtin.js +38 -192
  22. package/dist/tools/builtin.js.map +1 -1
  23. package/dist/tools/websearch.d.ts +92 -0
  24. package/dist/tools/websearch.d.ts.map +1 -0
  25. package/dist/tools/websearch.js +343 -0
  26. package/dist/tools/websearch.js.map +1 -0
  27. package/dist/web/server.js +2 -9
  28. package/dist/web/server.js.map +1 -1
  29. package/dist/web/ui.d.ts.map +1 -1
  30. package/dist/web/ui.js +3 -2
  31. package/dist/web/ui.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/cli/command_args.ts +159 -0
  34. package/src/cli/loom.ts +155 -17
  35. package/src/cli/loom_chat.ts +33 -0
  36. package/src/core/agent.ts +2 -2
  37. package/src/core/security.ts +1 -0
  38. package/src/core/tool_router.ts +11 -3
  39. package/src/tools/builtin.ts +38 -190
  40. package/src/tools/websearch.ts +368 -0
  41. package/src/web/server.ts +2 -10
  42. package/src/web/ui.ts +3 -2
  43. package/tests/command_args.test.ts +115 -0
  44. package/tests/loom.test.ts +74 -0
  45. package/tests/tool_router.test.ts +15 -0
  46. package/tests/web.test.ts +7 -5
  47. package/tests/websearch.test.ts +190 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 斜杠命令向导 · Cascading argument wizard for slash commands.
3
+ *
4
+ * After a slash command that takes structured arguments is chosen in the loom
5
+ * palette, the TUI walks the user through its arguments one level at a time:
6
+ * pick a provider, then paste a key; pick a model; pick a session. Each level is
7
+ * navigable with ↑/↓ and filterable by typing — the same affordance as the
8
+ * command palette itself, extended to arguments.
9
+ *
10
+ * This module is the pure brain of that flow (no I/O, no terminal) so it is
11
+ * fully unit-testable: given a command, the values chosen so far, and a snapshot
12
+ * of runtime context, it returns the next step — or null when the command is
13
+ * complete and ready to submit.
14
+ */
15
+ export interface ArgChoice {
16
+ /** The value contributed to the final command line. */
17
+ value: string;
18
+ /** Display label in the list. */
19
+ label: string;
20
+ /** Optional dim hint shown after the label. */
21
+ hint?: string;
22
+ /** Optional group heading (e.g. provider name) for sectioned lists. */
23
+ group?: string;
24
+ }
25
+ export interface WizardStep {
26
+ kind: 'choice' | 'freeform';
27
+ /** Heading shown above the list / prompt. */
28
+ title: string;
29
+ /** Choices for a 'choice' step (already ordered). */
30
+ choices: ArgChoice[];
31
+ /** A 'choice' step may also accept a typed value not in the list. */
32
+ allowFreeform: boolean;
33
+ /** Placeholder for a 'freeform' step (or a free-typed choice). */
34
+ placeholder?: string;
35
+ /** Mask typed input (API keys). */
36
+ secret?: boolean;
37
+ }
38
+ export interface WizardProvider {
39
+ id: string;
40
+ label: string;
41
+ configured: boolean;
42
+ envVar?: string;
43
+ }
44
+ export interface WizardModel {
45
+ id: string;
46
+ provider: string;
47
+ label: string;
48
+ hint?: string;
49
+ }
50
+ export interface WizardSession {
51
+ id: string;
52
+ label: string;
53
+ }
54
+ export interface WizardContext {
55
+ providers: WizardProvider[];
56
+ models: WizardModel[];
57
+ sessions: WizardSession[];
58
+ }
59
+ /** Does this base command (with or without leading slash) have a wizard? */
60
+ export declare function hasWizard(command: string): boolean;
61
+ /**
62
+ * The next step for `command` given the values already chosen, or null when the
63
+ * command is complete (ready to submit via {@link buildCommandLine}).
64
+ */
65
+ export declare function nextWizardStep(command: string, prior: string[], ctx: WizardContext): WizardStep | null;
66
+ /** Assemble the final command line from the base command + chosen values. */
67
+ export declare function buildCommandLine(command: string, values: string[]): string;
68
+ /**
69
+ * Filter + rank choices by a typed query (case-insensitive substring on value,
70
+ * label, and group). Empty query returns the list unchanged. Exact value/label
71
+ * prefix matches sort first so the obvious pick lands at the top.
72
+ */
73
+ export declare function filterChoices(choices: ArgChoice[], typed: string): ArgChoice[];
74
+ //# sourceMappingURL=command_args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command_args.d.ts","sourceRoot":"","sources":["../../src/cli/command_args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,SAAS;IACxB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,qEAAqE;IACrE,aAAa,EAAE,OAAO,CAAC;IACvB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;AACnG,MAAM,WAAW,WAAW;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC3F,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAE5D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAKD,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAElD;AAmBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,aAAa,GAAG,UAAU,GAAG,IAAI,CAoCtG;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAgB1E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAiB9E"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ /**
3
+ * 斜杠命令向导 · Cascading argument wizard for slash commands.
4
+ *
5
+ * After a slash command that takes structured arguments is chosen in the loom
6
+ * palette, the TUI walks the user through its arguments one level at a time:
7
+ * pick a provider, then paste a key; pick a model; pick a session. Each level is
8
+ * navigable with ↑/↓ and filterable by typing — the same affordance as the
9
+ * command palette itself, extended to arguments.
10
+ *
11
+ * This module is the pure brain of that flow (no I/O, no terminal) so it is
12
+ * fully unit-testable: given a command, the values chosen so far, and a snapshot
13
+ * of runtime context, it returns the next step — or null when the command is
14
+ * complete and ready to submit.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.hasWizard = hasWizard;
18
+ exports.nextWizardStep = nextWizardStep;
19
+ exports.buildCommandLine = buildCommandLine;
20
+ exports.filterChoices = filterChoices;
21
+ /** Commands that drive a guided wizard (base name without the leading slash). */
22
+ const WIZARD_COMMANDS = new Set(['model', 'apikey', 'connect', 'resume']);
23
+ /** Does this base command (with or without leading slash) have a wizard? */
24
+ function hasWizard(command) {
25
+ return WIZARD_COMMANDS.has(command.replace(/^\//, '').trim().toLowerCase());
26
+ }
27
+ function providerChoices(ctx) {
28
+ return ctx.providers.map((p) => ({
29
+ value: p.id,
30
+ label: p.label,
31
+ hint: p.configured ? '✓ 已配置' : (p.envVar ? `需 ${p.envVar}` : '未配置'),
32
+ }));
33
+ }
34
+ function modelChoices(ctx) {
35
+ return ctx.models.map((m) => ({
36
+ value: m.id,
37
+ label: m.id,
38
+ hint: m.hint,
39
+ group: m.provider,
40
+ }));
41
+ }
42
+ /**
43
+ * The next step for `command` given the values already chosen, or null when the
44
+ * command is complete (ready to submit via {@link buildCommandLine}).
45
+ */
46
+ function nextWizardStep(command, prior, ctx) {
47
+ const cmd = command.replace(/^\//, '').trim().toLowerCase();
48
+ switch (cmd) {
49
+ case 'model': {
50
+ if (prior.length >= 1)
51
+ return null;
52
+ const choices = [
53
+ { value: 'reset', label: '↺ reset', hint: '回到统一默认模型' },
54
+ ...modelChoices(ctx),
55
+ ];
56
+ return { kind: 'choice', title: '选择模型(输入可筛选)', choices, allowFreeform: true, placeholder: '模型 id' };
57
+ }
58
+ case 'connect': {
59
+ if (prior.length >= 1)
60
+ return null;
61
+ return { kind: 'choice', title: '选择 Provider', choices: providerChoices(ctx), allowFreeform: true, placeholder: 'provider' };
62
+ }
63
+ case 'apikey': {
64
+ // step 0: provider · step 1: the key
65
+ if (prior.length === 0) {
66
+ return { kind: 'choice', title: '为哪个 Provider 配置 API Key', choices: providerChoices(ctx), allowFreeform: true, placeholder: 'provider' };
67
+ }
68
+ if (prior.length === 1) {
69
+ return { kind: 'freeform', title: `粘贴 ${prior[0]} 的 API Key`, choices: [], allowFreeform: true, placeholder: 'sk-…(回车保存)', secret: true };
70
+ }
71
+ return null;
72
+ }
73
+ case 'resume': {
74
+ if (prior.length >= 1)
75
+ return null;
76
+ const choices = ctx.sessions.map((s, i) => ({ value: String(i + 1), label: `${i + 1}. ${s.label}`, hint: s.id.slice(0, 8) }));
77
+ return { kind: 'choice', title: choices.length ? '选择要恢复的会话' : '暂无历史会话', choices, allowFreeform: true, placeholder: '序号或 id' };
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ /** Assemble the final command line from the base command + chosen values. */
83
+ function buildCommandLine(command, values) {
84
+ const cmd = command.replace(/^\//, '').trim().toLowerCase();
85
+ const v = values.filter((x) => x !== undefined && x !== null);
86
+ switch (cmd) {
87
+ case 'apikey':
88
+ // /apikey set <provider> <key>
89
+ return `/apikey set ${v.join(' ')}`.trim();
90
+ case 'model':
91
+ return `/model ${v.join(' ')}`.trim();
92
+ case 'connect':
93
+ return `/connect ${v.join(' ')}`.trim();
94
+ case 'resume':
95
+ return `/resume ${v.join(' ')}`.trim();
96
+ default:
97
+ return `/${cmd} ${v.join(' ')}`.trim();
98
+ }
99
+ }
100
+ /**
101
+ * Filter + rank choices by a typed query (case-insensitive substring on value,
102
+ * label, and group). Empty query returns the list unchanged. Exact value/label
103
+ * prefix matches sort first so the obvious pick lands at the top.
104
+ */
105
+ function filterChoices(choices, typed) {
106
+ const q = typed.trim().toLowerCase();
107
+ if (!q)
108
+ return choices;
109
+ const scored = [];
110
+ for (const c of choices) {
111
+ const value = c.value.toLowerCase();
112
+ const label = c.label.toLowerCase();
113
+ const group = (c.group || '').toLowerCase();
114
+ let rank = -1;
115
+ if (value === q || label === q)
116
+ rank = 0;
117
+ else if (value.startsWith(q) || label.startsWith(q))
118
+ rank = 1;
119
+ else if (value.includes(q) || label.includes(q))
120
+ rank = 2;
121
+ else if (group.includes(q))
122
+ rank = 3;
123
+ if (rank >= 0)
124
+ scored.push({ c, rank });
125
+ }
126
+ scored.sort((a, b) => a.rank - b.rank); // stable within equal ranks
127
+ return scored.map((s) => s.c);
128
+ }
129
+ //# sourceMappingURL=command_args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command_args.js","sourceRoot":"","sources":["../../src/cli/command_args.ts"],"names":[],"mappings":"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"}
@@ -25,6 +25,7 @@
25
25
  *
26
26
  * Design rationale: docs/AESTHETIC_DESIGN.md §2.2 (方案三 · 立轴).
27
27
  */
28
+ import { type WizardStep } from "./command_args";
28
29
  /** Truncate a styled string to a visual width, keeping ANSI sequences intact. */
29
30
  export declare function cutVisual(s: string, maxW: number): string;
30
31
  /** Pad a styled string with spaces to an exact visual width (truncates if over). */
@@ -168,6 +169,14 @@ export declare class LoomUI {
168
169
  modeBadge: string;
169
170
  /** User-defined slash commands shown in the palette ([name, description]). */
170
171
  extraCommands: [string, string][];
172
+ /**
173
+ * Cascading argument wizard, active after a structured slash command is
174
+ * chosen (e.g. /apikey → pick provider → paste key). Resolved step-by-step
175
+ * via the wizardStep callback the chat loop wires up.
176
+ */
177
+ private wizard;
178
+ /** Next-step resolver for the argument wizard (set by the chat loop with runtime context). */
179
+ wizardStep: ((command: string, prior: string[]) => WizardStep | null) | null;
171
180
  private keypressHandler;
172
181
  private resizeHandler;
173
182
  constructor(opts?: LoomOpts);
@@ -202,6 +211,17 @@ export declare class LoomUI {
202
211
  private onKey;
203
212
  private handleSigint;
204
213
  private flashHint;
214
+ /** Submit a turn: clear input, record history, resolve the pending read. */
215
+ private submitText;
216
+ /** Choices for the current wizard step, filtered by what the user has typed. */
217
+ private wizardFiltered;
218
+ /** Open the wizard for `command` (e.g. "/model"). Returns false if it has none. */
219
+ private startWizard;
220
+ /** Accept a value for the current step; advance to the next or submit. */
221
+ private wizardCommit;
222
+ /** Step back one level (or close the wizard when at the first level). */
223
+ private wizardBack;
224
+ private handleWizardKey;
205
225
  private paletteMatches;
206
226
  private cols;
207
227
  private rows;
@@ -1 +1 @@
1
- {"version":3,"file":"loom.d.ts","sourceRoot":"","sources":["../../src/cli/loom.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loom.d.ts","sourceRoot":"","sources":["../../src/cli/loom.ts"],"names":[],"mappings":"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"}
package/dist/cli/loom.js CHANGED
@@ -74,6 +74,7 @@ const readline = __importStar(require("readline"));
74
74
  const chalk_1 = __importDefault(require("chalk"));
75
75
  const theme_1 = require("../core/theme");
76
76
  const tui_1 = require("./tui");
77
+ const command_args_1 = require("./command_args");
77
78
  /* ════════════════════════════════════════
78
79
  ANSI-aware string helpers (pure, tested)
79
80
  ════════════════════════════════════════ */
@@ -417,6 +418,14 @@ class LoomUI {
417
418
  this.modeBadge = "";
418
419
  /** User-defined slash commands shown in the palette ([name, description]). */
419
420
  this.extraCommands = [];
421
+ /**
422
+ * Cascading argument wizard, active after a structured slash command is
423
+ * chosen (e.g. /apikey → pick provider → paste key). Resolved step-by-step
424
+ * via the wizardStep callback the chat loop wires up.
425
+ */
426
+ this.wizard = null;
427
+ /** Next-step resolver for the argument wizard (set by the chat loop with runtime context). */
428
+ this.wizardStep = null;
420
429
  this.keypressHandler = null;
421
430
  this.resizeHandler = null;
422
431
  /* ── streaming ── */
@@ -651,6 +660,11 @@ class LoomUI {
651
660
  this.handleSigint();
652
661
  return;
653
662
  }
663
+ // The argument wizard owns all keys while it is open.
664
+ if (this.wizard && !this.busy) {
665
+ this.handleWizardKey(str, key);
666
+ return;
667
+ }
654
668
  if (name === "pageup") {
655
669
  this.scrollOff += Math.max(1, this.bodyH() - 2);
656
670
  this.clampScroll();
@@ -668,15 +682,15 @@ class LoomUI {
668
682
  return; // a reply is being woven; ignore submit
669
683
  let text = this.inputGlyphs.join("").trim();
670
684
  // Palette open: Enter runs the ↑↓-highlighted command (Claude Code
671
- // style). Commands that take arguments fill the input instead so the
672
- // user can type them.
685
+ // style). A command with a guided argument wizard opens the wizard;
686
+ // otherwise an argument-taking command fills the input to wait for input.
673
687
  const matches = this.paletteMatches();
674
688
  if (matches.length > 0 && text.startsWith("/")) {
675
689
  const [cmd] = matches[Math.max(0, Math.min(this.paletteIdx, matches.length - 1))];
690
+ if (this.startWizard(cmd.trim()))
691
+ return;
676
692
  if (cmd.endsWith(" ")) {
677
- // argument-taking command: fill the input and wait for arguments
678
- // (the palette closes once the line contains a space; a second
679
- // Enter then submits as typed)
693
+ // argument-taking command without a wizard: fill the input and wait.
680
694
  this.inputGlyphs = [...cmd];
681
695
  this.cursor = this.inputGlyphs.length;
682
696
  this.paletteIdx = 0;
@@ -685,21 +699,7 @@ class LoomUI {
685
699
  }
686
700
  text = cmd.trimEnd();
687
701
  }
688
- this.inputGlyphs = [];
689
- this.cursor = 0;
690
- this.histIdx = -1;
691
- this.paletteIdx = 0;
692
- this.scrollOff = 0; // submitting a turn snaps back to the tail to watch the reply
693
- if (text) {
694
- this.history.unshift(text);
695
- if (this.history.length > 200)
696
- this.history.pop();
697
- }
698
- const r = this.pendingResolve;
699
- this.pendingResolve = null;
700
- this.paint();
701
- if (r)
702
- r(text);
702
+ this.submitText(text);
703
703
  return;
704
704
  }
705
705
  const paletteOpen = this.paletteMatches().length > 0 && this.inputGlyphs[0] === "/";
@@ -739,6 +739,8 @@ class LoomUI {
739
739
  const m = this.paletteMatches();
740
740
  const pick = m[Math.min(this.paletteIdx, m.length - 1)];
741
741
  if (pick) {
742
+ if (this.startWizard(pick[0].trim()))
743
+ return;
742
744
  this.inputGlyphs = [...pick[0].trimEnd()];
743
745
  this.cursor = this.inputGlyphs.length;
744
746
  }
@@ -851,6 +853,134 @@ class LoomUI {
851
853
  this.sigintAt = now;
852
854
  this.flash("再按一次 Ctrl-C 退出");
853
855
  }
856
+ /* ── argument wizard ── */
857
+ /** Submit a turn: clear input, record history, resolve the pending read. */
858
+ submitText(text) {
859
+ this.wizard = null;
860
+ this.inputGlyphs = [];
861
+ this.cursor = 0;
862
+ this.histIdx = -1;
863
+ this.paletteIdx = 0;
864
+ this.scrollOff = 0; // submitting a turn snaps back to the tail to watch the reply
865
+ if (text) {
866
+ this.history.unshift(text);
867
+ if (this.history.length > 200)
868
+ this.history.pop();
869
+ }
870
+ const r = this.pendingResolve;
871
+ this.pendingResolve = null;
872
+ this.paint();
873
+ if (r)
874
+ r(text);
875
+ }
876
+ /** Choices for the current wizard step, filtered by what the user has typed. */
877
+ wizardFiltered() {
878
+ if (!this.wizard || this.wizard.step.kind !== "choice")
879
+ return [];
880
+ return (0, command_args_1.filterChoices)(this.wizard.step.choices, this.wizard.typed);
881
+ }
882
+ /** Open the wizard for `command` (e.g. "/model"). Returns false if it has none. */
883
+ startWizard(command) {
884
+ if (!this.wizardStep || !(0, command_args_1.hasWizard)(command))
885
+ return false;
886
+ const step = this.wizardStep(command, []);
887
+ if (!step)
888
+ return false;
889
+ this.wizard = { command, values: [], step, typed: "", idx: 0 };
890
+ this.inputGlyphs = [];
891
+ this.cursor = 0;
892
+ this.paletteIdx = 0;
893
+ this.paint();
894
+ return true;
895
+ }
896
+ /** Accept a value for the current step; advance to the next or submit. */
897
+ wizardCommit(value) {
898
+ const w = this.wizard;
899
+ const values = [...w.values, value];
900
+ const next = this.wizardStep ? this.wizardStep(w.command, values) : null;
901
+ if (next) {
902
+ this.wizard = { command: w.command, values, step: next, typed: "", idx: 0 };
903
+ this.paint();
904
+ }
905
+ else {
906
+ this.submitText((0, command_args_1.buildCommandLine)(w.command, values));
907
+ }
908
+ }
909
+ /** Step back one level (or close the wizard when at the first level). */
910
+ wizardBack() {
911
+ const w = this.wizard;
912
+ if (w.values.length === 0 || !this.wizardStep) {
913
+ this.wizard = null;
914
+ this.paint();
915
+ return;
916
+ }
917
+ const values = w.values.slice(0, -1);
918
+ const step = this.wizardStep(w.command, values);
919
+ if (!step) {
920
+ this.wizard = null;
921
+ this.paint();
922
+ return;
923
+ }
924
+ this.wizard = { command: w.command, values, step, typed: "", idx: 0 };
925
+ this.paint();
926
+ }
927
+ handleWizardKey(str, key) {
928
+ const w = this.wizard;
929
+ const name = key?.name;
930
+ if (name === "escape") {
931
+ this.wizard = null;
932
+ this.paint();
933
+ return;
934
+ }
935
+ if (name === "up") {
936
+ if (w.step.kind === "choice")
937
+ w.idx = Math.max(0, w.idx - 1);
938
+ this.paint();
939
+ return;
940
+ }
941
+ if (name === "down") {
942
+ if (w.step.kind === "choice")
943
+ w.idx = Math.min(Math.max(0, this.wizardFiltered().length - 1), w.idx + 1);
944
+ this.paint();
945
+ return;
946
+ }
947
+ if (name === "return" || name === "tab") {
948
+ if (w.step.kind === "choice") {
949
+ const filtered = this.wizardFiltered();
950
+ const pick = filtered[Math.min(w.idx, filtered.length - 1)];
951
+ if (pick) {
952
+ this.wizardCommit(pick.value);
953
+ return;
954
+ }
955
+ if (w.step.allowFreeform && w.typed.trim()) {
956
+ this.wizardCommit(w.typed.trim());
957
+ return;
958
+ }
959
+ return;
960
+ }
961
+ if (w.typed.trim())
962
+ this.wizardCommit(w.typed.trim());
963
+ return;
964
+ }
965
+ if (name === "backspace") {
966
+ if (w.typed.length > 0) {
967
+ w.typed = w.typed.slice(0, -1);
968
+ w.idx = 0;
969
+ this.paint();
970
+ return;
971
+ }
972
+ this.wizardBack();
973
+ return;
974
+ }
975
+ if (str && !key?.ctrl && !key?.meta) {
976
+ const glyphs = [...str].filter((c) => c >= " " || (0, tui_1.charWidth)(c.codePointAt(0)) > 0);
977
+ if (glyphs.length) {
978
+ w.typed += glyphs.join("");
979
+ w.idx = 0;
980
+ this.paint();
981
+ }
982
+ }
983
+ }
854
984
  paletteMatches() {
855
985
  const l = this.inputGlyphs.join("");
856
986
  if (!l.startsWith("/") || l.includes(" "))
@@ -1047,6 +1177,14 @@ class LoomUI {
1047
1177
  content = " " + chalk_1.default.yellow("⚠ ") + cutVisual(this.modal.text, innerW - 14) + chalk_1.default.bold(" 允许? ") + chalk_1.default.dim("[y/N]");
1048
1178
  cursorPos = { row: rows - 2, col: Math.min(innerW, (0, tui_1.visualWidth)(content) + 1) };
1049
1179
  }
1180
+ else if (this.wizard) {
1181
+ const w = this.wizard;
1182
+ const crumb = [w.command.replace(/^\//, ""), ...w.values].join(" ");
1183
+ const shownTyped = w.step.secret ? "•".repeat([...w.typed].length) : w.typed;
1184
+ const head = chalk_1.default.hex(t.hex)(` ${t.symbol} `) + chalk_1.default.hex(theme_1.PALETTE.inkLight)(crumb + " ▸ ");
1185
+ content = head + cutVisual(shownTyped, innerW - (0, tui_1.visualWidth)(head) - 2);
1186
+ cursorPos = { row: rows - 2, col: Math.min(innerW, (0, tui_1.visualWidth)(content) + 1) };
1187
+ }
1050
1188
  else {
1051
1189
  const promptStr = chalk_1.default.hex(t.hex)(` ${t.symbol} `) + chalk_1.default.hex(theme_1.PALETTE.inkLight)("❯ ");
1052
1190
  const promptW = (0, tui_1.visualWidth)(promptStr);
@@ -1079,16 +1217,56 @@ class LoomUI {
1079
1217
  const paletteUp = this.paletteMatches().length > 0 && this.inputGlyphs[0] === "/";
1080
1218
  const hint = this.busy
1081
1219
  ? " Ctrl-C 中断本轮 "
1082
- : paletteUp
1083
- ? " ↑↓ 选命令 · Enter 执行 · Tab 补全 · Esc 收起 "
1084
- : " / 命令 · 滚轮/PgUp 回看 · Shift+Tab 切模式 · Ctrl-C 退出 ";
1220
+ : this.wizard
1221
+ ? (this.wizard.step.kind === "choice"
1222
+ ? " ↑↓ 选择 · Enter 确认 · 输入筛选 · ⌫ 返回 · Esc 取消 "
1223
+ : " 输入后 Enter 确认 · ⌫ 返回上一步 · Esc 取消 ")
1224
+ : paletteUp
1225
+ ? " ↑↓ 选命令 · Enter 执行 · Tab 补全 · Esc 收起 "
1226
+ : " / 命令 · 滚轮/PgUp 回看 · Shift+Tab 切模式 · Ctrl-C 退出 ";
1085
1227
  // └─ hint ───…┘ → 2 + w(hint) + fill + 1 = cols
1086
1228
  const fill = innerW - (0, tui_1.visualWidth)(hint) - 1;
1087
1229
  frame.push(B("└─") + chalk_1.default.dim(hint) + B("─".repeat(Math.max(0, fill)) + "┘"));
1088
1230
  }
1231
+ // ── argument wizard: overlay the title + selectable choices ──
1232
+ if (this.wizard && !this.modal) {
1233
+ const w = this.wizard;
1234
+ const overlayRow = (row, s) => {
1235
+ if (row < 1 + SKY_H || row >= 1 + SKY_H + bodyH)
1236
+ return;
1237
+ frame[row] = B("│") + padAnsi(rail[row - 1 - SKY_H] ?? "", RAIL_W) + B("│") + " " + padAnsi(s, this.viewW()) + B("│");
1238
+ };
1239
+ const lines = [];
1240
+ lines.push(chalk_1.default.dim(" " + cutVisual(w.step.title, this.viewW() - 4)));
1241
+ if (w.step.kind === "choice") {
1242
+ const filtered = this.wizardFiltered();
1243
+ const bodyRows = Math.min(7, bodyH - 1);
1244
+ if (!filtered.length) {
1245
+ lines.push(" " + chalk_1.default.dim(w.step.allowFreeform ? `直接输入,回车确认` : `无匹配项`));
1246
+ }
1247
+ else {
1248
+ w.idx = Math.max(0, Math.min(w.idx, filtered.length - 1));
1249
+ const start = Math.max(0, Math.min(w.idx - bodyRows + 1, filtered.length - bodyRows));
1250
+ filtered.slice(start, start + bodyRows).forEach((c, i) => {
1251
+ const sel = start + i === w.idx;
1252
+ const mark = sel ? chalk_1.default.hex(t.hex)(" ▸ ") : " ";
1253
+ const group = c.group ? chalk_1.default.dim(`${c.group}/`) : "";
1254
+ const label = sel ? chalk_1.default.bold.hex(t.hex)(c.label) : chalk_1.default.hex(theme_1.PALETTE.inkLight)(c.label);
1255
+ const hint = c.hint ? chalk_1.default.dim(" " + c.hint) : "";
1256
+ const counter = sel && filtered.length > bodyRows ? chalk_1.default.dim(` ${w.idx + 1}/${filtered.length}`) : "";
1257
+ lines.push(mark + group + cutVisual(label + hint, this.viewW() - 8) + counter);
1258
+ });
1259
+ }
1260
+ }
1261
+ else {
1262
+ lines.push(" " + chalk_1.default.dim(w.step.placeholder || "输入后回车确认"));
1263
+ }
1264
+ const baseRow = 1 + SKY_H + bodyH - lines.length;
1265
+ lines.forEach((s, i) => overlayRow(baseRow + i, s));
1266
+ }
1089
1267
  // ── slash palette: overlay onto the rows just above the divider ──
1090
1268
  const matches = this.paletteMatches();
1091
- if (matches.length > 0 && this.inputGlyphs[0] === "/" && !this.modal) {
1269
+ if (!this.wizard && matches.length > 0 && this.inputGlyphs[0] === "/" && !this.modal) {
1092
1270
  const maxShow = Math.min(8, bodyH - 1);
1093
1271
  this.paletteIdx = Math.max(0, Math.min(this.paletteIdx, matches.length - 1));
1094
1272
  // scroll window that keeps the ↑↓ selection visible