silvery 0.3.0 → 0.4.1

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 (120) hide show
  1. package/README.md +41 -145
  2. package/dist/chalk.js +3 -0
  3. package/dist/chalk.js.map +11 -0
  4. package/dist/index.js +340 -0
  5. package/dist/index.js.map +282 -0
  6. package/dist/ink.js +129 -0
  7. package/dist/ink.js.map +140 -0
  8. package/dist/runtime.js +394 -0
  9. package/dist/runtime.js.map +286 -0
  10. package/dist/theme.js +343 -0
  11. package/dist/theme.js.map +286 -0
  12. package/dist/ui/animation.js +3 -0
  13. package/dist/ui/animation.js.map +15 -0
  14. package/dist/ui/ansi.js +3 -0
  15. package/dist/ui/ansi.js.map +10 -0
  16. package/dist/ui/cli.js +8 -0
  17. package/dist/ui/cli.js.map +14 -0
  18. package/dist/ui/display.js +4 -0
  19. package/dist/ui/display.js.map +10 -0
  20. package/dist/ui/image.js +4 -0
  21. package/dist/ui/image.js.map +15 -0
  22. package/dist/ui/input.js +3 -0
  23. package/dist/ui/input.js.map +11 -0
  24. package/dist/ui/progress.js +8 -0
  25. package/dist/ui/progress.js.map +20 -0
  26. package/dist/ui/react.js +3 -0
  27. package/dist/ui/react.js.map +15 -0
  28. package/dist/ui/utils.js +3 -0
  29. package/dist/ui/utils.js.map +10 -0
  30. package/dist/ui/wrappers.js +14 -0
  31. package/dist/ui/wrappers.js.map +19 -0
  32. package/dist/ui.js +17 -0
  33. package/dist/ui.js.map +20 -0
  34. package/package.json +67 -15
  35. package/src/index.ts +67 -1
  36. package/src/runtime.ts +4 -0
  37. package/src/theme.ts +4 -0
  38. package/src/ui/animation.ts +2 -0
  39. package/src/ui/ansi.ts +2 -0
  40. package/src/ui/cli.ts +2 -0
  41. package/src/ui/display.ts +2 -0
  42. package/src/ui/image.ts +2 -0
  43. package/src/ui/input.ts +2 -0
  44. package/src/ui/progress.ts +2 -0
  45. package/src/ui/react.ts +2 -0
  46. package/src/ui/utils.ts +2 -0
  47. package/src/ui/wrappers.ts +2 -0
  48. package/src/ui.ts +4 -0
  49. package/examples/CLAUDE.md +0 -75
  50. package/examples/_banner.tsx +0 -60
  51. package/examples/cli.ts +0 -228
  52. package/examples/index.md +0 -101
  53. package/examples/inline/inline-nontty.tsx +0 -98
  54. package/examples/inline/inline-progress.tsx +0 -79
  55. package/examples/inline/inline-simple.tsx +0 -63
  56. package/examples/inline/scrollback.tsx +0 -185
  57. package/examples/interactive/_input-debug.tsx +0 -110
  58. package/examples/interactive/_stdin-test.ts +0 -71
  59. package/examples/interactive/_textarea-bare.tsx +0 -45
  60. package/examples/interactive/aichat/components.tsx +0 -468
  61. package/examples/interactive/aichat/index.tsx +0 -207
  62. package/examples/interactive/aichat/script.ts +0 -460
  63. package/examples/interactive/aichat/state.ts +0 -326
  64. package/examples/interactive/aichat/types.ts +0 -19
  65. package/examples/interactive/app-todo.tsx +0 -198
  66. package/examples/interactive/async-data.tsx +0 -208
  67. package/examples/interactive/cli-wizard.tsx +0 -332
  68. package/examples/interactive/clipboard.tsx +0 -183
  69. package/examples/interactive/components.tsx +0 -463
  70. package/examples/interactive/data-explorer.tsx +0 -506
  71. package/examples/interactive/dev-tools.tsx +0 -379
  72. package/examples/interactive/explorer.tsx +0 -747
  73. package/examples/interactive/gallery.tsx +0 -652
  74. package/examples/interactive/inline-bench.tsx +0 -136
  75. package/examples/interactive/kanban.tsx +0 -267
  76. package/examples/interactive/layout-ref.tsx +0 -185
  77. package/examples/interactive/outline.tsx +0 -171
  78. package/examples/interactive/paste-demo.tsx +0 -198
  79. package/examples/interactive/scroll.tsx +0 -77
  80. package/examples/interactive/search-filter.tsx +0 -240
  81. package/examples/interactive/task-list.tsx +0 -279
  82. package/examples/interactive/terminal.tsx +0 -798
  83. package/examples/interactive/textarea.tsx +0 -103
  84. package/examples/interactive/theme.tsx +0 -336
  85. package/examples/interactive/transform.tsx +0 -256
  86. package/examples/interactive/virtual-10k.tsx +0 -413
  87. package/examples/kitty/canvas.tsx +0 -519
  88. package/examples/kitty/generate-samples.ts +0 -236
  89. package/examples/kitty/image-component.tsx +0 -273
  90. package/examples/kitty/images.tsx +0 -604
  91. package/examples/kitty/input.tsx +0 -371
  92. package/examples/kitty/keys.tsx +0 -378
  93. package/examples/kitty/paint.tsx +0 -1017
  94. package/examples/layout/dashboard.tsx +0 -551
  95. package/examples/layout/live-resize.tsx +0 -290
  96. package/examples/layout/overflow.tsx +0 -51
  97. package/examples/playground/README.md +0 -69
  98. package/examples/playground/build.ts +0 -61
  99. package/examples/playground/index.html +0 -420
  100. package/examples/playground/playground-app.tsx +0 -416
  101. package/examples/runtime/elm-counter.tsx +0 -206
  102. package/examples/runtime/hello-runtime.tsx +0 -73
  103. package/examples/runtime/pipe-composition.tsx +0 -184
  104. package/examples/runtime/run-counter.tsx +0 -78
  105. package/examples/runtime/runtime-counter.tsx +0 -197
  106. package/examples/screenshots/generate.tsx +0 -563
  107. package/examples/scrollback-perf.tsx +0 -230
  108. package/examples/viewer.tsx +0 -654
  109. package/examples/web/build.ts +0 -365
  110. package/examples/web/canvas-app.tsx +0 -80
  111. package/examples/web/canvas.html +0 -89
  112. package/examples/web/dom-app.tsx +0 -81
  113. package/examples/web/dom.html +0 -113
  114. package/examples/web/showcase-app.tsx +0 -107
  115. package/examples/web/showcase.html +0 -34
  116. package/examples/web/showcases/index.tsx +0 -56
  117. package/examples/web/viewer-app.tsx +0 -555
  118. package/examples/web/viewer.html +0 -30
  119. package/examples/web/xterm-app.tsx +0 -105
  120. package/examples/web/xterm.html +0 -118
