@kolisachint/hoocode-agent 0.4.24 → 0.4.25

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 (58) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cli/args.d.ts +2 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +13 -4
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +27 -3
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/extensions/types.d.ts +2 -0
  10. package/dist/core/extensions/types.d.ts.map +1 -1
  11. package/dist/core/extensions/types.js.map +1 -1
  12. package/dist/core/prompt-templates.d.ts +23 -0
  13. package/dist/core/prompt-templates.d.ts.map +1 -1
  14. package/dist/core/prompt-templates.js +48 -11
  15. package/dist/core/prompt-templates.js.map +1 -1
  16. package/dist/core/resource-loader.d.ts +5 -0
  17. package/dist/core/resource-loader.d.ts.map +1 -1
  18. package/dist/core/resource-loader.js +40 -5
  19. package/dist/core/resource-loader.js.map +1 -1
  20. package/dist/core/settings-defaults.d.ts +1 -0
  21. package/dist/core/settings-defaults.d.ts.map +1 -1
  22. package/dist/core/settings-defaults.js +1 -0
  23. package/dist/core/settings-defaults.js.map +1 -1
  24. package/dist/core/settings-manager.d.ts +4 -0
  25. package/dist/core/settings-manager.d.ts.map +1 -1
  26. package/dist/core/settings-manager.js +14 -0
  27. package/dist/core/settings-manager.js.map +1 -1
  28. package/dist/core/tools/edit.d.ts.map +1 -1
  29. package/dist/core/tools/edit.js +1 -1
  30. package/dist/core/tools/edit.js.map +1 -1
  31. package/dist/core/tools/subagent.d.ts +1 -1
  32. package/dist/core/tools/subagent.d.ts.map +1 -1
  33. package/dist/core/tools/subagent.js +1 -1
  34. package/dist/core/tools/subagent.js.map +1 -1
  35. package/dist/extensions/core/hoo-core.d.ts.map +1 -1
  36. package/dist/extensions/core/hoo-core.js +4 -1
  37. package/dist/extensions/core/hoo-core.js.map +1 -1
  38. package/dist/main.d.ts.map +1 -1
  39. package/dist/main.js +4 -1
  40. package/dist/main.js.map +1 -1
  41. package/dist/modes/interactive/command-executor.d.ts +64 -0
  42. package/dist/modes/interactive/command-executor.d.ts.map +1 -0
  43. package/dist/modes/interactive/command-executor.js +547 -0
  44. package/dist/modes/interactive/command-executor.js.map +1 -0
  45. package/dist/modes/interactive/components/ask-options.d.ts.map +1 -1
  46. package/dist/modes/interactive/components/ask-options.js +2 -0
  47. package/dist/modes/interactive/components/ask-options.js.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.d.ts +9 -20
  49. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.js +86 -552
  51. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  52. package/docs/prompt-templates.md +34 -2
  53. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  54. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  55. package/examples/extensions/sandbox/package.json +1 -1
  56. package/examples/extensions/with-deps/package.json +1 -1
  57. package/examples/sdk/08-prompt-templates.ts +1 -0
  58. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"ask-options.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/ask-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACN,KAAK,SAAS,EACd,KAAK,SAAS,EAMd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAGrE,qBAAa,mBAAoB,YAAW,SAAS,EAAE,SAAS;IAC/D,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YAAY,SAAS,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,EAIhG;IAED,UAAU,IAAI,IAAI,CAAG;IAErB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,WAAW;IAgBnB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkE9B;IAED,OAAO,CAAC,OAAO;IAmBf,OAAO,CAAC,IAAI;IAOZ,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA0C9B;CACD","sourcesContent":["/**\n * Options pane — the agent asking the USER for a decision before it acts.\n *\n * Rendered inline in the transcript (never a modal), in the same boxless\n * language as the other selectors: blue rules top & bottom, a cyan\n * \"INPUT NEEDED\" label, and the active row marked with the accent cursor.\n *\n * One decision per step:\n * - tui.select.up / down move between options (wraps)\n * - tui.select.next (→) confirm the highlighted option and advance; on the\n * last step it submits every answer\n * - tui.select.back (←) step back to the previous decision\n * - 1-9 quick-pick an option\n * - drop onto the custom row and type a free-form answer when none fit\n * - tui.select.cancel (esc) skips the whole sequence\n *\n * Answered steps stay on screen as a deterministic breadcrumb so you always\n * see what you've already committed to.\n */\n\nimport {\n\ttype Component,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\ttype Keybinding,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { AskQuestion } from \"../../../core/extensions/types.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport class AskOptionsComponent implements Component, Focusable {\n\tprivate questions: AskQuestion[];\n\tprivate step = 0;\n\tprivate index = 0;\n\tprivate answers: string[] = [];\n\tprivate customInput = new Input();\n\tprivate onSubmitCallback: (answers: string[]) => void;\n\tprivate onCancelCallback: () => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.customInput.focused = value;\n\t}\n\n\tconstructor(questions: AskQuestion[], onSubmit: (answers: string[]) => void, onCancel: () => void) {\n\t\tthis.questions = questions;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t}\n\n\tinvalidate(): void {}\n\n\tprivate rowCount(q: AskQuestion): number {\n\t\treturn q.options.length + (q.allowCustom ? 1 : 0);\n\t}\n\n\tprivate isOnCustomRow(q: AskQuestion): boolean {\n\t\treturn !!q.allowCustom && this.index === q.options.length;\n\t}\n\n\tprivate spread(left: string, right: string, width: number): string {\n\t\tconst gap = Math.max(1, width - visibleWidth(left) - visibleWidth(right));\n\t\treturn truncateToWidth(`${left}${\" \".repeat(gap)}${right}`, width, \"\");\n\t}\n\n\tprivate renderHints(last: boolean): string {\n\t\tconst kb = getKeybindings();\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = (keys: string, desc: string) => theme.fg(\"dim\", keys) + theme.fg(\"muted\", ` ${desc}`);\n\t\tconst upDown = `${kb.getKeys(\"tui.select.up\").join(\"/\")}/${kb.getKeys(\"tui.select.down\").join(\"/\")}`;\n\t\tconst parts = [\n\t\t\thint(upDown, \"move\"),\n\t\t\thint(kb.getKeys(\"app.options.next\" as Keybinding).join(\"/\"), last ? \"submit\" : \"next\"),\n\t\t];\n\t\tif (this.step > 0) {\n\t\t\tparts.push(hint(kb.getKeys(\"app.options.back\" as Keybinding).join(\"/\"), \"back\"));\n\t\t}\n\t\tparts.push(hint(kb.getKeys(\"tui.select.cancel\").join(\"/\"), \"skip\"));\n\t\treturn parts.join(sep);\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst rule = theme.fg(\"borderAccent\", \"─\".repeat(Math.max(1, width)));\n\t\tconst cur = this.questions[this.step];\n\t\tconst last = this.step === this.questions.length - 1;\n\n\t\tlines.push(rule);\n\n\t\t// Header: \"INPUT NEEDED\" on the left, key hints on the right.\n\t\tconst title = theme.bold(theme.fg(\"borderAccent\", \"INPUT NEEDED\"));\n\t\tlines.push(this.spread(title, this.renderHints(last), width));\n\n\t\t// Answered-step breadcrumb.\n\t\tfor (let i = 0; i < this.step; i++) {\n\t\t\tconst q = this.questions[i];\n\t\t\tconst check = theme.fg(\"success\", \"✓\");\n\t\t\tconst label = theme.fg(\"dim\", q.short ?? q.question);\n\t\t\tconst arrow = theme.fg(\"dim\", \"→ \");\n\t\t\tconst answer = theme.fg(\"accent\", this.answers[i] ?? \"\");\n\t\t\tlines.push(truncateToWidth(`${check} ${label} ${arrow}${answer}`, width, \"...\"));\n\t\t}\n\t\tif (this.step > 0) lines.push(\"\");\n\n\t\t// Current question.\n\t\tconst stepLabel = theme.fg(\"dim\", `${this.step + 1}/${this.questions.length}`);\n\t\tlines.push(truncateToWidth(`${stepLabel} ${theme.bold(cur.question)}`, width, \"...\"));\n\t\tif (cur.detail) lines.push(truncateToWidth(theme.fg(\"muted\", ` ${cur.detail}`), width, \"...\"));\n\t\tlines.push(\"\");\n\n\t\t// Options.\n\t\tfor (let i = 0; i < cur.options.length; i++) {\n\t\t\tconst o = cur.options[i];\n\t\t\tconst active = i === this.index;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst num = theme.fg(active ? \"accent\" : \"dim\", String(i + 1));\n\t\t\tconst label = active ? theme.bold(o.label) : o.label;\n\t\t\tlet line = `${cursor} ${num} ${label}`;\n\t\t\tif (o.description) line += theme.fg(\"muted\", ` ${o.description}`);\n\t\t\tlines.push(truncateToWidth(line, width, \"...\"));\n\t\t}\n\n\t\t// Custom row.\n\t\tif (cur.allowCustom) {\n\t\t\tconst customIndex = cur.options.length;\n\t\t\tconst active = this.index === customIndex;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst plus = theme.fg(active ? \"accent\" : \"dim\", \"+\");\n\t\t\tif (active) {\n\t\t\t\tconst value = this.customInput.getValue();\n\t\t\t\tconst prompt = theme.fg(\"accent\", \">\");\n\t\t\t\tconst caret = this._focused ? theme.fg(\"accent\", \"▏\") : \"\";\n\t\t\t\tconst body = value\n\t\t\t\t\t? `${theme.fg(\"text\", value)}${caret}`\n\t\t\t\t\t: `${caret}${theme.fg(\"dim\", \"type your own answer\")}`;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${prompt} ${body}`, width, \"...\"));\n\t\t\t} else {\n\t\t\t\tconst label = theme.fg(\"muted\", \"custom answer\");\n\t\t\t\tconst desc = theme.fg(\"dim\", \"type your own\");\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${label} ${desc}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Count.\n\t\tlines.push(theme.fg(\"dim\", `(${this.index + 1}/${this.rowCount(cur)})`));\n\t\tlines.push(rule);\n\t\treturn lines;\n\t}\n\n\tprivate confirm(): void {\n\t\tconst cur = this.questions[this.step];\n\t\tlet value: string;\n\t\tif (this.isOnCustomRow(cur)) {\n\t\t\tvalue = this.customInput.getValue().trim();\n\t\t\tif (!value) return; // can't submit an empty custom answer\n\t\t} else {\n\t\t\tvalue = cur.options[this.index].label;\n\t\t}\n\t\tthis.answers[this.step] = value;\n\t\tif (this.step < this.questions.length - 1) {\n\t\t\tthis.step += 1;\n\t\t\tthis.index = 0;\n\t\t\tthis.customInput.setValue(\"\");\n\t\t} else {\n\t\t\tthis.onSubmitCallback(this.answers.slice());\n\t\t}\n\t}\n\n\tprivate back(): void {\n\t\tif (this.step === 0) return;\n\t\tthis.step -= 1;\n\t\tthis.index = 0;\n\t\tthis.customInput.setValue(\"\");\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tconst cur = this.questions[this.step];\n\t\tconst rows = this.rowCount(cur);\n\t\tconst onCustom = this.isOnCustomRow(cur);\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.index = this.index === 0 ? rows - 1 : this.index - 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.index = this.index === rows - 1 ? 0 : this.index + 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.back\" as Keybinding)) {\n\t\t\tthis.back();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.next\" as Keybinding) || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tthis.confirm();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tif (onCustom) {\n\t\t\t// Delegate free-form typing (insert, backspace, paste, word-delete)\n\t\t\t// to a hidden Input buffer; the value is rendered inline above.\n\t\t\tthis.customInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Number quick-pick for the listed options (not the custom row).\n\t\tif (/^[1-9]$/.test(data)) {\n\t\t\tconst n = Number(data) - 1;\n\t\t\tif (n < cur.options.length) {\n\t\t\t\tthis.index = n;\n\t\t\t\tthis.confirm();\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"ask-options.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/ask-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACN,KAAK,SAAS,EACd,KAAK,SAAS,EAMd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAGrE,qBAAa,mBAAoB,YAAW,SAAS,EAAE,SAAS;IAC/D,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IAErC,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YAAY,SAAS,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,EAIhG;IAED,UAAU,IAAI,IAAI,CAAG;IAErB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,WAAW;IAgBnB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmE9B;IAED,OAAO,CAAC,OAAO;IAmBf,OAAO,CAAC,IAAI;IAOZ,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA0C9B;CACD","sourcesContent":["/**\n * Options pane — the agent asking the USER for a decision before it acts.\n *\n * Rendered inline in the transcript (never a modal), in the same boxless\n * language as the other selectors: blue rules top & bottom, a cyan\n * \"INPUT NEEDED\" label, and the active row marked with the accent cursor.\n *\n * One decision per step:\n * - tui.select.up / down move between options (wraps)\n * - tui.select.next (→) confirm the highlighted option and advance; on the\n * last step it submits every answer\n * - tui.select.back (←) step back to the previous decision\n * - 1-9 quick-pick an option\n * - drop onto the custom row and type a free-form answer when none fit\n * - tui.select.cancel (esc) skips the whole sequence\n *\n * Answered steps stay on screen as a deterministic breadcrumb so you always\n * see what you've already committed to.\n */\n\nimport {\n\ttype Component,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\ttype Keybinding,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { AskQuestion } from \"../../../core/extensions/types.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport class AskOptionsComponent implements Component, Focusable {\n\tprivate questions: AskQuestion[];\n\tprivate step = 0;\n\tprivate index = 0;\n\tprivate answers: string[] = [];\n\tprivate customInput = new Input();\n\tprivate onSubmitCallback: (answers: string[]) => void;\n\tprivate onCancelCallback: () => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.customInput.focused = value;\n\t}\n\n\tconstructor(questions: AskQuestion[], onSubmit: (answers: string[]) => void, onCancel: () => void) {\n\t\tthis.questions = questions;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t}\n\n\tinvalidate(): void {}\n\n\tprivate rowCount(q: AskQuestion): number {\n\t\treturn q.options.length + (q.allowCustom ? 1 : 0);\n\t}\n\n\tprivate isOnCustomRow(q: AskQuestion): boolean {\n\t\treturn !!q.allowCustom && this.index === q.options.length;\n\t}\n\n\tprivate spread(left: string, right: string, width: number): string {\n\t\tconst gap = Math.max(1, width - visibleWidth(left) - visibleWidth(right));\n\t\treturn truncateToWidth(`${left}${\" \".repeat(gap)}${right}`, width, \"\");\n\t}\n\n\tprivate renderHints(last: boolean): string {\n\t\tconst kb = getKeybindings();\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = (keys: string, desc: string) => theme.fg(\"dim\", keys) + theme.fg(\"muted\", ` ${desc}`);\n\t\tconst upDown = `${kb.getKeys(\"tui.select.up\").join(\"/\")}/${kb.getKeys(\"tui.select.down\").join(\"/\")}`;\n\t\tconst parts = [\n\t\t\thint(upDown, \"move\"),\n\t\t\thint(kb.getKeys(\"app.options.next\" as Keybinding).join(\"/\"), last ? \"submit\" : \"next\"),\n\t\t];\n\t\tif (this.step > 0) {\n\t\t\tparts.push(hint(kb.getKeys(\"app.options.back\" as Keybinding).join(\"/\"), \"back\"));\n\t\t}\n\t\tparts.push(hint(kb.getKeys(\"tui.select.cancel\").join(\"/\"), \"skip\"));\n\t\treturn parts.join(sep);\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst rule = theme.fg(\"borderAccent\", \"─\".repeat(Math.max(1, width)));\n\t\tconst cur = this.questions[this.step];\n\t\tconst last = this.step === this.questions.length - 1;\n\n\t\tlines.push(rule);\n\n\t\t// Header: \"INPUT NEEDED\" on the left, key hints on the right.\n\t\tconst title = theme.bold(theme.fg(\"borderAccent\", \"INPUT NEEDED\"));\n\t\tlines.push(this.spread(title, this.renderHints(last), width));\n\n\t\t// Answered-step breadcrumb.\n\t\tfor (let i = 0; i < this.step; i++) {\n\t\t\tconst q = this.questions[i];\n\t\t\tconst check = theme.fg(\"success\", \"✓\");\n\t\t\tconst label = theme.fg(\"dim\", q.short ?? q.question);\n\t\t\tconst arrow = theme.fg(\"dim\", \"→ \");\n\t\t\tconst answer = theme.fg(\"accent\", this.answers[i] ?? \"\");\n\t\t\tlines.push(truncateToWidth(`${check} ${label} ${arrow}${answer}`, width, \"...\"));\n\t\t}\n\t\tif (this.step > 0) lines.push(\"\");\n\n\t\t// Current question.\n\t\tconst stepLabel = theme.fg(\"dim\", `${this.step + 1}/${this.questions.length}`);\n\t\tlines.push(truncateToWidth(`${stepLabel} ${theme.bold(cur.question)}`, width, \"...\"));\n\t\tif (cur.detail) lines.push(truncateToWidth(theme.fg(\"muted\", ` ${cur.detail}`), width, \"...\"));\n\t\tlines.push(\"\");\n\n\t\t// Options.\n\t\tfor (let i = 0; i < cur.options.length; i++) {\n\t\t\tconst o = cur.options[i];\n\t\t\tconst active = i === this.index;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst num = theme.fg(active ? \"accent\" : \"dim\", String(i + 1));\n\t\t\tconst label = active ? theme.bold(o.label) : o.label;\n\t\t\tlet line = `${cursor} ${num} ${label}`;\n\t\t\tif (o.recommended) line += theme.fg(\"success\", \" (recommended)\");\n\t\t\tif (o.description) line += theme.fg(\"muted\", ` ${o.description}`);\n\t\t\tlines.push(truncateToWidth(line, width, \"...\"));\n\t\t}\n\n\t\t// Custom row.\n\t\tif (cur.allowCustom) {\n\t\t\tconst customIndex = cur.options.length;\n\t\t\tconst active = this.index === customIndex;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst plus = theme.fg(active ? \"accent\" : \"dim\", \"+\");\n\t\t\tif (active) {\n\t\t\t\tconst value = this.customInput.getValue();\n\t\t\t\tconst prompt = theme.fg(\"accent\", \">\");\n\t\t\t\tconst caret = this._focused ? theme.fg(\"accent\", \"▏\") : \"\";\n\t\t\t\tconst body = value\n\t\t\t\t\t? `${theme.fg(\"text\", value)}${caret}`\n\t\t\t\t\t: `${caret}${theme.fg(\"dim\", \"type your own answer\")}`;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${prompt} ${body}`, width, \"...\"));\n\t\t\t} else {\n\t\t\t\tconst label = theme.fg(\"muted\", \"custom answer\");\n\t\t\t\tconst desc = theme.fg(\"dim\", \"type your own\");\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${label} ${desc}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Count.\n\t\tlines.push(theme.fg(\"dim\", `(${this.index + 1}/${this.rowCount(cur)})`));\n\t\tlines.push(rule);\n\t\treturn lines;\n\t}\n\n\tprivate confirm(): void {\n\t\tconst cur = this.questions[this.step];\n\t\tlet value: string;\n\t\tif (this.isOnCustomRow(cur)) {\n\t\t\tvalue = this.customInput.getValue().trim();\n\t\t\tif (!value) return; // can't submit an empty custom answer\n\t\t} else {\n\t\t\tvalue = cur.options[this.index].label;\n\t\t}\n\t\tthis.answers[this.step] = value;\n\t\tif (this.step < this.questions.length - 1) {\n\t\t\tthis.step += 1;\n\t\t\tthis.index = 0;\n\t\t\tthis.customInput.setValue(\"\");\n\t\t} else {\n\t\t\tthis.onSubmitCallback(this.answers.slice());\n\t\t}\n\t}\n\n\tprivate back(): void {\n\t\tif (this.step === 0) return;\n\t\tthis.step -= 1;\n\t\tthis.index = 0;\n\t\tthis.customInput.setValue(\"\");\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tconst cur = this.questions[this.step];\n\t\tconst rows = this.rowCount(cur);\n\t\tconst onCustom = this.isOnCustomRow(cur);\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.index = this.index === 0 ? rows - 1 : this.index - 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.index = this.index === rows - 1 ? 0 : this.index + 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.back\" as Keybinding)) {\n\t\t\tthis.back();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.next\" as Keybinding) || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tthis.confirm();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tif (onCustom) {\n\t\t\t// Delegate free-form typing (insert, backspace, paste, word-delete)\n\t\t\t// to a hidden Input buffer; the value is rendered inline above.\n\t\t\tthis.customInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Number quick-pick for the listed options (not the custom row).\n\t\tif (/^[1-9]$/.test(data)) {\n\t\t\tconst n = Number(data) - 1;\n\t\t\tif (n < cur.options.length) {\n\t\t\t\tthis.index = n;\n\t\t\t\tthis.confirm();\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -100,6 +100,8 @@ export class AskOptionsComponent {
100
100
  const num = theme.fg(active ? "accent" : "dim", String(i + 1));
101
101
  const label = active ? theme.bold(o.label) : o.label;
102
102
  let line = `${cursor} ${num} ${label}`;
103
+ if (o.recommended)
104
+ line += theme.fg("success", " (recommended)");
103
105
  if (o.description)
104
106
  line += theme.fg("muted", ` ${o.description}`);
105
107
  lines.push(truncateToWidth(line, width, "..."));
@@ -1 +1 @@
1
- {"version":3,"file":"ask-options.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/ask-options.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options pane — the agent asking the USER for a decision before it acts.\n *\n * Rendered inline in the transcript (never a modal), in the same boxless\n * language as the other selectors: blue rules top & bottom, a cyan\n * \"INPUT NEEDED\" label, and the active row marked with the accent cursor.\n *\n * One decision per step:\n * - tui.select.up / down move between options (wraps)\n * - tui.select.next (→) confirm the highlighted option and advance; on the\n * last step it submits every answer\n * - tui.select.back (←) step back to the previous decision\n * - 1-9 quick-pick an option\n * - drop onto the custom row and type a free-form answer when none fit\n * - tui.select.cancel (esc) skips the whole sequence\n *\n * Answered steps stay on screen as a deterministic breadcrumb so you always\n * see what you've already committed to.\n */\n\nimport {\n\ttype Component,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\ttype Keybinding,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { AskQuestion } from \"../../../core/extensions/types.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport class AskOptionsComponent implements Component, Focusable {\n\tprivate questions: AskQuestion[];\n\tprivate step = 0;\n\tprivate index = 0;\n\tprivate answers: string[] = [];\n\tprivate customInput = new Input();\n\tprivate onSubmitCallback: (answers: string[]) => void;\n\tprivate onCancelCallback: () => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.customInput.focused = value;\n\t}\n\n\tconstructor(questions: AskQuestion[], onSubmit: (answers: string[]) => void, onCancel: () => void) {\n\t\tthis.questions = questions;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t}\n\n\tinvalidate(): void {}\n\n\tprivate rowCount(q: AskQuestion): number {\n\t\treturn q.options.length + (q.allowCustom ? 1 : 0);\n\t}\n\n\tprivate isOnCustomRow(q: AskQuestion): boolean {\n\t\treturn !!q.allowCustom && this.index === q.options.length;\n\t}\n\n\tprivate spread(left: string, right: string, width: number): string {\n\t\tconst gap = Math.max(1, width - visibleWidth(left) - visibleWidth(right));\n\t\treturn truncateToWidth(`${left}${\" \".repeat(gap)}${right}`, width, \"\");\n\t}\n\n\tprivate renderHints(last: boolean): string {\n\t\tconst kb = getKeybindings();\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = (keys: string, desc: string) => theme.fg(\"dim\", keys) + theme.fg(\"muted\", ` ${desc}`);\n\t\tconst upDown = `${kb.getKeys(\"tui.select.up\").join(\"/\")}/${kb.getKeys(\"tui.select.down\").join(\"/\")}`;\n\t\tconst parts = [\n\t\t\thint(upDown, \"move\"),\n\t\t\thint(kb.getKeys(\"app.options.next\" as Keybinding).join(\"/\"), last ? \"submit\" : \"next\"),\n\t\t];\n\t\tif (this.step > 0) {\n\t\t\tparts.push(hint(kb.getKeys(\"app.options.back\" as Keybinding).join(\"/\"), \"back\"));\n\t\t}\n\t\tparts.push(hint(kb.getKeys(\"tui.select.cancel\").join(\"/\"), \"skip\"));\n\t\treturn parts.join(sep);\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst rule = theme.fg(\"borderAccent\", \"─\".repeat(Math.max(1, width)));\n\t\tconst cur = this.questions[this.step];\n\t\tconst last = this.step === this.questions.length - 1;\n\n\t\tlines.push(rule);\n\n\t\t// Header: \"INPUT NEEDED\" on the left, key hints on the right.\n\t\tconst title = theme.bold(theme.fg(\"borderAccent\", \"INPUT NEEDED\"));\n\t\tlines.push(this.spread(title, this.renderHints(last), width));\n\n\t\t// Answered-step breadcrumb.\n\t\tfor (let i = 0; i < this.step; i++) {\n\t\t\tconst q = this.questions[i];\n\t\t\tconst check = theme.fg(\"success\", \"✓\");\n\t\t\tconst label = theme.fg(\"dim\", q.short ?? q.question);\n\t\t\tconst arrow = theme.fg(\"dim\", \"→ \");\n\t\t\tconst answer = theme.fg(\"accent\", this.answers[i] ?? \"\");\n\t\t\tlines.push(truncateToWidth(`${check} ${label} ${arrow}${answer}`, width, \"...\"));\n\t\t}\n\t\tif (this.step > 0) lines.push(\"\");\n\n\t\t// Current question.\n\t\tconst stepLabel = theme.fg(\"dim\", `${this.step + 1}/${this.questions.length}`);\n\t\tlines.push(truncateToWidth(`${stepLabel} ${theme.bold(cur.question)}`, width, \"...\"));\n\t\tif (cur.detail) lines.push(truncateToWidth(theme.fg(\"muted\", ` ${cur.detail}`), width, \"...\"));\n\t\tlines.push(\"\");\n\n\t\t// Options.\n\t\tfor (let i = 0; i < cur.options.length; i++) {\n\t\t\tconst o = cur.options[i];\n\t\t\tconst active = i === this.index;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst num = theme.fg(active ? \"accent\" : \"dim\", String(i + 1));\n\t\t\tconst label = active ? theme.bold(o.label) : o.label;\n\t\t\tlet line = `${cursor} ${num} ${label}`;\n\t\t\tif (o.description) line += theme.fg(\"muted\", ` ${o.description}`);\n\t\t\tlines.push(truncateToWidth(line, width, \"...\"));\n\t\t}\n\n\t\t// Custom row.\n\t\tif (cur.allowCustom) {\n\t\t\tconst customIndex = cur.options.length;\n\t\t\tconst active = this.index === customIndex;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst plus = theme.fg(active ? \"accent\" : \"dim\", \"+\");\n\t\t\tif (active) {\n\t\t\t\tconst value = this.customInput.getValue();\n\t\t\t\tconst prompt = theme.fg(\"accent\", \">\");\n\t\t\t\tconst caret = this._focused ? theme.fg(\"accent\", \"▏\") : \"\";\n\t\t\t\tconst body = value\n\t\t\t\t\t? `${theme.fg(\"text\", value)}${caret}`\n\t\t\t\t\t: `${caret}${theme.fg(\"dim\", \"type your own answer\")}`;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${prompt} ${body}`, width, \"...\"));\n\t\t\t} else {\n\t\t\t\tconst label = theme.fg(\"muted\", \"custom answer\");\n\t\t\t\tconst desc = theme.fg(\"dim\", \"type your own\");\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${label} ${desc}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Count.\n\t\tlines.push(theme.fg(\"dim\", `(${this.index + 1}/${this.rowCount(cur)})`));\n\t\tlines.push(rule);\n\t\treturn lines;\n\t}\n\n\tprivate confirm(): void {\n\t\tconst cur = this.questions[this.step];\n\t\tlet value: string;\n\t\tif (this.isOnCustomRow(cur)) {\n\t\t\tvalue = this.customInput.getValue().trim();\n\t\t\tif (!value) return; // can't submit an empty custom answer\n\t\t} else {\n\t\t\tvalue = cur.options[this.index].label;\n\t\t}\n\t\tthis.answers[this.step] = value;\n\t\tif (this.step < this.questions.length - 1) {\n\t\t\tthis.step += 1;\n\t\t\tthis.index = 0;\n\t\t\tthis.customInput.setValue(\"\");\n\t\t} else {\n\t\t\tthis.onSubmitCallback(this.answers.slice());\n\t\t}\n\t}\n\n\tprivate back(): void {\n\t\tif (this.step === 0) return;\n\t\tthis.step -= 1;\n\t\tthis.index = 0;\n\t\tthis.customInput.setValue(\"\");\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tconst cur = this.questions[this.step];\n\t\tconst rows = this.rowCount(cur);\n\t\tconst onCustom = this.isOnCustomRow(cur);\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.index = this.index === 0 ? rows - 1 : this.index - 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.index = this.index === rows - 1 ? 0 : this.index + 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.back\" as Keybinding)) {\n\t\t\tthis.back();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.next\" as Keybinding) || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tthis.confirm();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tif (onCustom) {\n\t\t\t// Delegate free-form typing (insert, backspace, paste, word-delete)\n\t\t\t// to a hidden Input buffer; the value is rendered inline above.\n\t\t\tthis.customInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Number quick-pick for the listed options (not the custom row).\n\t\tif (/^[1-9]$/.test(data)) {\n\t\t\tconst n = Number(data) - 1;\n\t\t\tif (n < cur.options.length) {\n\t\t\t\tthis.index = n;\n\t\t\t\tthis.confirm();\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"ask-options.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/ask-options.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options pane — the agent asking the USER for a decision before it acts.\n *\n * Rendered inline in the transcript (never a modal), in the same boxless\n * language as the other selectors: blue rules top & bottom, a cyan\n * \"INPUT NEEDED\" label, and the active row marked with the accent cursor.\n *\n * One decision per step:\n * - tui.select.up / down move between options (wraps)\n * - tui.select.next (→) confirm the highlighted option and advance; on the\n * last step it submits every answer\n * - tui.select.back (←) step back to the previous decision\n * - 1-9 quick-pick an option\n * - drop onto the custom row and type a free-form answer when none fit\n * - tui.select.cancel (esc) skips the whole sequence\n *\n * Answered steps stay on screen as a deterministic breadcrumb so you always\n * see what you've already committed to.\n */\n\nimport {\n\ttype Component,\n\ttype Focusable,\n\tgetKeybindings,\n\tInput,\n\ttype Keybinding,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@kolisachint/hoocode-tui\";\nimport type { AskQuestion } from \"../../../core/extensions/types.js\";\nimport { theme } from \"../theme/theme.js\";\n\nexport class AskOptionsComponent implements Component, Focusable {\n\tprivate questions: AskQuestion[];\n\tprivate step = 0;\n\tprivate index = 0;\n\tprivate answers: string[] = [];\n\tprivate customInput = new Input();\n\tprivate onSubmitCallback: (answers: string[]) => void;\n\tprivate onCancelCallback: () => void;\n\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.customInput.focused = value;\n\t}\n\n\tconstructor(questions: AskQuestion[], onSubmit: (answers: string[]) => void, onCancel: () => void) {\n\t\tthis.questions = questions;\n\t\tthis.onSubmitCallback = onSubmit;\n\t\tthis.onCancelCallback = onCancel;\n\t}\n\n\tinvalidate(): void {}\n\n\tprivate rowCount(q: AskQuestion): number {\n\t\treturn q.options.length + (q.allowCustom ? 1 : 0);\n\t}\n\n\tprivate isOnCustomRow(q: AskQuestion): boolean {\n\t\treturn !!q.allowCustom && this.index === q.options.length;\n\t}\n\n\tprivate spread(left: string, right: string, width: number): string {\n\t\tconst gap = Math.max(1, width - visibleWidth(left) - visibleWidth(right));\n\t\treturn truncateToWidth(`${left}${\" \".repeat(gap)}${right}`, width, \"\");\n\t}\n\n\tprivate renderHints(last: boolean): string {\n\t\tconst kb = getKeybindings();\n\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\tconst hint = (keys: string, desc: string) => theme.fg(\"dim\", keys) + theme.fg(\"muted\", ` ${desc}`);\n\t\tconst upDown = `${kb.getKeys(\"tui.select.up\").join(\"/\")}/${kb.getKeys(\"tui.select.down\").join(\"/\")}`;\n\t\tconst parts = [\n\t\t\thint(upDown, \"move\"),\n\t\t\thint(kb.getKeys(\"app.options.next\" as Keybinding).join(\"/\"), last ? \"submit\" : \"next\"),\n\t\t];\n\t\tif (this.step > 0) {\n\t\t\tparts.push(hint(kb.getKeys(\"app.options.back\" as Keybinding).join(\"/\"), \"back\"));\n\t\t}\n\t\tparts.push(hint(kb.getKeys(\"tui.select.cancel\").join(\"/\"), \"skip\"));\n\t\treturn parts.join(sep);\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst rule = theme.fg(\"borderAccent\", \"─\".repeat(Math.max(1, width)));\n\t\tconst cur = this.questions[this.step];\n\t\tconst last = this.step === this.questions.length - 1;\n\n\t\tlines.push(rule);\n\n\t\t// Header: \"INPUT NEEDED\" on the left, key hints on the right.\n\t\tconst title = theme.bold(theme.fg(\"borderAccent\", \"INPUT NEEDED\"));\n\t\tlines.push(this.spread(title, this.renderHints(last), width));\n\n\t\t// Answered-step breadcrumb.\n\t\tfor (let i = 0; i < this.step; i++) {\n\t\t\tconst q = this.questions[i];\n\t\t\tconst check = theme.fg(\"success\", \"✓\");\n\t\t\tconst label = theme.fg(\"dim\", q.short ?? q.question);\n\t\t\tconst arrow = theme.fg(\"dim\", \"→ \");\n\t\t\tconst answer = theme.fg(\"accent\", this.answers[i] ?? \"\");\n\t\t\tlines.push(truncateToWidth(`${check} ${label} ${arrow}${answer}`, width, \"...\"));\n\t\t}\n\t\tif (this.step > 0) lines.push(\"\");\n\n\t\t// Current question.\n\t\tconst stepLabel = theme.fg(\"dim\", `${this.step + 1}/${this.questions.length}`);\n\t\tlines.push(truncateToWidth(`${stepLabel} ${theme.bold(cur.question)}`, width, \"...\"));\n\t\tif (cur.detail) lines.push(truncateToWidth(theme.fg(\"muted\", ` ${cur.detail}`), width, \"...\"));\n\t\tlines.push(\"\");\n\n\t\t// Options.\n\t\tfor (let i = 0; i < cur.options.length; i++) {\n\t\t\tconst o = cur.options[i];\n\t\t\tconst active = i === this.index;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst num = theme.fg(active ? \"accent\" : \"dim\", String(i + 1));\n\t\t\tconst label = active ? theme.bold(o.label) : o.label;\n\t\t\tlet line = `${cursor} ${num} ${label}`;\n\t\t\tif (o.recommended) line += theme.fg(\"success\", \" (recommended)\");\n\t\t\tif (o.description) line += theme.fg(\"muted\", ` ${o.description}`);\n\t\t\tlines.push(truncateToWidth(line, width, \"...\"));\n\t\t}\n\n\t\t// Custom row.\n\t\tif (cur.allowCustom) {\n\t\t\tconst customIndex = cur.options.length;\n\t\t\tconst active = this.index === customIndex;\n\t\t\tconst cursor = theme.fg(\"accent\", active ? \">\" : \" \");\n\t\t\tconst plus = theme.fg(active ? \"accent\" : \"dim\", \"+\");\n\t\t\tif (active) {\n\t\t\t\tconst value = this.customInput.getValue();\n\t\t\t\tconst prompt = theme.fg(\"accent\", \">\");\n\t\t\t\tconst caret = this._focused ? theme.fg(\"accent\", \"▏\") : \"\";\n\t\t\t\tconst body = value\n\t\t\t\t\t? `${theme.fg(\"text\", value)}${caret}`\n\t\t\t\t\t: `${caret}${theme.fg(\"dim\", \"type your own answer\")}`;\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${prompt} ${body}`, width, \"...\"));\n\t\t\t} else {\n\t\t\t\tconst label = theme.fg(\"muted\", \"custom answer\");\n\t\t\t\tconst desc = theme.fg(\"dim\", \"type your own\");\n\t\t\t\tlines.push(truncateToWidth(`${cursor} ${plus} ${label} ${desc}`, width, \"...\"));\n\t\t\t}\n\t\t}\n\n\t\t// Count.\n\t\tlines.push(theme.fg(\"dim\", `(${this.index + 1}/${this.rowCount(cur)})`));\n\t\tlines.push(rule);\n\t\treturn lines;\n\t}\n\n\tprivate confirm(): void {\n\t\tconst cur = this.questions[this.step];\n\t\tlet value: string;\n\t\tif (this.isOnCustomRow(cur)) {\n\t\t\tvalue = this.customInput.getValue().trim();\n\t\t\tif (!value) return; // can't submit an empty custom answer\n\t\t} else {\n\t\t\tvalue = cur.options[this.index].label;\n\t\t}\n\t\tthis.answers[this.step] = value;\n\t\tif (this.step < this.questions.length - 1) {\n\t\t\tthis.step += 1;\n\t\t\tthis.index = 0;\n\t\t\tthis.customInput.setValue(\"\");\n\t\t} else {\n\t\t\tthis.onSubmitCallback(this.answers.slice());\n\t\t}\n\t}\n\n\tprivate back(): void {\n\t\tif (this.step === 0) return;\n\t\tthis.step -= 1;\n\t\tthis.index = 0;\n\t\tthis.customInput.setValue(\"\");\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tconst cur = this.questions[this.step];\n\t\tconst rows = this.rowCount(cur);\n\t\tconst onCustom = this.isOnCustomRow(cur);\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.index = this.index === 0 ? rows - 1 : this.index - 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.index = this.index === rows - 1 ? 0 : this.index + 1;\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.back\" as Keybinding)) {\n\t\t\tthis.back();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"app.options.next\" as Keybinding) || kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tthis.confirm();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t\treturn;\n\t\t}\n\n\t\tif (onCustom) {\n\t\t\t// Delegate free-form typing (insert, backspace, paste, word-delete)\n\t\t\t// to a hidden Input buffer; the value is rendered inline above.\n\t\t\tthis.customInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\n\t\t// Number quick-pick for the listed options (not the custom row).\n\t\tif (/^[1-9]$/.test(data)) {\n\t\t\tconst n = Number(data) - 1;\n\t\t\tif (n < cur.options.length) {\n\t\t\t\tthis.index = n;\n\t\t\t\tthis.confirm();\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -3,7 +3,7 @@
3
3
  * Handles TUI rendering and user interaction, delegating business logic to AgentSession.
4
4
  */
5
5
  import { type ImageContent } from "@kolisachint/hoocode-ai";
6
- import { type AgentSessionRuntime } from "../../core/agent-session-runtime.js";
6
+ import type { AgentSessionRuntime } from "../../core/agent-session-runtime.js";
7
7
  export declare function isApiKeyLoginProvider(providerId: string, oauthProviderIds: ReadonlySet<string>, builtInProviderIds?: ReadonlySet<string>): boolean;
8
8
  /**
9
9
  * Options for InteractiveMode initialization.
@@ -90,6 +90,14 @@ export declare class InteractiveMode {
90
90
  private builtInHeader;
91
91
  private customHeader;
92
92
  private get session();
93
+ private _commandExecutor?;
94
+ /**
95
+ * Lazily-built command executor. The context uses getters for mutable
96
+ * dependencies (e.g. the active session) so handlers always operate on the
97
+ * current state even after a session switch.
98
+ */
99
+ private get commandExecutor();
100
+ private stopLoadingAnimation;
93
101
  private get agent();
94
102
  private get sessionManager();
95
103
  private get settingsManager();
@@ -317,7 +325,6 @@ export declare class InteractiveMode {
317
325
  */
318
326
  private showSelector;
319
327
  private showSettingsSelector;
320
- private handleModelCommand;
321
328
  private findExactModelMatch;
322
329
  private getModelCandidates;
323
330
  private updateAvailableProviderCount;
@@ -325,8 +332,6 @@ export declare class InteractiveMode {
325
332
  private showModelSelector;
326
333
  private showModelsSelector;
327
334
  private showUserMessageSelector;
328
- private handleCloneCommand;
329
- private handleSubagentCommand;
330
335
  private showTreeSelector;
331
336
  private showSessionSelector;
332
337
  private handleResumeSession;
@@ -341,26 +346,10 @@ export declare class InteractiveMode {
341
346
  private showOAuthLoginSelect;
342
347
  private showLoginDialog;
343
348
  private handleReloadCommand;
344
- private handleExportCommand;
345
- private getPathCommandArgument;
346
- private handleImportCommand;
347
- private handleShareCommand;
348
- private handleCopyCommand;
349
- private handleNameCommand;
350
- private handleSessionCommand;
351
- private handleChangelogCommand;
352
349
  /**
353
350
  * Get capitalized display string for an app keybinding action.
354
351
  */
355
352
  private getAppKeyDisplay;
356
- /**
357
- * Get capitalized display string for an editor keybinding action.
358
- */
359
- private getEditorKeyDisplay;
360
- private handleHotkeysCommand;
361
- private handleClearCommand;
362
- private handleDebugCommand;
363
- private handleArminSaysHi;
364
353
  private handleDaxnuts;
365
354
  private checkDaxnutsEasterEgg;
366
355
  private handleBashCommand;