@@ -0,0 +1,19 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../packages/ag-react/src/ui/cli/spinner.ts", "../../packages/ag-react/src/ui/cli/ansi.ts", "../../packages/ag-react/src/ui/wrappers/with-spinner.ts", "../../packages/ag-react/src/ui/cli/progress-bar.ts", "../../packages/ag-react/src/ui/utils/eta.ts", "../../packages/ag-react/src/ui/wrappers/with-progress.ts", "../../packages/ag-react/src/ui/wrappers/wrap-generator.ts", "../../packages/ag-react/src/ui/wrappers/wrap-emitter.ts", "../../packages/ag-react/src/ui/wrappers/with-select.ts", "../../packages/ag-react/src/ui/wrappers/with-text-input.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * CLI Spinner - Animated indeterminate progress indicator\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerOptions, SpinnerStyle } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"./ansi\"\n\n/** Spinner animation frames by style */\nexport const SPINNER_FRAMES: Record<SpinnerStyle, string[]> = {\n dots: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n line: [\"-\", \"\\\\\", \"|\", \"/\"],\n arc: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n bounce: [\"⠁\", \"⠂\", \"⠄\", \"⠂\"],\n pulse: [\"█\", \"▓\", \"▒\", \"░\", \"▒\", \"▓\"],\n}\n\n/** Default intervals for each style (ms) */\nexport const SPINNER_INTERVALS: Record<SpinnerStyle, number> = {\n dots: 80,\n line: 120,\n arc: 100,\n bounce: 120,\n pulse: 100,\n}\n\n/**\n * Spinner class for CLI progress indication\n *\n * @example\n * ```ts\n * const spinner = new Spinner(\"Loading...\");\n * spinner.start();\n * await doWork();\n * spinner.succeed(\"Done!\");\n * ```\n */\nexport class Spinner {\n private text: string\n private style: SpinnerStyle\n private color: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private interval: number\n\n private frameIndex = 0\n private timer: ReturnType<typeof setInterval> | null = null\n private isSpinning = false\n\n constructor(textOrOptions?: string | SpinnerOptions) {\n const options: SpinnerOptions = typeof textOrOptions === \"string\" ? { text: textOrOptions } : (textOrOptions ?? {})\n\n this.text = options.text ?? \"\"\n this.style = options.style ?? \"dots\"\n this.color = options.color ?? \"cyan\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.interval = options.interval ?? SPINNER_INTERVALS[this.style]\n }\n\n /** Get current spinner text */\n get currentText(): string {\n return this.text\n }\n\n /** Set spinner text (updates immediately if spinning) */\n set currentText(value: string) {\n this.text = value\n if (this.isSpinning) {\n this.render()\n }\n }\n\n /** Check if spinner is currently active */\n get spinning(): boolean {\n return this.isSpinning\n }\n\n /**\n * Start the spinner animation\n */\n start(text?: string): this {\n if (text !== undefined) {\n this.text = text\n }\n\n if (this.isSpinning) {\n return this\n }\n\n this.isSpinning = true\n this.frameIndex = 0\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES[this.style].length\n this.render()\n }, this.interval)\n\n return this\n }\n\n /**\n * Stop the spinner\n */\n stop(): this {\n if (!this.isSpinning) {\n return this\n }\n\n this.isSpinning = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n this.clear()\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /**\n * Stop with success message (green checkmark)\n */\n succeed(text?: string): this {\n return this.stopWithSymbol(chalk.green(\"✔\"), text ?? this.text)\n }\n\n /**\n * Stop with failure message (red X)\n */\n fail(text?: string): this {\n return this.stopWithSymbol(chalk.red(\"✖\"), text ?? this.text)\n }\n\n /**\n * Stop with warning message (yellow warning)\n */\n warn(text?: string): this {\n return this.stopWithSymbol(chalk.yellow(\"⚠\"), text ?? this.text)\n }\n\n /**\n * Stop with info message (blue info)\n */\n info(text?: string): this {\n return this.stopWithSymbol(chalk.blue(\"ℹ\"), text ?? this.text)\n }\n\n /**\n * Clear the spinner line\n */\n clear(): this {\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n }\n return this\n }\n\n private render(): void {\n const frame = SPINNER_FRAMES[this.style][this.frameIndex]\n const colorFn = (chalk as unknown as Record<string, (s: string) => string>)[this.color]\n const coloredFrame = colorFn ? colorFn(frame!) : frame!\n const output = this.text ? `${coloredFrame} ${this.text}` : coloredFrame\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n private stopWithSymbol(symbol: string, text: string): this {\n this.stop()\n write(`${symbol} ${text}\\n`, this.stream)\n return this\n }\n\n /**\n * Dispose the spinner (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Static helper to quickly start a spinner\n * Returns a stop function\n *\n * @example\n * ```ts\n * const stop = Spinner.start(\"Loading...\");\n * await doWork();\n * stop();\n * ```\n */\n static start(textOrOptions?: string | SpinnerOptions): () => void {\n const spinner = new Spinner(textOrOptions)\n spinner.start()\n return () => spinner.stop()\n }\n}\n\n/**\n * Callable spinner interface - call with text to show/update\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * spinner(\"Loading...\"); // Shows spinner with text\n * spinner(\"Still loading...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport interface CallableSpinner extends Disposable {\n /** Call with text to show/update the spinner */\n (text: string): void\n /** Stop the spinner */\n stop(): void\n /** Stop with success message (green checkmark) */\n succeed(text?: string): void\n /** Stop with failure message (red X) */\n fail(text?: string): void\n /** Stop with warning message (yellow warning) */\n warn(text?: string): void\n /** Stop with info message (blue info) */\n info(text?: string): void\n}\n\n/**\n * Create a callable, disposable spinner\n *\n * The spinner is lazy - it won't show anything until you call it with text.\n * Use with `using` for automatic cleanup:\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * // Nothing visible yet\n *\n * spinner(\"Loading repo...\"); // Now shows spinner\n * spinner(\"Applying events...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport function createSpinner(options?: SpinnerOptions): CallableSpinner {\n const spinner = new Spinner({ ...options, text: \"\" })\n\n const callable = ((text: string) => {\n // Always restart if not spinning (handles initial call and after succeed/fail/etc)\n if (!spinner.spinning) {\n spinner.start(text)\n } else {\n spinner.currentText = text\n }\n }) as CallableSpinner\n\n callable.stop = () => spinner.stop()\n callable.succeed = (text) => spinner.succeed(text)\n callable.fail = (text) => spinner.fail(text)\n callable.warn = (text) => spinner.warn(text)\n callable.info = (text) => spinner.info(text)\n callable[Symbol.dispose] = () => spinner.stop()\n\n return callable\n}\n",
6
+ "/**\n * ANSI escape code utilities for terminal control\n */\n\n/** Hide the cursor */\nexport const CURSOR_HIDE = \"\\x1b[?25l\"\n\n/** Show the cursor */\nexport const CURSOR_SHOW = \"\\x1b[?25h\"\n\n/** Move cursor to beginning of line */\nexport const CURSOR_TO_START = \"\\r\"\n\n/** Clear from cursor to end of line */\nexport const CLEAR_LINE_END = \"\\x1b[K\"\n\n/** Clear entire line */\nexport const CLEAR_LINE = \"\\x1b[2K\"\n\n/** Clear screen and move to top-left */\nexport const CLEAR_SCREEN = \"\\x1b[2J\\x1b[H\"\n\n/** Move cursor up N lines */\nexport const cursorUp = (n: number = 1): string => `\\x1b[${n}A`\n\n/** Move cursor down N lines */\nexport const cursorDown = (n: number = 1): string => `\\x1b[${n}B`\n\n/** Save cursor position */\nexport const CURSOR_SAVE = \"\\x1b[s\"\n\n/** Restore cursor position */\nexport const CURSOR_RESTORE = \"\\x1b[u\"\n\n/**\n * Write to stream with proper handling\n */\nexport function write(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(text)\n}\n\n/**\n * Clear the current line and write new text\n */\nexport function writeLine(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(`${CURSOR_TO_START}${text}${CLEAR_LINE_END}`)\n}\n\n/**\n * Wrap a function to handle cursor visibility\n * Hides cursor on start, shows on completion/error\n */\nexport function withCursor<T>(fn: () => T | Promise<T>, stream: NodeJS.WriteStream = process.stdout): Promise<T> {\n stream.write(CURSOR_HIDE)\n\n const restore = () => stream.write(CURSOR_SHOW)\n\n try {\n const result = fn()\n if (result instanceof Promise) {\n return result.finally(restore)\n }\n restore()\n return Promise.resolve(result)\n } catch (error) {\n restore()\n throw error\n }\n}\n\n/**\n * Check if stream is a TTY (supports ANSI codes)\n * Also respects FORCE_TTY environment variable for testing\n */\nexport function isTTY(stream: NodeJS.WriteStream = process.stdout): boolean {\n if (process.env.FORCE_TTY === \"1\") return true\n return stream.isTTY ?? false\n}\n\n/**\n * Get terminal width\n */\nexport function getTerminalWidth(stream: NodeJS.WriteStream = process.stdout): number {\n return stream.columns ?? 80\n}\n",
7
+ "/**\n * withSpinner - Wrap promises with an animated spinner\n */\n\nimport type { WithSpinnerOptions } from \"../types.js\"\nimport { Spinner } from \"../cli/spinner\"\n\n/**\n * Wrap a promise with an animated spinner\n *\n * @example\n * ```ts\n * // Simple usage\n * const data = await withSpinner(fetchData(), \"Loading data...\");\n *\n * // With options\n * const result = await withSpinner(\n * processFiles(),\n * \"Processing...\",\n * { style: \"arc\", clearOnComplete: true }\n * );\n *\n * // With dynamic text\n * const result = await withSpinner(\n * longOperation(),\n * (elapsed) => `Processing... (${elapsed}s)`\n * );\n * ```\n */\nexport async function withSpinner<T>(\n promise: Promise<T> | (() => T | Promise<T>),\n text: string | ((elapsedSeconds: number) => string),\n options: WithSpinnerOptions = {},\n): Promise<T> {\n const spinner = new Spinner({\n text: typeof text === \"string\" ? text : text(0),\n style: options.style,\n color: options.color,\n })\n\n let timer: ReturnType<typeof setInterval> | null = null\n const startTime = Date.now()\n\n spinner.start()\n\n // Update text if dynamic\n if (typeof text === \"function\") {\n timer = setInterval(() => {\n const elapsed = Math.floor((Date.now() - startTime) / 1000)\n spinner.currentText = text(elapsed)\n }, 1000)\n }\n\n try {\n const result = await (typeof promise === \"function\" ? promise() : promise)\n\n if (timer) clearInterval(timer)\n\n if (options.clearOnComplete) {\n spinner.stop()\n } else {\n spinner.succeed()\n }\n\n return result\n } catch (error) {\n if (timer) clearInterval(timer)\n spinner.fail(error instanceof Error ? error.message : \"Failed\")\n throw error\n }\n}\n\n/**\n * Attach a spinner to a promise for manual control\n * Returns [result, spinner] tuple for custom control\n *\n * @example\n * ```ts\n * const [promise, spinner] = attachSpinner(fetchData(), \"Loading...\");\n * spinner.text = \"Still loading...\";\n * const result = await promise;\n * spinner.succeed(\"Loaded!\");\n * ```\n */\nexport function attachSpinner<T>(\n promise: Promise<T>,\n text: string,\n options: WithSpinnerOptions = {},\n): [Promise<T>, Spinner] {\n const spinner = new Spinner({\n text,\n style: options.style,\n color: options.color,\n })\n\n spinner.start()\n\n async function wrapPromise(): Promise<T> {\n try {\n return await promise\n } catch (error) {\n spinner.fail(error instanceof Error ? error.message : \"Failed\")\n throw error\n }\n }\n\n return [wrapPromise(), spinner]\n}\n",
8
+ "/**\n * CLI ProgressBar - Determinate progress indicator with ETA\n */\n\nimport chalk from \"chalk\"\nimport type { ProgressBarOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY, getTerminalWidth } from \"./ansi\"\nimport { calculateETA, formatETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/** Default format string */\nconst DEFAULT_FORMAT = \":bar :percent | :current/:total | ETA: :eta\"\n\n/**\n * ProgressBar class for CLI progress indication\n *\n * @example\n * ```ts\n * const bar = new ProgressBar({ total: 100 });\n * bar.start();\n * for (let i = 0; i <= 100; i++) {\n * await doWork();\n * bar.update(i);\n * }\n * bar.stop();\n * ```\n */\nexport class ProgressBar {\n private total: number\n private format: string\n private width: number\n private complete: string\n private incomplete: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private phases: Record<string, string>\n\n private current = 0\n private phase: string | null = null\n private startTime: number | null = null\n private isActive = false\n\n // ETA smoothing - track last N update times\n private etaBuffer: ETASample[] = []\n\n constructor(options: ProgressBarOptions = {}) {\n this.total = options.total ?? 100\n this.format = options.format ?? DEFAULT_FORMAT\n this.width = options.width ?? 40\n this.complete = options.complete ?? \"█\"\n this.incomplete = options.incomplete ?? \"░\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.phases = options.phases ?? {}\n }\n\n /**\n * Start the progress bar\n */\n start(initialValue = 0, initialTotal?: number): this {\n if (initialTotal !== undefined) {\n this.total = initialTotal\n }\n\n this.current = initialValue\n this.startTime = Date.now()\n this.isActive = true\n this.etaBuffer = [{ time: this.startTime, value: initialValue }]\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n return this\n }\n\n /**\n * Update progress value\n */\n update(value: number, tokens?: Record<string, string | number>): this {\n this.current = Math.min(value, this.total)\n\n // Update ETA buffer\n const now = Date.now()\n this.etaBuffer.push({ time: now, value: this.current })\n if (this.etaBuffer.length > DEFAULT_ETA_BUFFER_SIZE) {\n this.etaBuffer.shift()\n }\n\n if (this.isActive) {\n this.render(tokens)\n }\n\n return this\n }\n\n /**\n * Increment progress by amount (default: 1)\n */\n increment(amount = 1, tokens?: Record<string, string | number>): this {\n return this.update(this.current + amount, tokens)\n }\n\n /**\n * Set the current phase (for multi-phase progress)\n */\n setPhase(phaseName: string, options?: { current?: number; total?: number }): this {\n this.phase = phaseName\n\n if (options?.total !== undefined) {\n this.total = options.total\n }\n if (options?.current !== undefined) {\n this.current = options.current\n // Reset ETA buffer on phase change\n this.etaBuffer = [{ time: Date.now(), value: this.current }]\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return this\n }\n\n /**\n * Stop the progress bar\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (clear && isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n } else {\n write(\"\\n\", this.stream)\n }\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** Get ETA in seconds using smoothed rate */\n private getETASeconds(): number | null {\n return calculateETA(this.etaBuffer, this.current, this.total)\n }\n\n /**\n * Render the progress bar\n */\n private render(tokens?: Record<string, string | number>): void {\n const percent = this.total > 0 ? this.current / this.total : 0\n const eta = this.getETASeconds()\n\n // Build the bar\n const completeLength = Math.round(this.width * percent)\n const incompleteLength = this.width - completeLength\n const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(incompleteLength)\n\n // Get phase display name\n const phaseDisplay = this.phase ? (this.phases[this.phase] ?? this.phase) : \"\"\n\n // Calculate rate\n const elapsed = this.startTime ? (Date.now() - this.startTime) / 1000 : 0\n const rate = elapsed > 0 ? this.current / elapsed : 0\n\n // Replace tokens in format string\n let output = this.format\n .replace(\":bar\", chalk.cyan(bar))\n .replace(\":percent\", `${Math.round(percent * 100)}%`.padStart(4))\n .replace(\":current\", String(this.current))\n .replace(\":total\", String(this.total))\n .replace(\":eta\", formatETA(eta))\n .replace(\":elapsed\", formatETA(elapsed))\n .replace(\":rate\", rate.toFixed(1))\n .replace(\":phase\", chalk.dim(phaseDisplay))\n\n // Replace custom tokens\n if (tokens) {\n for (const [key, value] of Object.entries(tokens)) {\n output = output.replace(`:${key}`, String(value))\n }\n }\n\n // Truncate to terminal width\n const termWidth = getTerminalWidth(this.stream)\n if (output.length > termWidth) {\n output = output.slice(0, termWidth - 1)\n }\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n /**\n * Get current progress ratio (0-1)\n */\n get ratio(): number {\n return this.total > 0 ? this.current / this.total : 0\n }\n\n /**\n * Get current progress percentage (0-100)\n */\n get percentage(): number {\n return Math.round(this.ratio * 100)\n }\n\n /**\n * Dispose the progress bar (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n}\n",
9
+ "/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n",
10
+ "/**\n * withProgress - Wrap callback-based progress functions\n *\n * @deprecated Use `steps()` from `@silvery/ag-react/ui/progress` instead.\n *\n * @example\n * ```typescript\n * // OLD (deprecated):\n * import { withProgress } from \"@silvery/ag-react/ui/wrappers\";\n * const result = await withProgress(\n * (onProgress) => manager.syncFromFs(onProgress),\n * { phases: SYNC_PHASES }\n * );\n *\n * // NEW:\n * import { steps } from \"@silvery/ag-react/ui/progress\";\n * const results = await steps({ syncFiles: () => manager.syncFromFs() }).run();\n * ```\n */\n\nimport type { ProgressInfo, ProgressCallback, WithProgressOptions } from \"../types.js\"\nimport { ProgressBar } from \"../cli/progress-bar\"\nimport { Spinner } from \"../cli/spinner\"\nimport { CURSOR_HIDE, CURSOR_SHOW, write, isTTY } from \"../cli/ansi\"\n\n// Declare timer globals (not exposed by bun-types)\ndeclare function setTimeout(callback: () => void, ms: number): unknown\ndeclare function clearTimeout(id: unknown): void\n\n// Timer type - opaque handle, we only store and clear it\ntype TimerId = unknown\n\n/**\n * Wrap a function that takes a progress callback\n *\n * @example\n * ```ts\n * // Wrap existing km sync API\n * const result = await withProgress(\n * (onProgress) => manager.syncFromFs(onProgress),\n * {\n * phases: {\n * scanning: \"Scanning files\",\n * reconciling: \"Reconciling changes\",\n * rules: \"Evaluating rules\"\n * }\n * }\n * );\n *\n * // Simple usage without phases\n * await withProgress((onProgress) => rebuildState(onProgress));\n *\n * // With custom format\n * await withProgress(\n * (p) => processFiles(p),\n * { format: \":phase :bar :percent\" }\n * );\n *\n * // Show loading immediately (showAfter: 0) or after delay\n * await withProgress(\n * (p) => slowOperation(p),\n * { showAfter: 1000, initialMessage: \"Loading...\" }\n * );\n * ```\n */\nexport async function withProgress<T>(\n fn: (onProgress: ProgressCallback) => T | Promise<T>,\n options: WithProgressOptions = {},\n): Promise<T> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n // Determine format\n const format =\n options.format ?? (options.phases ? \":phase [:bar] :current/:total\" : \"[:bar] :current/:total :percent\")\n\n const bar = new ProgressBar({\n format,\n phases: options.phases ?? {},\n hideCursor: true,\n })\n\n let lastPhase: string | null = null\n let started = false\n\n // Initial spinner (shown before progress starts)\n const showAfter = options.showAfter ?? 1000\n const initialMessage = options.initialMessage ?? \"Loading...\"\n let spinner: Spinner | null = null\n let spinnerTimerId: TimerId | null = null\n\n // Hide cursor\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n // Schedule initial spinner if configured\n if (isTty && showAfter >= 0) {\n spinnerTimerId = setTimeout(() => {\n if (!started) {\n spinner = new Spinner({ text: initialMessage })\n spinner.start()\n }\n }, showAfter)\n }\n\n const onProgress: ProgressCallback = (info: ProgressInfo) => {\n // Stop initial spinner if it was shown\n if (spinner) {\n spinner.stop()\n spinner = null\n }\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n spinnerTimerId = null\n }\n\n // Handle phase transitions\n if (info.phase && info.phase !== lastPhase) {\n if (lastPhase !== null && isTty) {\n // Print newline before switching phases\n write(\"\\n\", stream)\n }\n lastPhase = info.phase\n\n // Start or update bar with new phase\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.setPhase(info.phase, { current: info.current, total: info.total })\n } else {\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.update(info.current)\n }\n }\n\n try {\n const result = await fn(onProgress)\n\n // Clean up spinner if still pending\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n }\n // Note: spinner may be set by setTimeout callback - TS can't track this\n const pendingSpinner = spinner as unknown as Spinner | null\n if (pendingSpinner) {\n pendingSpinner.stop()\n }\n\n // Stop and show cursor\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n\n return result\n } catch (error) {\n // Clean up spinner\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n }\n // Note: spinner may be set by setTimeout callback - TS can't track this\n const errorSpinner = spinner as unknown as Spinner | null\n if (errorSpinner) {\n errorSpinner.stop()\n }\n\n // Restore cursor on error\n if (started) {\n bar.stop()\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n\n/**\n * Create a progress callback that can be passed to existing APIs\n * Returns [callback, complete] tuple\n *\n * @example\n * ```ts\n * const [onProgress, complete] = createProgressCallback({\n * phases: { scanning: \"Scanning\", reconciling: \"Reconciling\" }\n * });\n *\n * const result = await manager.syncFromFs(onProgress);\n * complete();\n * ```\n */\nexport function createProgressCallback(options: WithProgressOptions = {}): [ProgressCallback, () => void] {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n const format =\n options.format ?? (options.phases ? \":phase [:bar] :current/:total\" : \"[:bar] :current/:total :percent\")\n\n const bar = new ProgressBar({\n format,\n phases: options.phases ?? {},\n hideCursor: true,\n })\n\n let lastPhase: string | null = null\n let started = false\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n const callback: ProgressCallback = (info: ProgressInfo) => {\n if (info.phase && info.phase !== lastPhase) {\n if (lastPhase !== null && isTty) {\n write(\"\\n\", stream)\n }\n lastPhase = info.phase\n\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.setPhase(info.phase, { current: info.current, total: info.total })\n } else {\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.update(info.current)\n }\n }\n\n const complete = () => {\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n }\n\n return [callback, complete]\n}\n",
11
+ "/**\n * wrapGenerator - Consume a generator while showing progress\n */\n\nimport type { ProgressGenerator } from \"../types.js\"\nimport { ProgressBar } from \"../cli/progress-bar\"\nimport { CURSOR_HIDE, CURSOR_SHOW, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Consume a progress generator while displaying progress\n *\n * @example\n * ```ts\n * // Wrap existing generator (like evaluateAllRules())\n * await wrapGenerator(evaluateAllRules(), \"Evaluating rules\");\n *\n * // With custom format\n * await wrapGenerator(\n * processItems(),\n * ({ current, total }) => `Processing: ${current}/${total}`\n * );\n *\n * // Get the generator's return value\n * const result = await wrapGenerator(generatorWithReturn(), \"Processing\");\n * ```\n */\nexport async function wrapGenerator<T>(\n generator: ProgressGenerator<T>,\n textOrFormat: string | ((progress: { current: number; total: number }) => string),\n options: { clearOnComplete?: boolean } = {},\n): Promise<T> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n const isCustomFormat = typeof textOrFormat === \"function\"\n const label = isCustomFormat ? \"\" : textOrFormat\n\n const bar = new ProgressBar({\n format: label ? `${label} [:bar] :current/:total :percent` : \":bar :current/:total :percent\",\n hideCursor: true,\n })\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n let started = false\n let result: IteratorResult<{ current: number; total: number }, T>\n\n try {\n // Consume the generator\n while (true) {\n result = generator.next()\n\n if (result.done) {\n break\n }\n\n const { current, total } = result.value\n\n if (!started) {\n bar.start(current, total)\n started = true\n } else {\n bar.update(current)\n }\n }\n\n // Stop bar\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n\n return result.value\n } catch (error) {\n if (started) {\n bar.stop()\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n\n/**\n * Create an async iterable wrapper that shows progress\n *\n * @example\n * ```ts\n * const items = [1, 2, 3, 4, 5];\n * for await (const item of withIterableProgress(items, \"Processing\")) {\n * await processItem(item);\n * }\n * ```\n */\nexport async function* withIterableProgress<T>(\n iterable: Iterable<T> | AsyncIterable<T>,\n label: string,\n options: { clearOnComplete?: boolean } = {},\n): AsyncGenerator<T, void, unknown> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n // Try to get length if array\n const items = Array.isArray(iterable) ? iterable : null\n const total = items?.length ?? 0\n\n const bar = new ProgressBar({\n format: `${label} [:bar] :current/:total :percent`,\n total,\n hideCursor: true,\n })\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n let current = 0\n bar.start(0, total)\n\n try {\n for await (const item of iterable as AsyncIterable<T>) {\n yield item\n current++\n bar.update(current)\n }\n\n bar.stop(options.clearOnComplete)\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n } catch (error) {\n bar.stop()\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n",
12
+ "/**\n * wrapEmitter - Track EventEmitter state changes with progress indicators\n */\n\nimport type { EventEmitter } from \"events\"\nimport { Spinner } from \"../cli/spinner\"\n\n/** Event handler configuration */\ninterface EventConfig {\n /** Display text for this event */\n text?: string\n /** Dynamic text based on event data */\n getText?: (data: unknown) => string\n /** Mark spinner as succeeded */\n succeed?: boolean\n /** Mark spinner as failed */\n fail?: boolean\n /** Stop tracking */\n stop?: boolean\n}\n\n/** Configuration for wrapEmitter */\ninterface WrapEmitterConfig {\n /** Event handlers */\n events: Record<string, EventConfig>\n /** Initial text */\n initialText?: string\n}\n\n/**\n * Track EventEmitter state changes with a spinner\n *\n * @example\n * ```ts\n * const stop = wrapEmitter(syncManager, {\n * initialText: \"Starting sync...\",\n * events: {\n * 'ready': { text: \"Watcher ready\", succeed: true },\n * 'state-change': { getText: (s) => `State: ${s}` },\n * 'error': { fail: true },\n * 'idle': { stop: true }\n * }\n * });\n *\n * // Later, to stop manually\n * stop();\n * ```\n */\nexport function wrapEmitter(emitter: EventEmitter, config: WrapEmitterConfig): () => void {\n const spinner = new Spinner(config.initialText ?? \"\")\n const handlers: Map<string, (...args: unknown[]) => void> = new Map()\n\n spinner.start()\n\n // Set up event handlers\n for (const [eventName, eventConfig] of Object.entries(config.events)) {\n const handler = (data: unknown) => {\n // Update text\n if (eventConfig.getText) {\n spinner.currentText = eventConfig.getText(data)\n } else if (eventConfig.text) {\n spinner.currentText = eventConfig.text\n }\n\n // Handle terminal states\n if (eventConfig.succeed) {\n spinner.succeed()\n cleanup()\n } else if (eventConfig.fail) {\n const message = data instanceof Error ? data.message : String(data ?? \"Failed\")\n spinner.fail(message)\n cleanup()\n } else if (eventConfig.stop) {\n spinner.stop()\n cleanup()\n }\n }\n\n handlers.set(eventName, handler)\n emitter.on(eventName, handler)\n }\n\n // Cleanup function\n function cleanup() {\n for (const [eventName, handler] of handlers) {\n emitter.off(eventName, handler)\n }\n handlers.clear()\n }\n\n // Return stop function\n return () => {\n spinner.stop()\n cleanup()\n }\n}\n\n/**\n * Wait for an EventEmitter to emit a specific event\n * Shows a spinner while waiting\n *\n * @example\n * ```ts\n * await waitForEvent(syncManager, \"ready\", \"Waiting for watcher...\");\n * ```\n */\nexport async function waitForEvent(\n emitter: EventEmitter,\n eventName: string,\n text: string,\n options: {\n errorEvent?: string\n timeout?: number\n } = {},\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const spinner = new Spinner(text)\n spinner.start()\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const cleanup = () => {\n emitter.off(eventName, successHandler)\n if (options.errorEvent) {\n emitter.off(options.errorEvent, errorHandler)\n }\n if (timer) {\n clearTimeout(timer)\n }\n }\n\n const successHandler = (data: unknown) => {\n cleanup()\n spinner.succeed()\n resolve(data)\n }\n\n const errorHandler = (error: unknown) => {\n cleanup()\n spinner.fail(error instanceof Error ? error.message : \"Error\")\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n\n emitter.once(eventName, successHandler)\n\n if (options.errorEvent) {\n emitter.once(options.errorEvent, errorHandler)\n }\n\n if (options.timeout) {\n timer = setTimeout(() => {\n cleanup()\n spinner.fail(\"Timeout\")\n reject(new Error(`Timeout waiting for ${eventName}`))\n }, options.timeout)\n }\n })\n}\n",
13
+ "/**\n * withSelect - Interactive CLI selection list\n */\n\nimport chalk from \"chalk\"\nimport type { SelectOption, WithSelectOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, cursorUp, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Display an interactive selection list in the terminal\n *\n * @example\n * ```ts\n * // Simple usage\n * const color = await withSelect(\"Choose a color:\", [\n * { label: \"Red\", value: \"red\" },\n * { label: \"Green\", value: \"green\" },\n * { label: \"Blue\", value: \"blue\" },\n * ]);\n *\n * // With options\n * const result = await withSelect(\n * \"Select item:\",\n * options,\n * { initial: 2, maxVisible: 5 }\n * );\n * ```\n */\nexport async function withSelect<T>(\n prompt: string,\n options: SelectOption<T>[],\n selectOptions: WithSelectOptions = {},\n): Promise<T> {\n const { initial = 0, maxVisible = 10 } = selectOptions\n const stream = process.stdout\n const stdin = process.stdin\n\n if (!isTTY(stream) || !stdin.isTTY) {\n // Non-interactive mode: return first option or initial\n return options[initial]?.value ?? options[0]!.value\n }\n\n return new Promise((resolve, reject) => {\n let highlightIndex = Math.min(Math.max(0, initial), options.length - 1)\n let linesRendered = 0\n\n // Enable raw mode for character-by-character input\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n\n // Hide cursor\n write(CURSOR_HIDE, stream)\n\n function render() {\n // Clear previously rendered lines\n if (linesRendered > 0) {\n write(cursorUp(linesRendered), stream)\n }\n\n // Calculate scroll window\n const scrollOffset = Math.max(\n 0,\n Math.min(highlightIndex - Math.floor(maxVisible / 2), options.length - maxVisible),\n )\n const visibleCount = Math.min(maxVisible, options.length)\n const visibleOptions = options.slice(scrollOffset, scrollOffset + visibleCount)\n const hasMoreAbove = scrollOffset > 0\n const hasMoreBelow = scrollOffset + visibleCount < options.length\n\n // Render prompt\n write(`${CURSOR_TO_START}${chalk.bold(prompt)}${CLEAR_LINE_END}\\n`, stream)\n\n let lines = 1\n\n // Render scroll indicator (above)\n if (hasMoreAbove) {\n write(`${CURSOR_TO_START} ${chalk.dim(\"...\")}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n // Render options\n for (let i = 0; i < visibleOptions.length; i++) {\n const option = visibleOptions[i]\n const actualIndex = scrollOffset + i\n const isHighlighted = actualIndex === highlightIndex\n\n const indicator = isHighlighted ? chalk.cyan(\">\") : \" \"\n const label = isHighlighted ? chalk.cyan(option!.label) : option!.label\n\n write(`${CURSOR_TO_START}${indicator} ${label}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n // Render scroll indicator (below)\n if (hasMoreBelow) {\n write(`${CURSOR_TO_START} ${chalk.dim(\"...\")}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n linesRendered = lines\n }\n\n function cleanup() {\n stdin.setRawMode(false)\n stdin.pause()\n stdin.removeListener(\"data\", onKeypress)\n write(CURSOR_SHOW, stream)\n }\n\n function onKeypress(key: string) {\n // Handle key sequences\n const keyCode = key.charCodeAt(0)\n\n // Ctrl+C\n if (key === \"\\x03\") {\n cleanup()\n reject(new Error(\"User cancelled\"))\n return\n }\n\n // Enter/Return\n if (key === \"\\r\" || key === \"\\n\") {\n cleanup()\n resolve(options[highlightIndex]!.value)\n return\n }\n\n // Escape\n if (key === \"\\x1b\" && key.length === 1) {\n cleanup()\n reject(new Error(\"User cancelled\"))\n return\n }\n\n // Arrow keys (escape sequences)\n if (key.startsWith(\"\\x1b[\")) {\n const code = key.slice(2)\n if (code === \"A\") {\n // Up arrow\n highlightIndex = Math.max(0, highlightIndex - 1)\n render()\n } else if (code === \"B\") {\n // Down arrow\n highlightIndex = Math.min(options.length - 1, highlightIndex + 1)\n render()\n }\n return\n }\n\n // j/k vim keys\n if (key === \"j\" || key === \"J\") {\n highlightIndex = Math.min(options.length - 1, highlightIndex + 1)\n render()\n return\n }\n if (key === \"k\" || key === \"K\") {\n highlightIndex = Math.max(0, highlightIndex - 1)\n render()\n return\n }\n\n // Space to select (optional alternative to Enter)\n if (key === \" \") {\n cleanup()\n resolve(options[highlightIndex]!.value)\n return\n }\n }\n\n stdin.on(\"data\", onKeypress)\n render()\n })\n}\n\n/**\n * Create a reusable select instance for multiple selections\n *\n * @example\n * ```ts\n * const select = createSelect({\n * maxVisible: 5,\n * });\n *\n * const first = await select(\"Choose first:\", options1);\n * const second = await select(\"Choose second:\", options2);\n * ```\n */\nexport function createSelect(\n defaultOptions: WithSelectOptions = {},\n): <T>(prompt: string, options: SelectOption<T>[], overrides?: WithSelectOptions) => Promise<T> {\n return <T>(prompt: string, options: SelectOption<T>[], overrides: WithSelectOptions = {}) =>\n withSelect(prompt, options, { ...defaultOptions, ...overrides })\n}\n",
14
+ "/**\n * withTextInput - CLI wrapper for text input prompts\n */\n\nimport chalk from \"chalk\"\nimport type { TextInputOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Prompt for text input in the terminal\n *\n * @example\n * ```ts\n * // Simple usage\n * const name = await withTextInput(\"What is your name?\");\n *\n * // With options\n * const password = await withTextInput(\"Password:\", { mask: \"*\" });\n *\n * // With validation\n * const email = await withTextInput(\"Email:\", {\n * validate: (v) => v.includes(\"@\") ? undefined : \"Invalid email\"\n * });\n *\n * // With autocomplete\n * const fruit = await withTextInput(\"Pick a fruit:\", {\n * autocomplete: [\"apple\", \"banana\", \"cherry\"]\n * });\n * ```\n */\nexport async function withTextInput(prompt: string, options: TextInputOptions = {}): Promise<string> {\n const stream = options.stream ?? process.stdout\n const inputStream = options.inputStream ?? process.stdin\n const isTty = isTTY(stream)\n\n // Initialize state\n let value = options.defaultValue ?? \"\"\n let cursorPosition = value.length\n let errorMessage: string | undefined\n\n // Setup raw mode for character-by-character input\n if (inputStream.isTTY) {\n inputStream.setRawMode(true)\n }\n inputStream.resume()\n\n // Render the current state\n const render = () => {\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n const suggestionSuffix = suggestion ? chalk.dim(suggestion.slice(value.length)) : \"\"\n\n // Build cursor display\n const beforeCursor = displayValue.slice(0, cursorPosition)\n const cursorChar = displayValue[cursorPosition] ?? \" \"\n const afterCursor = displayValue.slice(cursorPosition + 1)\n\n // Placeholder when empty\n const showPlaceholder = !value && options.placeholder\n const inputDisplay = showPlaceholder\n ? chalk.dim(options.placeholder) + chalk.inverse(\" \")\n : beforeCursor + chalk.inverse(cursorChar) + afterCursor + suggestionSuffix\n\n // Error message\n const errorDisplay = errorMessage ? chalk.red(` (${errorMessage})`) : \"\"\n\n const line = `${chalk.cyan(\"?\")} ${chalk.bold(prompt)} ${inputDisplay}${errorDisplay}`\n\n if (isTty) {\n write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`, stream)\n }\n }\n\n // Hide cursor during input (we show our own)\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n render()\n\n return new Promise<string>((resolve, reject) => {\n const cleanup = () => {\n inputStream.removeListener(\"data\", onData)\n inputStream.removeListener(\"error\", onError)\n if (inputStream.isTTY) {\n inputStream.setRawMode(false)\n }\n inputStream.pause()\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n }\n\n const submit = () => {\n // Validate before accepting\n if (options.validate) {\n const error = options.validate(value)\n if (error) {\n errorMessage = error\n render()\n return\n }\n }\n\n cleanup()\n\n // Show final value\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n write(\n `${CURSOR_TO_START}${chalk.green(\"✔\")} ${chalk.bold(prompt)} ${chalk.dim(displayValue)}${CLEAR_LINE_END}\\n`,\n stream,\n )\n\n resolve(value)\n }\n\n const onError = (err: Error) => {\n cleanup()\n reject(err)\n }\n\n const onData = (data: Buffer) => {\n const input = data.toString()\n errorMessage = undefined // Clear error on any input\n\n // Handle special keys\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!\n const code = char.charCodeAt(0)\n\n // Enter (CR or LF)\n if (code === 13 || code === 10) {\n submit()\n return\n }\n\n // Ctrl+C - abort\n if (code === 3) {\n cleanup()\n write(\"\\n\", stream)\n reject(new Error(\"User aborted\"))\n return\n }\n\n // Escape - clear or abort\n if (code === 27) {\n // Check for arrow key sequences\n if (input[i + 1] === \"[\") {\n const arrowCode = input[i + 2]\n if (arrowCode === \"D\") {\n // Left arrow\n cursorPosition = Math.max(0, cursorPosition - 1)\n i += 2\n continue\n }\n if (arrowCode === \"C\") {\n // Right arrow\n cursorPosition = Math.min(value.length, cursorPosition + 1)\n i += 2\n continue\n }\n if (arrowCode === \"H\") {\n // Home\n cursorPosition = 0\n i += 2\n continue\n }\n if (arrowCode === \"F\") {\n // End\n cursorPosition = value.length\n i += 2\n continue\n }\n // Skip other escape sequences\n i += 2\n continue\n }\n // Plain escape - clear input\n value = \"\"\n cursorPosition = 0\n continue\n }\n\n // Backspace (127 or 8)\n if (code === 127 || code === 8) {\n if (cursorPosition > 0) {\n value = value.slice(0, cursorPosition - 1) + value.slice(cursorPosition)\n cursorPosition--\n }\n continue\n }\n\n // Delete (escape sequence handled above)\n if (code === 4) {\n // Ctrl+D acts as delete\n if (cursorPosition < value.length) {\n value = value.slice(0, cursorPosition) + value.slice(cursorPosition + 1)\n }\n continue\n }\n\n // Tab - accept autocomplete suggestion\n if (code === 9) {\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n if (suggestion) {\n value = suggestion\n cursorPosition = value.length\n }\n continue\n }\n\n // Ctrl+A - beginning of line\n if (code === 1) {\n cursorPosition = 0\n continue\n }\n\n // Ctrl+E - end of line\n if (code === 5) {\n cursorPosition = value.length\n continue\n }\n\n // Ctrl+U - clear to beginning\n if (code === 21) {\n value = value.slice(cursorPosition)\n cursorPosition = 0\n continue\n }\n\n // Ctrl+K - clear to end\n if (code === 11) {\n value = value.slice(0, cursorPosition)\n continue\n }\n\n // Ctrl+W - delete word backward\n if (code === 23) {\n const before = value.slice(0, cursorPosition)\n const after = value.slice(cursorPosition)\n const trimmed = before.trimEnd()\n const lastSpace = trimmed.lastIndexOf(\" \")\n const newBefore = lastSpace === -1 ? \"\" : trimmed.slice(0, lastSpace + 1)\n value = newBefore + after\n cursorPosition = newBefore.length\n continue\n }\n\n // Regular printable character\n if (code >= 32 && code < 127) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition++\n continue\n }\n\n // Handle UTF-8 characters (multi-byte)\n if (code > 127) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition++\n continue\n }\n }\n\n render()\n }\n\n inputStream.on(\"data\", onData)\n inputStream.on(\"error\", onError)\n })\n}\n\n/**\n * Create a text input instance for manual control\n *\n * @example\n * ```ts\n * const input = createTextInput(\"Name:\", { placeholder: \"Enter name\" });\n * input.render();\n *\n * // Later, get the value\n * const value = await input.waitForSubmit();\n * ```\n */\nexport function createTextInput(prompt: string, options: TextInputOptions = {}): TextInputInstance {\n const stream = options.stream ?? process.stdout\n const isTty = isTTY(stream)\n\n let value = options.defaultValue ?? \"\"\n let cursorPosition = value.length\n\n const render = () => {\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n const suggestionSuffix = suggestion ? chalk.dim(suggestion.slice(value.length)) : \"\"\n\n const beforeCursor = displayValue.slice(0, cursorPosition)\n const cursorChar = displayValue[cursorPosition] ?? \" \"\n const afterCursor = displayValue.slice(cursorPosition + 1)\n\n const showPlaceholder = !value && options.placeholder\n const inputDisplay = showPlaceholder\n ? chalk.dim(options.placeholder) + chalk.inverse(\" \")\n : beforeCursor + chalk.inverse(cursorChar) + afterCursor + suggestionSuffix\n\n const line = `${chalk.cyan(\"?\")} ${chalk.bold(prompt)} ${inputDisplay}`\n\n if (isTty) {\n write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`, stream)\n }\n }\n\n return {\n get value() {\n return value\n },\n set value(v: string) {\n value = v\n cursorPosition = Math.min(cursorPosition, v.length)\n },\n get cursorPosition() {\n return cursorPosition\n },\n set cursorPosition(pos: number) {\n cursorPosition = Math.max(0, Math.min(value.length, pos))\n },\n render,\n insert(char: string) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition += char.length\n },\n backspace() {\n if (cursorPosition > 0) {\n value = value.slice(0, cursorPosition - 1) + value.slice(cursorPosition)\n cursorPosition--\n }\n },\n delete() {\n if (cursorPosition < value.length) {\n value = value.slice(0, cursorPosition) + value.slice(cursorPosition + 1)\n }\n },\n clear() {\n value = \"\"\n cursorPosition = 0\n },\n acceptSuggestion() {\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n if (suggestion) {\n value = suggestion\n cursorPosition = value.length\n }\n },\n }\n}\n\n/** Instance returned by createTextInput for manual control */\nexport interface TextInputInstance {\n /** Current input value */\n value: string\n /** Current cursor position */\n cursorPosition: number\n /** Render the current state */\n render(): void\n /** Insert text at cursor */\n insert(char: string): void\n /** Delete character before cursor */\n backspace(): void\n /** Delete character at cursor */\n delete(): void\n /** Clear all input */\n clear(): void\n /** Accept autocomplete suggestion */\n acceptSuggestion(): void\n}\n\n/**\n * Find a matching autocomplete suggestion for the current input\n */\nfunction getAutocompleteSuggestion(value: string, autocomplete?: string[]): string | undefined {\n if (!value || !autocomplete?.length) {\n return undefined\n }\n\n const lowerValue = value.toLowerCase()\n return autocomplete.find((item) => item.toLowerCase().startsWith(lowerValue) && item.length > value.length)\n}\n"
15
+ ],
16
+ "mappings": "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",
17
+ "debugId": "E1BC722563022ECD64756E2164756E21",
18
+ "names": []
19
+ }
package/dist/ui.js ADDED
@@ -0,0 +1,17 @@
1
+ import chalk from"chalk";var CURSOR_HIDE="\x1B[?25l",CURSOR_SHOW="\x1B[?25h",CURSOR_TO_START="\r",CLEAR_LINE_END="\x1B[K",CLEAR_LINE="\x1B[2K",CLEAR_SCREEN="\x1B[2J\x1B[H",cursorUp=(n=1)=>`\x1B[${n}A`,cursorDown=(n=1)=>`\x1B[${n}B`,CURSOR_SAVE="\x1B[s",CURSOR_RESTORE="\x1B[u";function write(text,stream=process.stdout){stream.write(text)}function writeLine(text,stream=process.stdout){stream.write(`\r${text}\x1B[K`)}function withCursor(fn,stream=process.stdout){stream.write("\x1B[?25l");let restore=()=>stream.write("\x1B[?25h");try{let result=fn();if(result instanceof Promise)return result.finally(restore);return restore(),Promise.resolve(result)}catch(error){throw restore(),error}}function isTTY(stream=process.stdout){if(process.env.FORCE_TTY==="1")return!0;return stream.isTTY??!1}function getTerminalWidth(stream=process.stdout){return stream.columns??80}var SPINNER_FRAMES={dots:["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],line:["-","\\","|","/"],arc:["◜","◠","◝","◞","◡","◟"],bounce:["⠁","⠂","⠄","⠂"],pulse:["█","▓","▒","░","▒","▓"]},SPINNER_INTERVALS={dots:80,line:120,arc:100,bounce:120,pulse:100};class Spinner{text;style;color;stream;hideCursor;interval;frameIndex=0;timer=null;isSpinning=!1;constructor(textOrOptions){let options=typeof textOrOptions==="string"?{text:textOrOptions}:textOrOptions??{};this.text=options.text??"",this.style=options.style??"dots",this.color=options.color??"cyan",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.interval=options.interval??SPINNER_INTERVALS[this.style]}get currentText(){return this.text}set currentText(value){if(this.text=value,this.isSpinning)this.render()}get spinning(){return this.isSpinning}start(text){if(text!==void 0)this.text=text;if(this.isSpinning)return this;if(this.isSpinning=!0,this.frameIndex=0,this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this.timer=setInterval(()=>{this.frameIndex=(this.frameIndex+1)%SPINNER_FRAMES[this.style].length,this.render()},this.interval),this}stop(){if(!this.isSpinning)return this;if(this.isSpinning=!1,this.timer)clearInterval(this.timer),this.timer=null;if(this.clear(),this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}succeed(text){return this.stopWithSymbol(chalk.green("✔"),text??this.text)}fail(text){return this.stopWithSymbol(chalk.red("✖"),text??this.text)}warn(text){return this.stopWithSymbol(chalk.yellow("⚠"),text??this.text)}info(text){return this.stopWithSymbol(chalk.blue("ℹ"),text??this.text)}clear(){if(isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);return this}render(){let frame=SPINNER_FRAMES[this.style][this.frameIndex],colorFn=chalk[this.color],coloredFrame=colorFn?colorFn(frame):frame,output=this.text?`${coloredFrame} ${this.text}`:coloredFrame;if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}stopWithSymbol(symbol,text){return this.stop(),write(`${symbol} ${text}
2
+ `,this.stream),this}[Symbol.dispose](){this.stop()}static start(textOrOptions){let spinner=new Spinner(textOrOptions);return spinner.start(),()=>spinner.stop()}}function createSpinner(options){let spinner=new Spinner({...options,text:""}),callable=(text)=>{if(!spinner.spinning)spinner.start(text);else spinner.currentText=text};return callable.stop=()=>spinner.stop(),callable.succeed=(text)=>spinner.succeed(text),callable.fail=(text)=>spinner.fail(text),callable.warn=(text)=>spinner.warn(text),callable.info=(text)=>spinner.info(text),callable[Symbol.dispose]=()=>spinner.stop(),callable}import chalk2 from"chalk";function calculateETA(buffer,current,total){if(buffer.length<2)return null;let first=buffer[0],last=buffer[buffer.length-1],elapsed=(last.time-first.time)/1000,progress=last.value-first.value;if(elapsed<=0||progress<=0)return null;let rate=progress/elapsed;return(total-current)/rate}function formatETA(eta){if(eta===null||!isFinite(eta))return"--:--";if(eta>86400)return">1d";let hours=Math.floor(eta/3600),minutes=Math.floor(eta%3600/60),seconds=Math.floor(eta%60);if(hours>0)return`${hours}:${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;return`${minutes}:${seconds.toString().padStart(2,"0")}`}var DEFAULT_ETA_BUFFER_SIZE=10;var DEFAULT_FORMAT=":bar :percent | :current/:total | ETA: :eta";class ProgressBar{total;format;width;complete;incomplete;stream;hideCursor;phases;current=0;phase=null;startTime=null;isActive=!1;etaBuffer=[];constructor(options={}){this.total=options.total??100,this.format=options.format??DEFAULT_FORMAT,this.width=options.width??40,this.complete=options.complete??"█",this.incomplete=options.incomplete??"░",this.stream=options.stream??process.stdout,this.hideCursor=options.hideCursor??!0,this.phases=options.phases??{}}start(initialValue=0,initialTotal){if(initialTotal!==void 0)this.total=initialTotal;if(this.current=initialValue,this.startTime=Date.now(),this.isActive=!0,this.etaBuffer=[{time:this.startTime,value:initialValue}],this.hideCursor&&isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this}update(value,tokens){this.current=Math.min(value,this.total);let now=Date.now();if(this.etaBuffer.push({time:now,value:this.current}),this.etaBuffer.length>DEFAULT_ETA_BUFFER_SIZE)this.etaBuffer.shift();if(this.isActive)this.render(tokens);return this}increment(amount=1,tokens){return this.update(this.current+amount,tokens)}setPhase(phaseName,options){if(this.phase=phaseName,options?.total!==void 0)this.total=options.total;if(options?.current!==void 0)this.current=options.current,this.etaBuffer=[{time:Date.now(),value:this.current}];if(this.isActive)this.render();return this}stop(clear=!1){if(!this.isActive)return this;if(this.isActive=!1,clear&&isTTY(this.stream))write(`${CURSOR_TO_START}${CLEAR_LINE_END}`,this.stream);else write(`
3
+ `,this.stream);if(this.hideCursor&&isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}getETASeconds(){return calculateETA(this.etaBuffer,this.current,this.total)}render(tokens){let percent=this.total>0?this.current/this.total:0,eta=this.getETASeconds(),completeLength=Math.round(this.width*percent),incompleteLength=this.width-completeLength,bar=this.complete.repeat(completeLength)+this.incomplete.repeat(incompleteLength),phaseDisplay=this.phase?this.phases[this.phase]??this.phase:"",elapsed=this.startTime?(Date.now()-this.startTime)/1000:0,rate=elapsed>0?this.current/elapsed:0,output=this.format.replace(":bar",chalk2.cyan(bar)).replace(":percent",`${Math.round(percent*100)}%`.padStart(4)).replace(":current",String(this.current)).replace(":total",String(this.total)).replace(":eta",formatETA(eta)).replace(":elapsed",formatETA(elapsed)).replace(":rate",rate.toFixed(1)).replace(":phase",chalk2.dim(phaseDisplay));if(tokens)for(let[key,value]of Object.entries(tokens))output=output.replace(`:${key}`,String(value));let termWidth=getTerminalWidth(this.stream);if(output.length>termWidth)output=output.slice(0,termWidth-1);if(isTTY(this.stream))write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`,this.stream)}get ratio(){return this.total>0?this.current/this.total:0}get percentage(){return Math.round(this.ratio*100)}[Symbol.dispose](){this.stop()}}import chalk3 from"chalk";var STATUS_ICONS={pending:chalk3.gray("○"),running:"",completed:chalk3.green("✔"),failed:chalk3.red("✖"),skipped:chalk3.yellow("⊘")};class MultiProgress{tasks=new Map;taskOrder=[];stream;isActive=!1;timer=null;frameIndex=0;renderedLines=0;constructor(stream=process.stdout){this.stream=stream}add(title,options={}){let id=`task-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,task={id,title,type:options.type??"spinner",status:"pending",total:options.total,current:0,spinnerStyle:options.spinnerStyle??"dots",indent:options.indent??0};if(this.tasks.set(id,task),options.insertAfter){let afterIndex=this.taskOrder.indexOf(options.insertAfter);if(afterIndex>=0)this.taskOrder.splice(afterIndex+1,0,id);else this.taskOrder.push(id)}else this.taskOrder.push(id);if(this.isActive)this.render();return new TaskHandle(this,id)}start(){if(this.isActive)return this;if(this.isActive=!0,isTTY(this.stream))write(CURSOR_HIDE,this.stream);return this.render(),this.timer=setInterval(()=>{this.frameIndex=(this.frameIndex+1)%10,this.render()},80),this}[Symbol.dispose](){this.stop()}stop(clear=!1){if(!this.isActive)return this;if(this.isActive=!1,this.timer)clearInterval(this.timer),this.timer=null;if(clear&&isTTY(this.stream)){if(this.renderedLines>0){write(cursorUp(this.renderedLines),this.stream);for(let i=0;i<this.renderedLines;i++)write(`${CLEAR_LINE}
4
+ `,this.stream);write(cursorUp(this.renderedLines),this.stream)}}else this.render(),write(`
5
+ `,this.stream);if(isTTY(this.stream))write(CURSOR_SHOW,this.stream);return this}_updateTask(id,updates){let task=this.tasks.get(id);if(task){if(Object.assign(task,updates),this.isActive&&updates.status)this.render()}}_getTask(id){return this.tasks.get(id)}render(){if(!isTTY(this.stream))return;if(this.renderedLines>0)write(cursorUp(this.renderedLines),this.stream);let lines=[];for(let id of this.taskOrder){let task=this.tasks.get(id);if(!task)continue;let icon;if(task.status==="running")if(task.type==="group")icon=STATUS_ICONS.pending;else{let frames=SPINNER_FRAMES[task.spinnerStyle??"dots"];icon=chalk3.cyan(frames[this.frameIndex%frames.length])}else icon=STATUS_ICONS[task.status];let line=`${" ".repeat(task.indent??0)}${icon} ${task.title}`;if(task.type==="bar"&&task.total&&task.total>0){let percent=task.current/task.total,barWidth=20,filled=Math.round(20*percent),empty=20-filled,bar=chalk3.cyan("█".repeat(filled))+chalk3.gray("░".repeat(empty));line+=` ${bar} ${Math.round(percent*100)}%`}if(task.status==="completed"&&task.completionTime!==void 0)line+=chalk3.dim(` ${task.completionTime}ms`);lines.push(line)}for(let line of lines)write(`${CLEAR_LINE}${line}
6
+ `,this.stream);this.renderedLines=lines.length}}class TaskHandle{multi;_id;constructor(multi,_id){this.multi=multi;this._id=_id}get id(){return this._id}start(){return this.multi._updateTask(this._id,{status:"running"}),this}update(current){return this.multi._updateTask(this._id,{current}),this}complete(titleOrTime){let updates={status:"completed"};if(typeof titleOrTime==="number")updates.completionTime=titleOrTime;else if(titleOrTime)updates.title=titleOrTime;return this.multi._updateTask(this._id,updates),this}fail(title){let updates={status:"failed"};if(title)updates.title=title;return this.multi._updateTask(this._id,updates),this}skip(title){let updates={status:"skipped"};if(title)updates.title=title;return this.multi._updateTask(this._id,updates),this}setTitle(title){return this.multi._updateTask(this._id,{title}),this}setType(type){return this.multi._updateTask(this._id,{type}),this}get status(){return this.multi._getTask(this._id)?.status??"pending"}}async function withSpinner(promise,text,options={}){let spinner=new Spinner({text:typeof text==="string"?text:text(0),style:options.style,color:options.color}),timer=null,startTime=Date.now();if(spinner.start(),typeof text==="function")timer=setInterval(()=>{let elapsed=Math.floor((Date.now()-startTime)/1000);spinner.currentText=text(elapsed)},1000);try{let result=await(typeof promise==="function"?promise():promise);if(timer)clearInterval(timer);if(options.clearOnComplete)spinner.stop();else spinner.succeed();return result}catch(error){if(timer)clearInterval(timer);throw spinner.fail(error instanceof Error?error.message:"Failed"),error}}function attachSpinner(promise,text,options={}){let spinner=new Spinner({text,style:options.style,color:options.color});spinner.start();async function wrapPromise(){try{return await promise}catch(error){throw spinner.fail(error instanceof Error?error.message:"Failed"),error}}return[wrapPromise(),spinner]}async function withProgress(fn,options={}){let stream=process.stdout,isTty=isTTY(stream),format=options.format??(options.phases?":phase [:bar] :current/:total":"[:bar] :current/:total :percent"),bar=new ProgressBar({format,phases:options.phases??{},hideCursor:!0}),lastPhase=null,started=!1,showAfter=options.showAfter??1000,initialMessage=options.initialMessage??"Loading...",spinner=null,spinnerTimerId=null;if(isTty)write(CURSOR_HIDE,stream);if(isTty&&showAfter>=0)spinnerTimerId=setTimeout(()=>{if(!started)spinner=new Spinner({text:initialMessage}),spinner.start()},showAfter);let onProgress=(info)=>{if(spinner)spinner.stop(),spinner=null;if(spinnerTimerId!==null)clearTimeout(spinnerTimerId),spinnerTimerId=null;if(info.phase&&info.phase!==lastPhase){if(lastPhase!==null&&isTty)write(`
7
+ `,stream);if(lastPhase=info.phase,!started)bar.start(info.current,info.total),started=!0;bar.setPhase(info.phase,{current:info.current,total:info.total})}else{if(!started)bar.start(info.current,info.total),started=!0;bar.update(info.current)}};try{let result=await fn(onProgress);if(spinnerTimerId!==null)clearTimeout(spinnerTimerId);let pendingSpinner=spinner;if(pendingSpinner)pendingSpinner.stop();if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream);return result}catch(error){if(spinnerTimerId!==null)clearTimeout(spinnerTimerId);let errorSpinner=spinner;if(errorSpinner)errorSpinner.stop();if(started)bar.stop();if(isTty)write(CURSOR_SHOW,stream);throw error}}function createProgressCallback(options={}){let stream=process.stdout,isTty=isTTY(stream),format=options.format??(options.phases?":phase [:bar] :current/:total":"[:bar] :current/:total :percent"),bar=new ProgressBar({format,phases:options.phases??{},hideCursor:!0}),lastPhase=null,started=!1;if(isTty)write(CURSOR_HIDE,stream);return[(info)=>{if(info.phase&&info.phase!==lastPhase){if(lastPhase!==null&&isTty)write(`
8
+ `,stream);if(lastPhase=info.phase,!started)bar.start(info.current,info.total),started=!0;bar.setPhase(info.phase,{current:info.current,total:info.total})}else{if(!started)bar.start(info.current,info.total),started=!0;bar.update(info.current)}},()=>{if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream)}]}async function wrapGenerator(generator,textOrFormat,options={}){let stream=process.stdout,isTty=isTTY(stream),label=typeof textOrFormat==="function"?"":textOrFormat,bar=new ProgressBar({format:label?`${label} [:bar] :current/:total :percent`:":bar :current/:total :percent",hideCursor:!0});if(isTty)write(CURSOR_HIDE,stream);let started=!1,result;try{while(!0){if(result=generator.next(),result.done)break;let{current,total}=result.value;if(!started)bar.start(current,total),started=!0;else bar.update(current)}if(started)bar.stop(options.clearOnComplete);if(isTty)write(CURSOR_SHOW,stream);return result.value}catch(error){if(started)bar.stop();if(isTty)write(CURSOR_SHOW,stream);throw error}}async function*withIterableProgress(iterable,label,options={}){let stream=process.stdout,isTty=isTTY(stream),total=(Array.isArray(iterable)?iterable:null)?.length??0,bar=new ProgressBar({format:`${label} [:bar] :current/:total :percent`,total,hideCursor:!0});if(isTty)write(CURSOR_HIDE,stream);let current=0;bar.start(0,total);try{for await(let item of iterable)yield item,current++,bar.update(current);if(bar.stop(options.clearOnComplete),isTty)write(CURSOR_SHOW,stream)}catch(error){if(bar.stop(),isTty)write(CURSOR_SHOW,stream);throw error}}function wrapEmitter(emitter,config){let spinner=new Spinner(config.initialText??""),handlers=new Map;spinner.start();for(let[eventName,eventConfig]of Object.entries(config.events)){let handler=(data)=>{if(eventConfig.getText)spinner.currentText=eventConfig.getText(data);else if(eventConfig.text)spinner.currentText=eventConfig.text;if(eventConfig.succeed)spinner.succeed(),cleanup();else if(eventConfig.fail){let message=data instanceof Error?data.message:String(data??"Failed");spinner.fail(message),cleanup()}else if(eventConfig.stop)spinner.stop(),cleanup()};handlers.set(eventName,handler),emitter.on(eventName,handler)}function cleanup(){for(let[eventName,handler]of handlers)emitter.off(eventName,handler);handlers.clear()}return()=>{spinner.stop(),cleanup()}}async function waitForEvent(emitter,eventName,text,options={}){return new Promise((resolve,reject)=>{let spinner=new Spinner(text);spinner.start();let timer=null,cleanup=()=>{if(emitter.off(eventName,successHandler),options.errorEvent)emitter.off(options.errorEvent,errorHandler);if(timer)clearTimeout(timer)},successHandler=(data)=>{cleanup(),spinner.succeed(),resolve(data)},errorHandler=(error)=>{cleanup(),spinner.fail(error instanceof Error?error.message:"Error"),reject(error instanceof Error?error:Error(String(error)))};if(emitter.once(eventName,successHandler),options.errorEvent)emitter.once(options.errorEvent,errorHandler);if(options.timeout)timer=setTimeout(()=>{cleanup(),spinner.fail("Timeout"),reject(Error(`Timeout waiting for ${eventName}`))},options.timeout)})}import chalk4 from"chalk";async function withSelect(prompt,options,selectOptions={}){let{initial=0,maxVisible=10}=selectOptions,stream=process.stdout,stdin=process.stdin;if(!isTTY(stream)||!stdin.isTTY)return options[initial]?.value??options[0].value;return new Promise((resolve,reject)=>{let highlightIndex=Math.min(Math.max(0,initial),options.length-1),linesRendered=0;stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8"),write(CURSOR_HIDE,stream);function render(){if(linesRendered>0)write(cursorUp(linesRendered),stream);let scrollOffset=Math.max(0,Math.min(highlightIndex-Math.floor(maxVisible/2),options.length-maxVisible)),visibleCount=Math.min(maxVisible,options.length),visibleOptions=options.slice(scrollOffset,scrollOffset+visibleCount),hasMoreAbove=scrollOffset>0,hasMoreBelow=scrollOffset+visibleCount<options.length;write(`${CURSOR_TO_START}${chalk4.bold(prompt)}${CLEAR_LINE_END}
9
+ `,stream);let lines=1;if(hasMoreAbove)write(`${CURSOR_TO_START} ${chalk4.dim("...")}${CLEAR_LINE_END}
10
+ `,stream),lines++;for(let i=0;i<visibleOptions.length;i++){let option=visibleOptions[i],isHighlighted=scrollOffset+i===highlightIndex,indicator=isHighlighted?chalk4.cyan(">"):" ",label=isHighlighted?chalk4.cyan(option.label):option.label;write(`${CURSOR_TO_START}${indicator} ${label}${CLEAR_LINE_END}
11
+ `,stream),lines++}if(hasMoreBelow)write(`${CURSOR_TO_START} ${chalk4.dim("...")}${CLEAR_LINE_END}
12
+ `,stream),lines++;linesRendered=lines}function cleanup(){stdin.setRawMode(!1),stdin.pause(),stdin.removeListener("data",onKeypress),write(CURSOR_SHOW,stream)}function onKeypress(key){let keyCode=key.charCodeAt(0);if(key==="\x03"){cleanup(),reject(Error("User cancelled"));return}if(key==="\r"||key===`
13
+ `){cleanup(),resolve(options[highlightIndex].value);return}if(key==="\x1B"&&key.length===1){cleanup(),reject(Error("User cancelled"));return}if(key.startsWith("\x1B[")){let code=key.slice(2);if(code==="A")highlightIndex=Math.max(0,highlightIndex-1),render();else if(code==="B")highlightIndex=Math.min(options.length-1,highlightIndex+1),render();return}if(key==="j"||key==="J"){highlightIndex=Math.min(options.length-1,highlightIndex+1),render();return}if(key==="k"||key==="K"){highlightIndex=Math.max(0,highlightIndex-1),render();return}if(key===" "){cleanup(),resolve(options[highlightIndex].value);return}}stdin.on("data",onKeypress),render()})}function createSelect(defaultOptions={}){return(prompt,options,overrides={})=>withSelect(prompt,options,{...defaultOptions,...overrides})}import chalk5 from"chalk";async function withTextInput(prompt,options={}){let stream=options.stream??process.stdout,inputStream=options.inputStream??process.stdin,isTty=isTTY(stream),value=options.defaultValue??"",cursorPosition=value.length,errorMessage;if(inputStream.isTTY)inputStream.setRawMode(!0);inputStream.resume();let render=()=>{let displayValue=options.mask?options.mask.repeat(value.length):value,suggestion=getAutocompleteSuggestion(value,options.autocomplete),suggestionSuffix=suggestion?chalk5.dim(suggestion.slice(value.length)):"",beforeCursor=displayValue.slice(0,cursorPosition),cursorChar=displayValue[cursorPosition]??" ",afterCursor=displayValue.slice(cursorPosition+1),inputDisplay=!value&&options.placeholder?chalk5.dim(options.placeholder)+chalk5.inverse(" "):beforeCursor+chalk5.inverse(cursorChar)+afterCursor+suggestionSuffix,errorDisplay=errorMessage?chalk5.red(` (${errorMessage})`):"",line=`${chalk5.cyan("?")} ${chalk5.bold(prompt)} ${inputDisplay}${errorDisplay}`;if(isTty)write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`,stream)};if(isTty)write(CURSOR_HIDE,stream);return render(),new Promise((resolve,reject)=>{let cleanup=()=>{if(inputStream.removeListener("data",onData),inputStream.removeListener("error",onError),inputStream.isTTY)inputStream.setRawMode(!1);if(inputStream.pause(),isTty)write(CURSOR_SHOW,stream)},submit=()=>{if(options.validate){let error=options.validate(value);if(error){errorMessage=error,render();return}}cleanup();let displayValue=options.mask?options.mask.repeat(value.length):value;write(`${CURSOR_TO_START}${chalk5.green("✔")} ${chalk5.bold(prompt)} ${chalk5.dim(displayValue)}${CLEAR_LINE_END}
14
+ `,stream),resolve(value)},onError=(err)=>{cleanup(),reject(err)},onData=(data)=>{let input=data.toString();errorMessage=void 0;for(let i=0;i<input.length;i++){let char=input[i],code=char.charCodeAt(0);if(code===13||code===10){submit();return}if(code===3){cleanup(),write(`
15
+ `,stream),reject(Error("User aborted"));return}if(code===27){if(input[i+1]==="["){let arrowCode=input[i+2];if(arrowCode==="D"){cursorPosition=Math.max(0,cursorPosition-1),i+=2;continue}if(arrowCode==="C"){cursorPosition=Math.min(value.length,cursorPosition+1),i+=2;continue}if(arrowCode==="H"){cursorPosition=0,i+=2;continue}if(arrowCode==="F"){cursorPosition=value.length,i+=2;continue}i+=2;continue}value="",cursorPosition=0;continue}if(code===127||code===8){if(cursorPosition>0)value=value.slice(0,cursorPosition-1)+value.slice(cursorPosition),cursorPosition--;continue}if(code===4){if(cursorPosition<value.length)value=value.slice(0,cursorPosition)+value.slice(cursorPosition+1);continue}if(code===9){let suggestion=getAutocompleteSuggestion(value,options.autocomplete);if(suggestion)value=suggestion,cursorPosition=value.length;continue}if(code===1){cursorPosition=0;continue}if(code===5){cursorPosition=value.length;continue}if(code===21){value=value.slice(cursorPosition),cursorPosition=0;continue}if(code===11){value=value.slice(0,cursorPosition);continue}if(code===23){let before=value.slice(0,cursorPosition),after=value.slice(cursorPosition),trimmed=before.trimEnd(),lastSpace=trimmed.lastIndexOf(" "),newBefore=lastSpace===-1?"":trimmed.slice(0,lastSpace+1);value=newBefore+after,cursorPosition=newBefore.length;continue}if(code>=32&&code<127){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition++;continue}if(code>127){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition++;continue}}render()};inputStream.on("data",onData),inputStream.on("error",onError)})}function createTextInput(prompt,options={}){let stream=options.stream??process.stdout,isTty=isTTY(stream),value=options.defaultValue??"",cursorPosition=value.length;return{get value(){return value},set value(v){value=v,cursorPosition=Math.min(cursorPosition,v.length)},get cursorPosition(){return cursorPosition},set cursorPosition(pos){cursorPosition=Math.max(0,Math.min(value.length,pos))},render:()=>{let displayValue=options.mask?options.mask.repeat(value.length):value,suggestion=getAutocompleteSuggestion(value,options.autocomplete),suggestionSuffix=suggestion?chalk5.dim(suggestion.slice(value.length)):"",beforeCursor=displayValue.slice(0,cursorPosition),cursorChar=displayValue[cursorPosition]??" ",afterCursor=displayValue.slice(cursorPosition+1),inputDisplay=!value&&options.placeholder?chalk5.dim(options.placeholder)+chalk5.inverse(" "):beforeCursor+chalk5.inverse(cursorChar)+afterCursor+suggestionSuffix,line=`${chalk5.cyan("?")} ${chalk5.bold(prompt)} ${inputDisplay}`;if(isTty)write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`,stream)},insert(char){value=value.slice(0,cursorPosition)+char+value.slice(cursorPosition),cursorPosition+=char.length},backspace(){if(cursorPosition>0)value=value.slice(0,cursorPosition-1)+value.slice(cursorPosition),cursorPosition--},delete(){if(cursorPosition<value.length)value=value.slice(0,cursorPosition)+value.slice(cursorPosition+1)},clear(){value="",cursorPosition=0},acceptSuggestion(){let suggestion=getAutocompleteSuggestion(value,options.autocomplete);if(suggestion)value=suggestion,cursorPosition=value.length}}}function getAutocompleteSuggestion(value,autocomplete){if(!value||!autocomplete?.length)return;let lowerValue=value.toLowerCase();return autocomplete.find((item)=>item.toLowerCase().startsWith(lowerValue)&&item.length>value.length)}export{writeLine,write,wrapGenerator,wrapEmitter,withTextInput,withSpinner,withSelect,withProgress,withIterableProgress,withCursor,waitForEvent,isTTY,getTerminalWidth,cursorUp,cursorDown,createTextInput,createSpinner,createSelect,createProgressCallback,attachSpinner,Spinner,SPINNER_FRAMES,ProgressBar,MultiProgress,CURSOR_TO_START,CURSOR_SHOW,CURSOR_SAVE,CURSOR_RESTORE,CURSOR_HIDE,CLEAR_SCREEN,CLEAR_LINE_END,CLEAR_LINE};
16
+
17
+ //# debugId=9D8095BD530610BD64756E2164756E21
package/dist/ui.js.map ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../packages/ag-react/src/ui/cli/spinner.ts", "../packages/ag-react/src/ui/cli/ansi.ts", "../packages/ag-react/src/ui/cli/progress-bar.ts", "../packages/ag-react/src/ui/utils/eta.ts", "../packages/ag-react/src/ui/cli/multi-progress.ts", "../packages/ag-react/src/ui/wrappers/with-spinner.ts", "../packages/ag-react/src/ui/wrappers/with-progress.ts", "../packages/ag-react/src/ui/wrappers/wrap-generator.ts", "../packages/ag-react/src/ui/wrappers/wrap-emitter.ts", "../packages/ag-react/src/ui/wrappers/with-select.ts", "../packages/ag-react/src/ui/wrappers/with-text-input.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * CLI Spinner - Animated indeterminate progress indicator\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerOptions, SpinnerStyle } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"./ansi\"\n\n/** Spinner animation frames by style */\nexport const SPINNER_FRAMES: Record<SpinnerStyle, string[]> = {\n dots: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n line: [\"-\", \"\\\\\", \"|\", \"/\"],\n arc: [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"],\n bounce: [\"⠁\", \"⠂\", \"⠄\", \"⠂\"],\n pulse: [\"█\", \"▓\", \"▒\", \"░\", \"▒\", \"▓\"],\n}\n\n/** Default intervals for each style (ms) */\nexport const SPINNER_INTERVALS: Record<SpinnerStyle, number> = {\n dots: 80,\n line: 120,\n arc: 100,\n bounce: 120,\n pulse: 100,\n}\n\n/**\n * Spinner class for CLI progress indication\n *\n * @example\n * ```ts\n * const spinner = new Spinner(\"Loading...\");\n * spinner.start();\n * await doWork();\n * spinner.succeed(\"Done!\");\n * ```\n */\nexport class Spinner {\n private text: string\n private style: SpinnerStyle\n private color: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private interval: number\n\n private frameIndex = 0\n private timer: ReturnType<typeof setInterval> | null = null\n private isSpinning = false\n\n constructor(textOrOptions?: string | SpinnerOptions) {\n const options: SpinnerOptions = typeof textOrOptions === \"string\" ? { text: textOrOptions } : (textOrOptions ?? {})\n\n this.text = options.text ?? \"\"\n this.style = options.style ?? \"dots\"\n this.color = options.color ?? \"cyan\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.interval = options.interval ?? SPINNER_INTERVALS[this.style]\n }\n\n /** Get current spinner text */\n get currentText(): string {\n return this.text\n }\n\n /** Set spinner text (updates immediately if spinning) */\n set currentText(value: string) {\n this.text = value\n if (this.isSpinning) {\n this.render()\n }\n }\n\n /** Check if spinner is currently active */\n get spinning(): boolean {\n return this.isSpinning\n }\n\n /**\n * Start the spinner animation\n */\n start(text?: string): this {\n if (text !== undefined) {\n this.text = text\n }\n\n if (this.isSpinning) {\n return this\n }\n\n this.isSpinning = true\n this.frameIndex = 0\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES[this.style].length\n this.render()\n }, this.interval)\n\n return this\n }\n\n /**\n * Stop the spinner\n */\n stop(): this {\n if (!this.isSpinning) {\n return this\n }\n\n this.isSpinning = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n this.clear()\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /**\n * Stop with success message (green checkmark)\n */\n succeed(text?: string): this {\n return this.stopWithSymbol(chalk.green(\"✔\"), text ?? this.text)\n }\n\n /**\n * Stop with failure message (red X)\n */\n fail(text?: string): this {\n return this.stopWithSymbol(chalk.red(\"✖\"), text ?? this.text)\n }\n\n /**\n * Stop with warning message (yellow warning)\n */\n warn(text?: string): this {\n return this.stopWithSymbol(chalk.yellow(\"⚠\"), text ?? this.text)\n }\n\n /**\n * Stop with info message (blue info)\n */\n info(text?: string): this {\n return this.stopWithSymbol(chalk.blue(\"ℹ\"), text ?? this.text)\n }\n\n /**\n * Clear the spinner line\n */\n clear(): this {\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n }\n return this\n }\n\n private render(): void {\n const frame = SPINNER_FRAMES[this.style][this.frameIndex]\n const colorFn = (chalk as unknown as Record<string, (s: string) => string>)[this.color]\n const coloredFrame = colorFn ? colorFn(frame!) : frame!\n const output = this.text ? `${coloredFrame} ${this.text}` : coloredFrame\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n private stopWithSymbol(symbol: string, text: string): this {\n this.stop()\n write(`${symbol} ${text}\\n`, this.stream)\n return this\n }\n\n /**\n * Dispose the spinner (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Static helper to quickly start a spinner\n * Returns a stop function\n *\n * @example\n * ```ts\n * const stop = Spinner.start(\"Loading...\");\n * await doWork();\n * stop();\n * ```\n */\n static start(textOrOptions?: string | SpinnerOptions): () => void {\n const spinner = new Spinner(textOrOptions)\n spinner.start()\n return () => spinner.stop()\n }\n}\n\n/**\n * Callable spinner interface - call with text to show/update\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * spinner(\"Loading...\"); // Shows spinner with text\n * spinner(\"Still loading...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport interface CallableSpinner extends Disposable {\n /** Call with text to show/update the spinner */\n (text: string): void\n /** Stop the spinner */\n stop(): void\n /** Stop with success message (green checkmark) */\n succeed(text?: string): void\n /** Stop with failure message (red X) */\n fail(text?: string): void\n /** Stop with warning message (yellow warning) */\n warn(text?: string): void\n /** Stop with info message (blue info) */\n info(text?: string): void\n}\n\n/**\n * Create a callable, disposable spinner\n *\n * The spinner is lazy - it won't show anything until you call it with text.\n * Use with `using` for automatic cleanup:\n *\n * @example\n * ```ts\n * {\n * using spinner = createSpinner({ style: \"dots\" });\n * // Nothing visible yet\n *\n * spinner(\"Loading repo...\"); // Now shows spinner\n * spinner(\"Applying events...\"); // Updates text\n * } // Auto-stops via Symbol.dispose\n * ```\n */\nexport function createSpinner(options?: SpinnerOptions): CallableSpinner {\n const spinner = new Spinner({ ...options, text: \"\" })\n\n const callable = ((text: string) => {\n // Always restart if not spinning (handles initial call and after succeed/fail/etc)\n if (!spinner.spinning) {\n spinner.start(text)\n } else {\n spinner.currentText = text\n }\n }) as CallableSpinner\n\n callable.stop = () => spinner.stop()\n callable.succeed = (text) => spinner.succeed(text)\n callable.fail = (text) => spinner.fail(text)\n callable.warn = (text) => spinner.warn(text)\n callable.info = (text) => spinner.info(text)\n callable[Symbol.dispose] = () => spinner.stop()\n\n return callable\n}\n",
6
+ "/**\n * ANSI escape code utilities for terminal control\n */\n\n/** Hide the cursor */\nexport const CURSOR_HIDE = \"\\x1b[?25l\"\n\n/** Show the cursor */\nexport const CURSOR_SHOW = \"\\x1b[?25h\"\n\n/** Move cursor to beginning of line */\nexport const CURSOR_TO_START = \"\\r\"\n\n/** Clear from cursor to end of line */\nexport const CLEAR_LINE_END = \"\\x1b[K\"\n\n/** Clear entire line */\nexport const CLEAR_LINE = \"\\x1b[2K\"\n\n/** Clear screen and move to top-left */\nexport const CLEAR_SCREEN = \"\\x1b[2J\\x1b[H\"\n\n/** Move cursor up N lines */\nexport const cursorUp = (n: number = 1): string => `\\x1b[${n}A`\n\n/** Move cursor down N lines */\nexport const cursorDown = (n: number = 1): string => `\\x1b[${n}B`\n\n/** Save cursor position */\nexport const CURSOR_SAVE = \"\\x1b[s\"\n\n/** Restore cursor position */\nexport const CURSOR_RESTORE = \"\\x1b[u\"\n\n/**\n * Write to stream with proper handling\n */\nexport function write(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(text)\n}\n\n/**\n * Clear the current line and write new text\n */\nexport function writeLine(text: string, stream: NodeJS.WriteStream = process.stdout): void {\n stream.write(`${CURSOR_TO_START}${text}${CLEAR_LINE_END}`)\n}\n\n/**\n * Wrap a function to handle cursor visibility\n * Hides cursor on start, shows on completion/error\n */\nexport function withCursor<T>(fn: () => T | Promise<T>, stream: NodeJS.WriteStream = process.stdout): Promise<T> {\n stream.write(CURSOR_HIDE)\n\n const restore = () => stream.write(CURSOR_SHOW)\n\n try {\n const result = fn()\n if (result instanceof Promise) {\n return result.finally(restore)\n }\n restore()\n return Promise.resolve(result)\n } catch (error) {\n restore()\n throw error\n }\n}\n\n/**\n * Check if stream is a TTY (supports ANSI codes)\n * Also respects FORCE_TTY environment variable for testing\n */\nexport function isTTY(stream: NodeJS.WriteStream = process.stdout): boolean {\n if (process.env.FORCE_TTY === \"1\") return true\n return stream.isTTY ?? false\n}\n\n/**\n * Get terminal width\n */\nexport function getTerminalWidth(stream: NodeJS.WriteStream = process.stdout): number {\n return stream.columns ?? 80\n}\n",
7
+ "/**\n * CLI ProgressBar - Determinate progress indicator with ETA\n */\n\nimport chalk from \"chalk\"\nimport type { ProgressBarOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY, getTerminalWidth } from \"./ansi\"\nimport { calculateETA, formatETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/** Default format string */\nconst DEFAULT_FORMAT = \":bar :percent | :current/:total | ETA: :eta\"\n\n/**\n * ProgressBar class for CLI progress indication\n *\n * @example\n * ```ts\n * const bar = new ProgressBar({ total: 100 });\n * bar.start();\n * for (let i = 0; i <= 100; i++) {\n * await doWork();\n * bar.update(i);\n * }\n * bar.stop();\n * ```\n */\nexport class ProgressBar {\n private total: number\n private format: string\n private width: number\n private complete: string\n private incomplete: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private phases: Record<string, string>\n\n private current = 0\n private phase: string | null = null\n private startTime: number | null = null\n private isActive = false\n\n // ETA smoothing - track last N update times\n private etaBuffer: ETASample[] = []\n\n constructor(options: ProgressBarOptions = {}) {\n this.total = options.total ?? 100\n this.format = options.format ?? DEFAULT_FORMAT\n this.width = options.width ?? 40\n this.complete = options.complete ?? \"█\"\n this.incomplete = options.incomplete ?? \"░\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.phases = options.phases ?? {}\n }\n\n /**\n * Start the progress bar\n */\n start(initialValue = 0, initialTotal?: number): this {\n if (initialTotal !== undefined) {\n this.total = initialTotal\n }\n\n this.current = initialValue\n this.startTime = Date.now()\n this.isActive = true\n this.etaBuffer = [{ time: this.startTime, value: initialValue }]\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n return this\n }\n\n /**\n * Update progress value\n */\n update(value: number, tokens?: Record<string, string | number>): this {\n this.current = Math.min(value, this.total)\n\n // Update ETA buffer\n const now = Date.now()\n this.etaBuffer.push({ time: now, value: this.current })\n if (this.etaBuffer.length > DEFAULT_ETA_BUFFER_SIZE) {\n this.etaBuffer.shift()\n }\n\n if (this.isActive) {\n this.render(tokens)\n }\n\n return this\n }\n\n /**\n * Increment progress by amount (default: 1)\n */\n increment(amount = 1, tokens?: Record<string, string | number>): this {\n return this.update(this.current + amount, tokens)\n }\n\n /**\n * Set the current phase (for multi-phase progress)\n */\n setPhase(phaseName: string, options?: { current?: number; total?: number }): this {\n this.phase = phaseName\n\n if (options?.total !== undefined) {\n this.total = options.total\n }\n if (options?.current !== undefined) {\n this.current = options.current\n // Reset ETA buffer on phase change\n this.etaBuffer = [{ time: Date.now(), value: this.current }]\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return this\n }\n\n /**\n * Stop the progress bar\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (clear && isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n } else {\n write(\"\\n\", this.stream)\n }\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** Get ETA in seconds using smoothed rate */\n private getETASeconds(): number | null {\n return calculateETA(this.etaBuffer, this.current, this.total)\n }\n\n /**\n * Render the progress bar\n */\n private render(tokens?: Record<string, string | number>): void {\n const percent = this.total > 0 ? this.current / this.total : 0\n const eta = this.getETASeconds()\n\n // Build the bar\n const completeLength = Math.round(this.width * percent)\n const incompleteLength = this.width - completeLength\n const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(incompleteLength)\n\n // Get phase display name\n const phaseDisplay = this.phase ? (this.phases[this.phase] ?? this.phase) : \"\"\n\n // Calculate rate\n const elapsed = this.startTime ? (Date.now() - this.startTime) / 1000 : 0\n const rate = elapsed > 0 ? this.current / elapsed : 0\n\n // Replace tokens in format string\n let output = this.format\n .replace(\":bar\", chalk.cyan(bar))\n .replace(\":percent\", `${Math.round(percent * 100)}%`.padStart(4))\n .replace(\":current\", String(this.current))\n .replace(\":total\", String(this.total))\n .replace(\":eta\", formatETA(eta))\n .replace(\":elapsed\", formatETA(elapsed))\n .replace(\":rate\", rate.toFixed(1))\n .replace(\":phase\", chalk.dim(phaseDisplay))\n\n // Replace custom tokens\n if (tokens) {\n for (const [key, value] of Object.entries(tokens)) {\n output = output.replace(`:${key}`, String(value))\n }\n }\n\n // Truncate to terminal width\n const termWidth = getTerminalWidth(this.stream)\n if (output.length > termWidth) {\n output = output.slice(0, termWidth - 1)\n }\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n /**\n * Get current progress ratio (0-1)\n */\n get ratio(): number {\n return this.total > 0 ? this.current / this.total : 0\n }\n\n /**\n * Get current progress percentage (0-100)\n */\n get percentage(): number {\n return Math.round(this.ratio * 100)\n }\n\n /**\n * Dispose the progress bar (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n}\n",
8
+ "/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n",
9
+ "/**\n * MultiProgress - Container for managing multiple concurrent progress indicators\n */\n\nimport chalk from \"chalk\"\nimport type { SpinnerStyle, TaskStatus } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CLEAR_LINE, cursorUp, write, isTTY } from \"./ansi\"\nimport { Spinner, SPINNER_FRAMES } from \"./spinner\"\nimport { ProgressBar } from \"./progress-bar\"\n\n/** Status icons */\nconst STATUS_ICONS: Record<TaskStatus, string> = {\n pending: chalk.gray(\"○\"),\n running: \"\", // Will be replaced with spinner frame\n completed: chalk.green(\"✔\"),\n failed: chalk.red(\"✖\"),\n skipped: chalk.yellow(\"⊘\"),\n}\n\n/** Task configuration */\ninterface TaskConfig {\n title: string\n type: \"spinner\" | \"bar\" | \"group\"\n status: TaskStatus\n total?: number\n current?: number\n spinnerStyle?: SpinnerStyle\n indent?: number\n}\n\n/** Internal task state */\ninterface TaskState extends TaskConfig {\n id: string\n /** Completion time in ms (shown dimmed after title on completion) */\n completionTime?: number\n}\n\n/**\n * MultiProgress - Manage multiple concurrent progress indicators\n *\n * @example\n * ```ts\n * const multi = new MultiProgress();\n *\n * const download = multi.add(\"Downloading files\", { type: \"bar\", total: 100 });\n * const process = multi.add(\"Processing\", { type: \"spinner\" });\n *\n * download.start();\n * download.update(50);\n * download.complete();\n *\n * process.start();\n * process.complete();\n *\n * multi.stop();\n * ```\n */\nexport class MultiProgress {\n private tasks: Map<string, TaskState> = new Map()\n private taskOrder: string[] = []\n private stream: NodeJS.WriteStream\n private isActive = false\n private timer: ReturnType<typeof setInterval> | null = null\n private frameIndex = 0\n private renderedLines = 0\n\n constructor(stream: NodeJS.WriteStream = process.stdout) {\n this.stream = stream\n }\n\n /**\n * Add a new task\n * @param insertAfter - ID of task to insert after (for hierarchical display)\n */\n add(\n title: string,\n options: {\n type?: \"spinner\" | \"bar\" | \"group\"\n total?: number\n spinnerStyle?: SpinnerStyle\n indent?: number\n insertAfter?: string\n } = {},\n ): TaskHandle {\n const id = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n\n const task: TaskState = {\n id,\n title,\n type: options.type ?? \"spinner\",\n status: \"pending\",\n total: options.total,\n current: 0,\n spinnerStyle: options.spinnerStyle ?? \"dots\",\n indent: options.indent ?? 0,\n }\n\n this.tasks.set(id, task)\n\n // Insert after specified task, or append to end\n if (options.insertAfter) {\n const afterIndex = this.taskOrder.indexOf(options.insertAfter)\n if (afterIndex >= 0) {\n this.taskOrder.splice(afterIndex + 1, 0, id)\n } else {\n this.taskOrder.push(id)\n }\n } else {\n this.taskOrder.push(id)\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return new TaskHandle(this, id)\n }\n\n /**\n * Start the multi-progress display\n */\n start(): this {\n if (this.isActive) {\n return this\n }\n\n this.isActive = true\n\n if (isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n\n // Start animation timer\n this.timer = setInterval(() => {\n this.frameIndex = (this.frameIndex + 1) % 10\n this.render()\n }, 80)\n\n return this\n }\n\n /**\n * Dispose the multi-progress display (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n\n /**\n * Stop the multi-progress display\n * @param clear - If true, clear all task lines from terminal\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n if (clear && isTTY(this.stream)) {\n // Clear all rendered lines\n if (this.renderedLines > 0) {\n write(cursorUp(this.renderedLines), this.stream)\n for (let i = 0; i < this.renderedLines; i++) {\n write(`${CLEAR_LINE}\\n`, this.stream)\n }\n write(cursorUp(this.renderedLines), this.stream)\n }\n } else {\n // Final render\n this.render()\n write(\"\\n\", this.stream)\n }\n\n if (isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** @internal Update task state */\n _updateTask(id: string, updates: Partial<TaskState>): void {\n const task = this.tasks.get(id)\n if (task) {\n Object.assign(task, updates)\n // Only render immediately for status changes (complete/fail/etc.)\n // Progress updates (current/total) are debounced by the 80ms animation timer\n if (this.isActive && updates.status) {\n this.render()\n }\n }\n }\n\n /** @internal Get task state */\n _getTask(id: string): TaskState | undefined {\n return this.tasks.get(id)\n }\n\n private render(): void {\n if (!isTTY(this.stream)) {\n return\n }\n\n // Move cursor up to clear previous render\n if (this.renderedLines > 0) {\n write(cursorUp(this.renderedLines), this.stream)\n }\n\n const lines: string[] = []\n\n for (const id of this.taskOrder) {\n const task = this.tasks.get(id)\n if (!task) continue\n\n let icon: string\n if (task.status === \"running\") {\n if (task.type === \"group\") {\n // Groups don't animate - keep pending icon while running\n icon = STATUS_ICONS.pending\n } else {\n const frames = SPINNER_FRAMES[task.spinnerStyle ?? \"dots\"]\n icon = chalk.cyan(frames[this.frameIndex % frames.length])\n }\n } else {\n icon = STATUS_ICONS[task.status]\n }\n\n const indent = \" \".repeat(task.indent ?? 0)\n let line = `${indent}${icon} ${task.title}`\n\n // Add progress bar for bar type\n if (task.type === \"bar\" && task.total && task.total > 0) {\n const percent = task.current! / task.total\n const barWidth = 20\n const filled = Math.round(barWidth * percent)\n const empty = barWidth - filled\n const bar = chalk.cyan(\"█\".repeat(filled)) + chalk.gray(\"░\".repeat(empty))\n line += ` ${bar} ${Math.round(percent * 100)}%`\n }\n\n // Add completion time in dimmed text\n if (task.status === \"completed\" && task.completionTime !== undefined) {\n line += chalk.dim(` ${task.completionTime}ms`)\n }\n\n lines.push(line)\n }\n\n // Clear and write each line\n for (const line of lines) {\n write(`${CLEAR_LINE}${line}\\n`, this.stream)\n }\n\n this.renderedLines = lines.length\n }\n}\n\n/**\n * Handle for controlling an individual task\n */\nclass TaskHandle {\n constructor(\n private multi: MultiProgress,\n private _id: string,\n ) {}\n\n /** Get task ID (for insertAfter) */\n get id(): string {\n return this._id\n }\n\n /** Start the task (set status to running) */\n start(): this {\n this.multi._updateTask(this._id, { status: \"running\" })\n return this\n }\n\n /** Update progress (for bar type) */\n update(current: number): this {\n this.multi._updateTask(this._id, { current })\n return this\n }\n\n /** Mark task as completed */\n complete(titleOrTime?: string | number): this {\n const updates: Partial<TaskState> = { status: \"completed\" }\n if (typeof titleOrTime === \"number\") {\n // Numeric = completion time in ms (preserves current title)\n updates.completionTime = titleOrTime\n } else if (titleOrTime) {\n // String = new title (legacy behavior)\n updates.title = titleOrTime\n }\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Mark task as failed */\n fail(title?: string): this {\n const updates: Partial<TaskState> = { status: \"failed\" }\n if (title) updates.title = title\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Mark task as skipped */\n skip(title?: string): this {\n const updates: Partial<TaskState> = { status: \"skipped\" }\n if (title) updates.title = title\n this.multi._updateTask(this._id, updates)\n return this\n }\n\n /** Update task title */\n setTitle(title: string): this {\n this.multi._updateTask(this._id, { title })\n return this\n }\n\n /** Change task type (e.g., from spinner to group when sub-steps are added) */\n setType(type: \"spinner\" | \"bar\" | \"group\"): this {\n this.multi._updateTask(this._id, { type })\n return this\n }\n\n /** Get current status */\n get status(): TaskStatus {\n return this.multi._getTask(this._id)?.status ?? \"pending\"\n }\n}\n\nexport type { TaskHandle }\n",
10
+ "/**\n * withSpinner - Wrap promises with an animated spinner\n */\n\nimport type { WithSpinnerOptions } from \"../types.js\"\nimport { Spinner } from \"../cli/spinner\"\n\n/**\n * Wrap a promise with an animated spinner\n *\n * @example\n * ```ts\n * // Simple usage\n * const data = await withSpinner(fetchData(), \"Loading data...\");\n *\n * // With options\n * const result = await withSpinner(\n * processFiles(),\n * \"Processing...\",\n * { style: \"arc\", clearOnComplete: true }\n * );\n *\n * // With dynamic text\n * const result = await withSpinner(\n * longOperation(),\n * (elapsed) => `Processing... (${elapsed}s)`\n * );\n * ```\n */\nexport async function withSpinner<T>(\n promise: Promise<T> | (() => T | Promise<T>),\n text: string | ((elapsedSeconds: number) => string),\n options: WithSpinnerOptions = {},\n): Promise<T> {\n const spinner = new Spinner({\n text: typeof text === \"string\" ? text : text(0),\n style: options.style,\n color: options.color,\n })\n\n let timer: ReturnType<typeof setInterval> | null = null\n const startTime = Date.now()\n\n spinner.start()\n\n // Update text if dynamic\n if (typeof text === \"function\") {\n timer = setInterval(() => {\n const elapsed = Math.floor((Date.now() - startTime) / 1000)\n spinner.currentText = text(elapsed)\n }, 1000)\n }\n\n try {\n const result = await (typeof promise === \"function\" ? promise() : promise)\n\n if (timer) clearInterval(timer)\n\n if (options.clearOnComplete) {\n spinner.stop()\n } else {\n spinner.succeed()\n }\n\n return result\n } catch (error) {\n if (timer) clearInterval(timer)\n spinner.fail(error instanceof Error ? error.message : \"Failed\")\n throw error\n }\n}\n\n/**\n * Attach a spinner to a promise for manual control\n * Returns [result, spinner] tuple for custom control\n *\n * @example\n * ```ts\n * const [promise, spinner] = attachSpinner(fetchData(), \"Loading...\");\n * spinner.text = \"Still loading...\";\n * const result = await promise;\n * spinner.succeed(\"Loaded!\");\n * ```\n */\nexport function attachSpinner<T>(\n promise: Promise<T>,\n text: string,\n options: WithSpinnerOptions = {},\n): [Promise<T>, Spinner] {\n const spinner = new Spinner({\n text,\n style: options.style,\n color: options.color,\n })\n\n spinner.start()\n\n async function wrapPromise(): Promise<T> {\n try {\n return await promise\n } catch (error) {\n spinner.fail(error instanceof Error ? error.message : \"Failed\")\n throw error\n }\n }\n\n return [wrapPromise(), spinner]\n}\n",
11
+ "/**\n * withProgress - Wrap callback-based progress functions\n *\n * @deprecated Use `steps()` from `@silvery/ag-react/ui/progress` instead.\n *\n * @example\n * ```typescript\n * // OLD (deprecated):\n * import { withProgress } from \"@silvery/ag-react/ui/wrappers\";\n * const result = await withProgress(\n * (onProgress) => manager.syncFromFs(onProgress),\n * { phases: SYNC_PHASES }\n * );\n *\n * // NEW:\n * import { steps } from \"@silvery/ag-react/ui/progress\";\n * const results = await steps({ syncFiles: () => manager.syncFromFs() }).run();\n * ```\n */\n\nimport type { ProgressInfo, ProgressCallback, WithProgressOptions } from \"../types.js\"\nimport { ProgressBar } from \"../cli/progress-bar\"\nimport { Spinner } from \"../cli/spinner\"\nimport { CURSOR_HIDE, CURSOR_SHOW, write, isTTY } from \"../cli/ansi\"\n\n// Declare timer globals (not exposed by bun-types)\ndeclare function setTimeout(callback: () => void, ms: number): unknown\ndeclare function clearTimeout(id: unknown): void\n\n// Timer type - opaque handle, we only store and clear it\ntype TimerId = unknown\n\n/**\n * Wrap a function that takes a progress callback\n *\n * @example\n * ```ts\n * // Wrap existing km sync API\n * const result = await withProgress(\n * (onProgress) => manager.syncFromFs(onProgress),\n * {\n * phases: {\n * scanning: \"Scanning files\",\n * reconciling: \"Reconciling changes\",\n * rules: \"Evaluating rules\"\n * }\n * }\n * );\n *\n * // Simple usage without phases\n * await withProgress((onProgress) => rebuildState(onProgress));\n *\n * // With custom format\n * await withProgress(\n * (p) => processFiles(p),\n * { format: \":phase :bar :percent\" }\n * );\n *\n * // Show loading immediately (showAfter: 0) or after delay\n * await withProgress(\n * (p) => slowOperation(p),\n * { showAfter: 1000, initialMessage: \"Loading...\" }\n * );\n * ```\n */\nexport async function withProgress<T>(\n fn: (onProgress: ProgressCallback) => T | Promise<T>,\n options: WithProgressOptions = {},\n): Promise<T> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n // Determine format\n const format =\n options.format ?? (options.phases ? \":phase [:bar] :current/:total\" : \"[:bar] :current/:total :percent\")\n\n const bar = new ProgressBar({\n format,\n phases: options.phases ?? {},\n hideCursor: true,\n })\n\n let lastPhase: string | null = null\n let started = false\n\n // Initial spinner (shown before progress starts)\n const showAfter = options.showAfter ?? 1000\n const initialMessage = options.initialMessage ?? \"Loading...\"\n let spinner: Spinner | null = null\n let spinnerTimerId: TimerId | null = null\n\n // Hide cursor\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n // Schedule initial spinner if configured\n if (isTty && showAfter >= 0) {\n spinnerTimerId = setTimeout(() => {\n if (!started) {\n spinner = new Spinner({ text: initialMessage })\n spinner.start()\n }\n }, showAfter)\n }\n\n const onProgress: ProgressCallback = (info: ProgressInfo) => {\n // Stop initial spinner if it was shown\n if (spinner) {\n spinner.stop()\n spinner = null\n }\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n spinnerTimerId = null\n }\n\n // Handle phase transitions\n if (info.phase && info.phase !== lastPhase) {\n if (lastPhase !== null && isTty) {\n // Print newline before switching phases\n write(\"\\n\", stream)\n }\n lastPhase = info.phase\n\n // Start or update bar with new phase\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.setPhase(info.phase, { current: info.current, total: info.total })\n } else {\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.update(info.current)\n }\n }\n\n try {\n const result = await fn(onProgress)\n\n // Clean up spinner if still pending\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n }\n // Note: spinner may be set by setTimeout callback - TS can't track this\n const pendingSpinner = spinner as unknown as Spinner | null\n if (pendingSpinner) {\n pendingSpinner.stop()\n }\n\n // Stop and show cursor\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n\n return result\n } catch (error) {\n // Clean up spinner\n if (spinnerTimerId !== null) {\n clearTimeout(spinnerTimerId)\n }\n // Note: spinner may be set by setTimeout callback - TS can't track this\n const errorSpinner = spinner as unknown as Spinner | null\n if (errorSpinner) {\n errorSpinner.stop()\n }\n\n // Restore cursor on error\n if (started) {\n bar.stop()\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n\n/**\n * Create a progress callback that can be passed to existing APIs\n * Returns [callback, complete] tuple\n *\n * @example\n * ```ts\n * const [onProgress, complete] = createProgressCallback({\n * phases: { scanning: \"Scanning\", reconciling: \"Reconciling\" }\n * });\n *\n * const result = await manager.syncFromFs(onProgress);\n * complete();\n * ```\n */\nexport function createProgressCallback(options: WithProgressOptions = {}): [ProgressCallback, () => void] {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n const format =\n options.format ?? (options.phases ? \":phase [:bar] :current/:total\" : \"[:bar] :current/:total :percent\")\n\n const bar = new ProgressBar({\n format,\n phases: options.phases ?? {},\n hideCursor: true,\n })\n\n let lastPhase: string | null = null\n let started = false\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n const callback: ProgressCallback = (info: ProgressInfo) => {\n if (info.phase && info.phase !== lastPhase) {\n if (lastPhase !== null && isTty) {\n write(\"\\n\", stream)\n }\n lastPhase = info.phase\n\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.setPhase(info.phase, { current: info.current, total: info.total })\n } else {\n if (!started) {\n bar.start(info.current, info.total)\n started = true\n }\n bar.update(info.current)\n }\n }\n\n const complete = () => {\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n }\n\n return [callback, complete]\n}\n",
12
+ "/**\n * wrapGenerator - Consume a generator while showing progress\n */\n\nimport type { ProgressGenerator } from \"../types.js\"\nimport { ProgressBar } from \"../cli/progress-bar\"\nimport { CURSOR_HIDE, CURSOR_SHOW, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Consume a progress generator while displaying progress\n *\n * @example\n * ```ts\n * // Wrap existing generator (like evaluateAllRules())\n * await wrapGenerator(evaluateAllRules(), \"Evaluating rules\");\n *\n * // With custom format\n * await wrapGenerator(\n * processItems(),\n * ({ current, total }) => `Processing: ${current}/${total}`\n * );\n *\n * // Get the generator's return value\n * const result = await wrapGenerator(generatorWithReturn(), \"Processing\");\n * ```\n */\nexport async function wrapGenerator<T>(\n generator: ProgressGenerator<T>,\n textOrFormat: string | ((progress: { current: number; total: number }) => string),\n options: { clearOnComplete?: boolean } = {},\n): Promise<T> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n const isCustomFormat = typeof textOrFormat === \"function\"\n const label = isCustomFormat ? \"\" : textOrFormat\n\n const bar = new ProgressBar({\n format: label ? `${label} [:bar] :current/:total :percent` : \":bar :current/:total :percent\",\n hideCursor: true,\n })\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n let started = false\n let result: IteratorResult<{ current: number; total: number }, T>\n\n try {\n // Consume the generator\n while (true) {\n result = generator.next()\n\n if (result.done) {\n break\n }\n\n const { current, total } = result.value\n\n if (!started) {\n bar.start(current, total)\n started = true\n } else {\n bar.update(current)\n }\n }\n\n // Stop bar\n if (started) {\n bar.stop(options.clearOnComplete)\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n\n return result.value\n } catch (error) {\n if (started) {\n bar.stop()\n }\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n\n/**\n * Create an async iterable wrapper that shows progress\n *\n * @example\n * ```ts\n * const items = [1, 2, 3, 4, 5];\n * for await (const item of withIterableProgress(items, \"Processing\")) {\n * await processItem(item);\n * }\n * ```\n */\nexport async function* withIterableProgress<T>(\n iterable: Iterable<T> | AsyncIterable<T>,\n label: string,\n options: { clearOnComplete?: boolean } = {},\n): AsyncGenerator<T, void, unknown> {\n const stream = process.stdout\n const isTty = isTTY(stream)\n\n // Try to get length if array\n const items = Array.isArray(iterable) ? iterable : null\n const total = items?.length ?? 0\n\n const bar = new ProgressBar({\n format: `${label} [:bar] :current/:total :percent`,\n total,\n hideCursor: true,\n })\n\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n let current = 0\n bar.start(0, total)\n\n try {\n for await (const item of iterable as AsyncIterable<T>) {\n yield item\n current++\n bar.update(current)\n }\n\n bar.stop(options.clearOnComplete)\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n } catch (error) {\n bar.stop()\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n throw error\n }\n}\n",
13
+ "/**\n * wrapEmitter - Track EventEmitter state changes with progress indicators\n */\n\nimport type { EventEmitter } from \"events\"\nimport { Spinner } from \"../cli/spinner\"\n\n/** Event handler configuration */\ninterface EventConfig {\n /** Display text for this event */\n text?: string\n /** Dynamic text based on event data */\n getText?: (data: unknown) => string\n /** Mark spinner as succeeded */\n succeed?: boolean\n /** Mark spinner as failed */\n fail?: boolean\n /** Stop tracking */\n stop?: boolean\n}\n\n/** Configuration for wrapEmitter */\ninterface WrapEmitterConfig {\n /** Event handlers */\n events: Record<string, EventConfig>\n /** Initial text */\n initialText?: string\n}\n\n/**\n * Track EventEmitter state changes with a spinner\n *\n * @example\n * ```ts\n * const stop = wrapEmitter(syncManager, {\n * initialText: \"Starting sync...\",\n * events: {\n * 'ready': { text: \"Watcher ready\", succeed: true },\n * 'state-change': { getText: (s) => `State: ${s}` },\n * 'error': { fail: true },\n * 'idle': { stop: true }\n * }\n * });\n *\n * // Later, to stop manually\n * stop();\n * ```\n */\nexport function wrapEmitter(emitter: EventEmitter, config: WrapEmitterConfig): () => void {\n const spinner = new Spinner(config.initialText ?? \"\")\n const handlers: Map<string, (...args: unknown[]) => void> = new Map()\n\n spinner.start()\n\n // Set up event handlers\n for (const [eventName, eventConfig] of Object.entries(config.events)) {\n const handler = (data: unknown) => {\n // Update text\n if (eventConfig.getText) {\n spinner.currentText = eventConfig.getText(data)\n } else if (eventConfig.text) {\n spinner.currentText = eventConfig.text\n }\n\n // Handle terminal states\n if (eventConfig.succeed) {\n spinner.succeed()\n cleanup()\n } else if (eventConfig.fail) {\n const message = data instanceof Error ? data.message : String(data ?? \"Failed\")\n spinner.fail(message)\n cleanup()\n } else if (eventConfig.stop) {\n spinner.stop()\n cleanup()\n }\n }\n\n handlers.set(eventName, handler)\n emitter.on(eventName, handler)\n }\n\n // Cleanup function\n function cleanup() {\n for (const [eventName, handler] of handlers) {\n emitter.off(eventName, handler)\n }\n handlers.clear()\n }\n\n // Return stop function\n return () => {\n spinner.stop()\n cleanup()\n }\n}\n\n/**\n * Wait for an EventEmitter to emit a specific event\n * Shows a spinner while waiting\n *\n * @example\n * ```ts\n * await waitForEvent(syncManager, \"ready\", \"Waiting for watcher...\");\n * ```\n */\nexport async function waitForEvent(\n emitter: EventEmitter,\n eventName: string,\n text: string,\n options: {\n errorEvent?: string\n timeout?: number\n } = {},\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const spinner = new Spinner(text)\n spinner.start()\n\n let timer: ReturnType<typeof setTimeout> | null = null\n\n const cleanup = () => {\n emitter.off(eventName, successHandler)\n if (options.errorEvent) {\n emitter.off(options.errorEvent, errorHandler)\n }\n if (timer) {\n clearTimeout(timer)\n }\n }\n\n const successHandler = (data: unknown) => {\n cleanup()\n spinner.succeed()\n resolve(data)\n }\n\n const errorHandler = (error: unknown) => {\n cleanup()\n spinner.fail(error instanceof Error ? error.message : \"Error\")\n reject(error instanceof Error ? error : new Error(String(error)))\n }\n\n emitter.once(eventName, successHandler)\n\n if (options.errorEvent) {\n emitter.once(options.errorEvent, errorHandler)\n }\n\n if (options.timeout) {\n timer = setTimeout(() => {\n cleanup()\n spinner.fail(\"Timeout\")\n reject(new Error(`Timeout waiting for ${eventName}`))\n }, options.timeout)\n }\n })\n}\n",
14
+ "/**\n * withSelect - Interactive CLI selection list\n */\n\nimport chalk from \"chalk\"\nimport type { SelectOption, WithSelectOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, cursorUp, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Display an interactive selection list in the terminal\n *\n * @example\n * ```ts\n * // Simple usage\n * const color = await withSelect(\"Choose a color:\", [\n * { label: \"Red\", value: \"red\" },\n * { label: \"Green\", value: \"green\" },\n * { label: \"Blue\", value: \"blue\" },\n * ]);\n *\n * // With options\n * const result = await withSelect(\n * \"Select item:\",\n * options,\n * { initial: 2, maxVisible: 5 }\n * );\n * ```\n */\nexport async function withSelect<T>(\n prompt: string,\n options: SelectOption<T>[],\n selectOptions: WithSelectOptions = {},\n): Promise<T> {\n const { initial = 0, maxVisible = 10 } = selectOptions\n const stream = process.stdout\n const stdin = process.stdin\n\n if (!isTTY(stream) || !stdin.isTTY) {\n // Non-interactive mode: return first option or initial\n return options[initial]?.value ?? options[0]!.value\n }\n\n return new Promise((resolve, reject) => {\n let highlightIndex = Math.min(Math.max(0, initial), options.length - 1)\n let linesRendered = 0\n\n // Enable raw mode for character-by-character input\n stdin.setRawMode(true)\n stdin.resume()\n stdin.setEncoding(\"utf8\")\n\n // Hide cursor\n write(CURSOR_HIDE, stream)\n\n function render() {\n // Clear previously rendered lines\n if (linesRendered > 0) {\n write(cursorUp(linesRendered), stream)\n }\n\n // Calculate scroll window\n const scrollOffset = Math.max(\n 0,\n Math.min(highlightIndex - Math.floor(maxVisible / 2), options.length - maxVisible),\n )\n const visibleCount = Math.min(maxVisible, options.length)\n const visibleOptions = options.slice(scrollOffset, scrollOffset + visibleCount)\n const hasMoreAbove = scrollOffset > 0\n const hasMoreBelow = scrollOffset + visibleCount < options.length\n\n // Render prompt\n write(`${CURSOR_TO_START}${chalk.bold(prompt)}${CLEAR_LINE_END}\\n`, stream)\n\n let lines = 1\n\n // Render scroll indicator (above)\n if (hasMoreAbove) {\n write(`${CURSOR_TO_START} ${chalk.dim(\"...\")}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n // Render options\n for (let i = 0; i < visibleOptions.length; i++) {\n const option = visibleOptions[i]\n const actualIndex = scrollOffset + i\n const isHighlighted = actualIndex === highlightIndex\n\n const indicator = isHighlighted ? chalk.cyan(\">\") : \" \"\n const label = isHighlighted ? chalk.cyan(option!.label) : option!.label\n\n write(`${CURSOR_TO_START}${indicator} ${label}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n // Render scroll indicator (below)\n if (hasMoreBelow) {\n write(`${CURSOR_TO_START} ${chalk.dim(\"...\")}${CLEAR_LINE_END}\\n`, stream)\n lines++\n }\n\n linesRendered = lines\n }\n\n function cleanup() {\n stdin.setRawMode(false)\n stdin.pause()\n stdin.removeListener(\"data\", onKeypress)\n write(CURSOR_SHOW, stream)\n }\n\n function onKeypress(key: string) {\n // Handle key sequences\n const keyCode = key.charCodeAt(0)\n\n // Ctrl+C\n if (key === \"\\x03\") {\n cleanup()\n reject(new Error(\"User cancelled\"))\n return\n }\n\n // Enter/Return\n if (key === \"\\r\" || key === \"\\n\") {\n cleanup()\n resolve(options[highlightIndex]!.value)\n return\n }\n\n // Escape\n if (key === \"\\x1b\" && key.length === 1) {\n cleanup()\n reject(new Error(\"User cancelled\"))\n return\n }\n\n // Arrow keys (escape sequences)\n if (key.startsWith(\"\\x1b[\")) {\n const code = key.slice(2)\n if (code === \"A\") {\n // Up arrow\n highlightIndex = Math.max(0, highlightIndex - 1)\n render()\n } else if (code === \"B\") {\n // Down arrow\n highlightIndex = Math.min(options.length - 1, highlightIndex + 1)\n render()\n }\n return\n }\n\n // j/k vim keys\n if (key === \"j\" || key === \"J\") {\n highlightIndex = Math.min(options.length - 1, highlightIndex + 1)\n render()\n return\n }\n if (key === \"k\" || key === \"K\") {\n highlightIndex = Math.max(0, highlightIndex - 1)\n render()\n return\n }\n\n // Space to select (optional alternative to Enter)\n if (key === \" \") {\n cleanup()\n resolve(options[highlightIndex]!.value)\n return\n }\n }\n\n stdin.on(\"data\", onKeypress)\n render()\n })\n}\n\n/**\n * Create a reusable select instance for multiple selections\n *\n * @example\n * ```ts\n * const select = createSelect({\n * maxVisible: 5,\n * });\n *\n * const first = await select(\"Choose first:\", options1);\n * const second = await select(\"Choose second:\", options2);\n * ```\n */\nexport function createSelect(\n defaultOptions: WithSelectOptions = {},\n): <T>(prompt: string, options: SelectOption<T>[], overrides?: WithSelectOptions) => Promise<T> {\n return <T>(prompt: string, options: SelectOption<T>[], overrides: WithSelectOptions = {}) =>\n withSelect(prompt, options, { ...defaultOptions, ...overrides })\n}\n",
15
+ "/**\n * withTextInput - CLI wrapper for text input prompts\n */\n\nimport chalk from \"chalk\"\nimport type { TextInputOptions } from \"../types.js\"\nimport { CURSOR_HIDE, CURSOR_SHOW, CURSOR_TO_START, CLEAR_LINE_END, write, isTTY } from \"../cli/ansi\"\n\n/**\n * Prompt for text input in the terminal\n *\n * @example\n * ```ts\n * // Simple usage\n * const name = await withTextInput(\"What is your name?\");\n *\n * // With options\n * const password = await withTextInput(\"Password:\", { mask: \"*\" });\n *\n * // With validation\n * const email = await withTextInput(\"Email:\", {\n * validate: (v) => v.includes(\"@\") ? undefined : \"Invalid email\"\n * });\n *\n * // With autocomplete\n * const fruit = await withTextInput(\"Pick a fruit:\", {\n * autocomplete: [\"apple\", \"banana\", \"cherry\"]\n * });\n * ```\n */\nexport async function withTextInput(prompt: string, options: TextInputOptions = {}): Promise<string> {\n const stream = options.stream ?? process.stdout\n const inputStream = options.inputStream ?? process.stdin\n const isTty = isTTY(stream)\n\n // Initialize state\n let value = options.defaultValue ?? \"\"\n let cursorPosition = value.length\n let errorMessage: string | undefined\n\n // Setup raw mode for character-by-character input\n if (inputStream.isTTY) {\n inputStream.setRawMode(true)\n }\n inputStream.resume()\n\n // Render the current state\n const render = () => {\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n const suggestionSuffix = suggestion ? chalk.dim(suggestion.slice(value.length)) : \"\"\n\n // Build cursor display\n const beforeCursor = displayValue.slice(0, cursorPosition)\n const cursorChar = displayValue[cursorPosition] ?? \" \"\n const afterCursor = displayValue.slice(cursorPosition + 1)\n\n // Placeholder when empty\n const showPlaceholder = !value && options.placeholder\n const inputDisplay = showPlaceholder\n ? chalk.dim(options.placeholder) + chalk.inverse(\" \")\n : beforeCursor + chalk.inverse(cursorChar) + afterCursor + suggestionSuffix\n\n // Error message\n const errorDisplay = errorMessage ? chalk.red(` (${errorMessage})`) : \"\"\n\n const line = `${chalk.cyan(\"?\")} ${chalk.bold(prompt)} ${inputDisplay}${errorDisplay}`\n\n if (isTty) {\n write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`, stream)\n }\n }\n\n // Hide cursor during input (we show our own)\n if (isTty) {\n write(CURSOR_HIDE, stream)\n }\n\n render()\n\n return new Promise<string>((resolve, reject) => {\n const cleanup = () => {\n inputStream.removeListener(\"data\", onData)\n inputStream.removeListener(\"error\", onError)\n if (inputStream.isTTY) {\n inputStream.setRawMode(false)\n }\n inputStream.pause()\n if (isTty) {\n write(CURSOR_SHOW, stream)\n }\n }\n\n const submit = () => {\n // Validate before accepting\n if (options.validate) {\n const error = options.validate(value)\n if (error) {\n errorMessage = error\n render()\n return\n }\n }\n\n cleanup()\n\n // Show final value\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n write(\n `${CURSOR_TO_START}${chalk.green(\"✔\")} ${chalk.bold(prompt)} ${chalk.dim(displayValue)}${CLEAR_LINE_END}\\n`,\n stream,\n )\n\n resolve(value)\n }\n\n const onError = (err: Error) => {\n cleanup()\n reject(err)\n }\n\n const onData = (data: Buffer) => {\n const input = data.toString()\n errorMessage = undefined // Clear error on any input\n\n // Handle special keys\n for (let i = 0; i < input.length; i++) {\n const char = input[i]!\n const code = char.charCodeAt(0)\n\n // Enter (CR or LF)\n if (code === 13 || code === 10) {\n submit()\n return\n }\n\n // Ctrl+C - abort\n if (code === 3) {\n cleanup()\n write(\"\\n\", stream)\n reject(new Error(\"User aborted\"))\n return\n }\n\n // Escape - clear or abort\n if (code === 27) {\n // Check for arrow key sequences\n if (input[i + 1] === \"[\") {\n const arrowCode = input[i + 2]\n if (arrowCode === \"D\") {\n // Left arrow\n cursorPosition = Math.max(0, cursorPosition - 1)\n i += 2\n continue\n }\n if (arrowCode === \"C\") {\n // Right arrow\n cursorPosition = Math.min(value.length, cursorPosition + 1)\n i += 2\n continue\n }\n if (arrowCode === \"H\") {\n // Home\n cursorPosition = 0\n i += 2\n continue\n }\n if (arrowCode === \"F\") {\n // End\n cursorPosition = value.length\n i += 2\n continue\n }\n // Skip other escape sequences\n i += 2\n continue\n }\n // Plain escape - clear input\n value = \"\"\n cursorPosition = 0\n continue\n }\n\n // Backspace (127 or 8)\n if (code === 127 || code === 8) {\n if (cursorPosition > 0) {\n value = value.slice(0, cursorPosition - 1) + value.slice(cursorPosition)\n cursorPosition--\n }\n continue\n }\n\n // Delete (escape sequence handled above)\n if (code === 4) {\n // Ctrl+D acts as delete\n if (cursorPosition < value.length) {\n value = value.slice(0, cursorPosition) + value.slice(cursorPosition + 1)\n }\n continue\n }\n\n // Tab - accept autocomplete suggestion\n if (code === 9) {\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n if (suggestion) {\n value = suggestion\n cursorPosition = value.length\n }\n continue\n }\n\n // Ctrl+A - beginning of line\n if (code === 1) {\n cursorPosition = 0\n continue\n }\n\n // Ctrl+E - end of line\n if (code === 5) {\n cursorPosition = value.length\n continue\n }\n\n // Ctrl+U - clear to beginning\n if (code === 21) {\n value = value.slice(cursorPosition)\n cursorPosition = 0\n continue\n }\n\n // Ctrl+K - clear to end\n if (code === 11) {\n value = value.slice(0, cursorPosition)\n continue\n }\n\n // Ctrl+W - delete word backward\n if (code === 23) {\n const before = value.slice(0, cursorPosition)\n const after = value.slice(cursorPosition)\n const trimmed = before.trimEnd()\n const lastSpace = trimmed.lastIndexOf(\" \")\n const newBefore = lastSpace === -1 ? \"\" : trimmed.slice(0, lastSpace + 1)\n value = newBefore + after\n cursorPosition = newBefore.length\n continue\n }\n\n // Regular printable character\n if (code >= 32 && code < 127) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition++\n continue\n }\n\n // Handle UTF-8 characters (multi-byte)\n if (code > 127) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition++\n continue\n }\n }\n\n render()\n }\n\n inputStream.on(\"data\", onData)\n inputStream.on(\"error\", onError)\n })\n}\n\n/**\n * Create a text input instance for manual control\n *\n * @example\n * ```ts\n * const input = createTextInput(\"Name:\", { placeholder: \"Enter name\" });\n * input.render();\n *\n * // Later, get the value\n * const value = await input.waitForSubmit();\n * ```\n */\nexport function createTextInput(prompt: string, options: TextInputOptions = {}): TextInputInstance {\n const stream = options.stream ?? process.stdout\n const isTty = isTTY(stream)\n\n let value = options.defaultValue ?? \"\"\n let cursorPosition = value.length\n\n const render = () => {\n const displayValue = options.mask ? options.mask.repeat(value.length) : value\n\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n const suggestionSuffix = suggestion ? chalk.dim(suggestion.slice(value.length)) : \"\"\n\n const beforeCursor = displayValue.slice(0, cursorPosition)\n const cursorChar = displayValue[cursorPosition] ?? \" \"\n const afterCursor = displayValue.slice(cursorPosition + 1)\n\n const showPlaceholder = !value && options.placeholder\n const inputDisplay = showPlaceholder\n ? chalk.dim(options.placeholder) + chalk.inverse(\" \")\n : beforeCursor + chalk.inverse(cursorChar) + afterCursor + suggestionSuffix\n\n const line = `${chalk.cyan(\"?\")} ${chalk.bold(prompt)} ${inputDisplay}`\n\n if (isTty) {\n write(`${CURSOR_TO_START}${line}${CLEAR_LINE_END}`, stream)\n }\n }\n\n return {\n get value() {\n return value\n },\n set value(v: string) {\n value = v\n cursorPosition = Math.min(cursorPosition, v.length)\n },\n get cursorPosition() {\n return cursorPosition\n },\n set cursorPosition(pos: number) {\n cursorPosition = Math.max(0, Math.min(value.length, pos))\n },\n render,\n insert(char: string) {\n value = value.slice(0, cursorPosition) + char + value.slice(cursorPosition)\n cursorPosition += char.length\n },\n backspace() {\n if (cursorPosition > 0) {\n value = value.slice(0, cursorPosition - 1) + value.slice(cursorPosition)\n cursorPosition--\n }\n },\n delete() {\n if (cursorPosition < value.length) {\n value = value.slice(0, cursorPosition) + value.slice(cursorPosition + 1)\n }\n },\n clear() {\n value = \"\"\n cursorPosition = 0\n },\n acceptSuggestion() {\n const suggestion = getAutocompleteSuggestion(value, options.autocomplete)\n if (suggestion) {\n value = suggestion\n cursorPosition = value.length\n }\n },\n }\n}\n\n/** Instance returned by createTextInput for manual control */\nexport interface TextInputInstance {\n /** Current input value */\n value: string\n /** Current cursor position */\n cursorPosition: number\n /** Render the current state */\n render(): void\n /** Insert text at cursor */\n insert(char: string): void\n /** Delete character before cursor */\n backspace(): void\n /** Delete character at cursor */\n delete(): void\n /** Clear all input */\n clear(): void\n /** Accept autocomplete suggestion */\n acceptSuggestion(): void\n}\n\n/**\n * Find a matching autocomplete suggestion for the current input\n */\nfunction getAutocompleteSuggestion(value: string, autocomplete?: string[]): string | undefined {\n if (!value || !autocomplete?.length) {\n return undefined\n }\n\n const lowerValue = value.toLowerCase()\n return autocomplete.find((item) => item.toLowerCase().startsWith(lowerValue) && item.length > value.length)\n}\n"
16
+ ],
17
+ "mappings": "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",
18
+ "debugId": "9D8095BD530610BD64756E2164756E21",
19
+ "names": []
20
+ }