@astralyn/pi 0.84.14 → 0.84.16

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 (185) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/cli.js +2435 -18
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +12 -6
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/types.d.ts +4 -1
  7. package/dist/core/extensions/types.d.ts.map +1 -1
  8. package/dist/core/extensions/types.js.map +1 -1
  9. package/dist/core/tools/bash.d.ts +1 -0
  10. package/dist/core/tools/bash.d.ts.map +1 -1
  11. package/dist/core/tools/bash.js +4 -1
  12. package/dist/core/tools/bash.js.map +1 -1
  13. package/dist/core/tools/find.d.ts +2 -0
  14. package/dist/core/tools/find.d.ts.map +1 -1
  15. package/dist/core/tools/find.js +18 -15
  16. package/dist/core/tools/find.js.map +1 -1
  17. package/dist/extensions/background/actions.d.ts.map +1 -1
  18. package/dist/extensions/background/actions.js +3 -2
  19. package/dist/extensions/background/actions.js.map +1 -1
  20. package/dist/extensions/background/index.d.ts +5 -4
  21. package/dist/extensions/background/index.d.ts.map +1 -1
  22. package/dist/extensions/background/index.js +9 -5
  23. package/dist/extensions/background/index.js.map +1 -1
  24. package/dist/extensions/background/manager.d.ts.map +1 -1
  25. package/dist/extensions/background/manager.js +8 -9
  26. package/dist/extensions/background/manager.js.map +1 -1
  27. package/dist/extensions/background/registry.d.ts +4 -1
  28. package/dist/extensions/background/registry.d.ts.map +1 -1
  29. package/dist/extensions/background/registry.js +30 -17
  30. package/dist/extensions/background/registry.js.map +1 -1
  31. package/dist/extensions/background/task-view.d.ts +6 -0
  32. package/dist/extensions/background/task-view.d.ts.map +1 -1
  33. package/dist/extensions/background/task-view.js +11 -6
  34. package/dist/extensions/background/task-view.js.map +1 -1
  35. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  36. package/dist/extensions/deepwiki/client.js +3 -0
  37. package/dist/extensions/deepwiki/client.js.map +1 -1
  38. package/dist/extensions/deepwiki/execute.d.ts +2 -1
  39. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  40. package/dist/extensions/deepwiki/execute.js +17 -2
  41. package/dist/extensions/deepwiki/execute.js.map +1 -1
  42. package/dist/extensions/deepwiki/http.d.ts +1 -0
  43. package/dist/extensions/deepwiki/http.d.ts.map +1 -1
  44. package/dist/extensions/deepwiki/http.js +16 -2
  45. package/dist/extensions/deepwiki/http.js.map +1 -1
  46. package/dist/extensions/deepwiki/schema.d.ts +1 -1
  47. package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
  48. package/dist/extensions/deepwiki/schema.js +8 -2
  49. package/dist/extensions/deepwiki/schema.js.map +1 -1
  50. package/dist/extensions/question/dialog.d.ts.map +1 -1
  51. package/dist/extensions/question/dialog.js +80 -9
  52. package/dist/extensions/question/dialog.js.map +1 -1
  53. package/dist/extensions/question/index.d.ts.map +1 -1
  54. package/dist/extensions/question/index.js +8 -0
  55. package/dist/extensions/question/index.js.map +1 -1
  56. package/dist/extensions/question/schema.d.ts +2 -2
  57. package/dist/extensions/question/schema.d.ts.map +1 -1
  58. package/dist/extensions/question/schema.js +41 -11
  59. package/dist/extensions/question/schema.js.map +1 -1
  60. package/dist/extensions/question/types.d.ts +1 -1
  61. package/dist/extensions/question/types.d.ts.map +1 -1
  62. package/dist/extensions/question/types.js.map +1 -1
  63. package/dist/extensions/router/probe.d.ts.map +1 -1
  64. package/dist/extensions/router/probe.js +10 -29
  65. package/dist/extensions/router/probe.js.map +1 -1
  66. package/dist/extensions/router/register.d.ts.map +1 -1
  67. package/dist/extensions/router/register.js +16 -6
  68. package/dist/extensions/router/register.js.map +1 -1
  69. package/dist/extensions/router/store.d.ts.map +1 -1
  70. package/dist/extensions/router/store.js +108 -51
  71. package/dist/extensions/router/store.js.map +1 -1
  72. package/dist/extensions/router/ui.d.ts +6 -0
  73. package/dist/extensions/router/ui.d.ts.map +1 -1
  74. package/dist/extensions/router/ui.js +14 -6
  75. package/dist/extensions/router/ui.js.map +1 -1
  76. package/dist/extensions/statusline/index.d.ts.map +1 -1
  77. package/dist/extensions/statusline/index.js +40 -43
  78. package/dist/extensions/statusline/index.js.map +1 -1
  79. package/dist/extensions/subagent/activity.d.ts +7 -1
  80. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  81. package/dist/extensions/subagent/activity.js +69 -6
  82. package/dist/extensions/subagent/activity.js.map +1 -1
  83. package/dist/extensions/subagent/agents-command.js +1 -1
  84. package/dist/extensions/subagent/agents-command.js.map +1 -1
  85. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  86. package/dist/extensions/subagent/agents.js +0 -3
  87. package/dist/extensions/subagent/agents.js.map +1 -1
  88. package/dist/extensions/subagent/budget.d.ts.map +1 -1
  89. package/dist/extensions/subagent/budget.js +29 -2
  90. package/dist/extensions/subagent/budget.js.map +1 -1
  91. package/dist/extensions/subagent/cancellation.d.ts +4 -4
  92. package/dist/extensions/subagent/cancellation.d.ts.map +1 -1
  93. package/dist/extensions/subagent/cancellation.js +31 -13
  94. package/dist/extensions/subagent/cancellation.js.map +1 -1
  95. package/dist/extensions/subagent/constants.d.ts +1 -0
  96. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  97. package/dist/extensions/subagent/constants.js +1 -0
  98. package/dist/extensions/subagent/constants.js.map +1 -1
  99. package/dist/extensions/subagent/index.d.ts.map +1 -1
  100. package/dist/extensions/subagent/index.js +1 -0
  101. package/dist/extensions/subagent/index.js.map +1 -1
  102. package/dist/extensions/subagent/render.d.ts +8 -1
  103. package/dist/extensions/subagent/render.d.ts.map +1 -1
  104. package/dist/extensions/subagent/render.js +155 -178
  105. package/dist/extensions/subagent/render.js.map +1 -1
  106. package/dist/extensions/subagent/runner.d.ts +1 -0
  107. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  108. package/dist/extensions/subagent/runner.js +53 -44
  109. package/dist/extensions/subagent/runner.js.map +1 -1
  110. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  111. package/dist/extensions/subagent/sdk-runner.js +0 -3
  112. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  113. package/dist/extensions/subagent/settings.d.ts +1 -1
  114. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  115. package/dist/extensions/subagent/settings.js +74 -25
  116. package/dist/extensions/subagent/settings.js.map +1 -1
  117. package/dist/extensions/subagent/state.d.ts +1 -0
  118. package/dist/extensions/subagent/state.d.ts.map +1 -1
  119. package/dist/extensions/subagent/state.js +46 -16
  120. package/dist/extensions/subagent/state.js.map +1 -1
  121. package/dist/extensions/subagent/types.d.ts +0 -7
  122. package/dist/extensions/subagent/types.d.ts.map +1 -1
  123. package/dist/extensions/subagent/types.js.map +1 -1
  124. package/dist/extensions/subagent/ui/agents-dialog.js +1 -1
  125. package/dist/extensions/subagent/ui/agents-dialog.js.map +1 -1
  126. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  127. package/dist/extensions/subagent/ui/model-list.js +3 -4
  128. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  129. package/dist/extensions/subagent/ui/model-picker.d.ts.map +1 -1
  130. package/dist/extensions/subagent/ui/model-picker.js +2 -1
  131. package/dist/extensions/subagent/ui/model-picker.js.map +1 -1
  132. package/dist/extensions/web-search/format.d.ts.map +1 -1
  133. package/dist/extensions/web-search/format.js +36 -15
  134. package/dist/extensions/web-search/format.js.map +1 -1
  135. package/dist/extensions/web-search/index.d.ts.map +1 -1
  136. package/dist/extensions/web-search/index.js +11 -8
  137. package/dist/extensions/web-search/index.js.map +1 -1
  138. package/dist/extensions/web-search/providers/deepseek.d.ts.map +1 -1
  139. package/dist/extensions/web-search/providers/deepseek.js +36 -5
  140. package/dist/extensions/web-search/providers/deepseek.js.map +1 -1
  141. package/dist/extensions/web-search/providers/http.d.ts +2 -7
  142. package/dist/extensions/web-search/providers/http.d.ts.map +1 -1
  143. package/dist/extensions/web-search/providers/http.js +12 -57
  144. package/dist/extensions/web-search/providers/http.js.map +1 -1
  145. package/dist/extensions/web-search/providers/minimax.d.ts.map +1 -1
  146. package/dist/extensions/web-search/providers/minimax.js +18 -6
  147. package/dist/extensions/web-search/providers/minimax.js.map +1 -1
  148. package/dist/extensions/web-search/render.d.ts.map +1 -1
  149. package/dist/extensions/web-search/render.js +16 -5
  150. package/dist/extensions/web-search/render.js.map +1 -1
  151. package/dist/extensions/web-search/results.d.ts +10 -3
  152. package/dist/extensions/web-search/results.d.ts.map +1 -1
  153. package/dist/extensions/web-search/results.js +25 -7
  154. package/dist/extensions/web-search/results.js.map +1 -1
  155. package/dist/image-resize-worker.js +2 -0
  156. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  157. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/tool-execution.js +7 -0
  159. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  160. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  161. package/dist/modes/interactive/components/tool-group.js +10 -0
  162. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  163. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/dist/modes/interactive/interactive-mode.js +3 -40
  165. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/dist/modes/interactive/theme/theme.d.ts +0 -4
  167. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  168. package/dist/modes/interactive/theme/theme.js +16 -23
  169. package/dist/modes/interactive/theme/theme.js.map +1 -1
  170. package/dist/rpc-entry.js +2435 -10
  171. package/dist/utils/http-response.d.ts +10 -0
  172. package/dist/utils/http-response.d.ts.map +1 -0
  173. package/dist/utils/http-response.js +68 -0
  174. package/dist/utils/http-response.js.map +1 -0
  175. package/docs/bundled/extensions/background.md +4 -3
  176. package/docs/bundled/extensions/deepwiki.md +7 -5
  177. package/docs/bundled/extensions/question.md +6 -4
  178. package/docs/bundled/extensions/router.md +1 -1
  179. package/docs/bundled/extensions/statusline.md +1 -0
  180. package/docs/bundled/extensions/subagent.md +29 -39
  181. package/docs/bundled/extensions/web-search.md +3 -3
  182. package/docs/development.md +8 -3
  183. package/docs/extensions.md +2 -2
  184. package/npm-shrinkwrap.json +110 -109
  185. package/package.json +4 -2
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/extensions/deepwiki/http.ts"],"names":[],"mappings":"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","sourcesContent":["export interface FetchRetryOptions {\n\ttimeoutMs: number;\n\tretries?: number;\n\tsignal?: AbortSignal;\n\tlabel?: string;\n}\n\nfunction delay(milliseconds: number, label: string, signal: AbortSignal | undefined): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst handleAbort = () => {\n\t\t\tcleanup();\n\t\t\treject(new Error(`${label} request aborted`));\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t}, milliseconds);\n\t\tconst cleanup = () => {\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", handleAbort);\n\t\t};\n\n\t\tif (signal?.aborted) {\n\t\t\thandleAbort();\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", handleAbort, { once: true });\n\t});\n}\n\nasync function attemptOnce(\n\turl: string,\n\trequest: RequestInit,\n\ttimeoutMs: number,\n\tlabel: string,\n\tsignal: AbortSignal | undefined,\n): Promise<{ ok: true; response: Response; text: string } | { ok: false; error: Error }> {\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst abortFromParent = () => controller.abort(signal?.reason);\n\tconst timeout = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\n\tif (signal?.aborted) abortFromParent();\n\telse signal?.addEventListener(\"abort\", abortFromParent, { once: true });\n\n\ttry {\n\t\tconst response = await fetch(url, { ...request, signal: controller.signal });\n\t\tconst text = await response.text();\n\t\treturn { ok: true, response, text };\n\t} catch (error) {\n\t\tif (timedOut) throw new Error(`${label} request timed out after ${timeoutMs / 1000}s`);\n\t\tif (signal?.aborted) throw new Error(`${label} request aborted`);\n\t\treturn { ok: false, error: error instanceof Error ? error : new Error(String(error)) };\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal?.removeEventListener(\"abort\", abortFromParent);\n\t}\n}\n\n/** Fetch with a per-attempt timeout and bounded retries for transient failures. */\nexport async function fetchWithRetry(\n\turl: string,\n\trequest: RequestInit,\n\toptions: FetchRetryOptions,\n): Promise<{ response: Response; text: string }> {\n\tconst retries = options.retries ?? 2;\n\tconst label = options.label ?? \"HTTP\";\n\tlet lastError: Error | undefined;\n\n\tfor (let attemptIndex = 0; attemptIndex <= retries; attemptIndex++) {\n\t\tif (attemptIndex > 0) await delay(1000 * attemptIndex, label, options.signal);\n\t\tconst outcome = await attemptOnce(url, request, options.timeoutMs, label, options.signal);\n\t\tif (!outcome.ok) {\n\t\t\tlastError = outcome.error;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { response, text } = outcome;\n\t\tconst retryable = response.status >= 500 || response.status === 429;\n\t\tif (!response.ok && retryable && attemptIndex < retries) {\n\t\t\tlastError = new Error(`${label} HTTP ${response.status}`);\n\t\t\tcontinue;\n\t\t}\n\t\treturn { response, text };\n\t}\n\n\tthrow lastError ?? new Error(`${label} request failed`);\n}\n"]}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/extensions/deepwiki/http.ts"],"names":[],"mappings":"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","sourcesContent":["import { readResponseTextBounded } from \"../../utils/http-response.ts\";\n\nexport const DEEPWIKI_RESPONSE_BODY_BYTES = 8 * 1024 * 1024;\nconst DEEPWIKI_RESPONSE_OVERFLOW_MESSAGE = `DeepWiki response exceeded ${DEEPWIKI_RESPONSE_BODY_BYTES} bytes`;\n\nexport interface FetchRetryOptions {\n\ttimeoutMs: number;\n\tretries?: number;\n\tsignal?: AbortSignal;\n\tlabel?: string;\n}\n\nfunction delay(milliseconds: number, label: string, signal: AbortSignal | undefined): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst handleAbort = () => {\n\t\t\tcleanup();\n\t\t\treject(new Error(`${label} request aborted`));\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tcleanup();\n\t\t\tresolve();\n\t\t}, milliseconds);\n\t\tconst cleanup = () => {\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", handleAbort);\n\t\t};\n\n\t\tif (signal?.aborted) {\n\t\t\thandleAbort();\n\t\t\treturn;\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", handleAbort, { once: true });\n\t});\n}\n\nasync function attemptOnce(\n\turl: string,\n\trequest: RequestInit,\n\ttimeoutMs: number,\n\tlabel: string,\n\tsignal: AbortSignal | undefined,\n): Promise<{ ok: true; response: Response; text: string } | { ok: false; error: Error; retryable: boolean }> {\n\tconst controller = new AbortController();\n\tlet timedOut = false;\n\tconst abortFromParent = () => controller.abort(signal?.reason);\n\tconst timeout = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort();\n\t}, timeoutMs);\n\n\tif (signal?.aborted) abortFromParent();\n\telse signal?.addEventListener(\"abort\", abortFromParent, { once: true });\n\n\ttry {\n\t\tconst response = await fetch(url, { ...request, signal: controller.signal });\n\t\tconst text = await readResponseTextBounded(response, {\n\t\t\tmaxBytes: DEEPWIKI_RESPONSE_BODY_BYTES,\n\t\t\toverflowMessage: DEEPWIKI_RESPONSE_OVERFLOW_MESSAGE,\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\treturn { ok: true, response, text };\n\t} catch (error) {\n\t\tif (timedOut) throw new Error(`${label} request timed out after ${timeoutMs / 1000}s`);\n\t\tif (signal?.aborted) throw new Error(`${label} request aborted`);\n\t\tconst normalizedError = error instanceof Error ? error : new Error(String(error));\n\t\treturn {\n\t\t\tok: false,\n\t\t\terror: normalizedError,\n\t\t\tretryable: normalizedError.message !== DEEPWIKI_RESPONSE_OVERFLOW_MESSAGE,\n\t\t};\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal?.removeEventListener(\"abort\", abortFromParent);\n\t}\n}\n\n/** Fetch with a per-attempt timeout and bounded retries for transient failures. */\nexport async function fetchWithRetry(\n\turl: string,\n\trequest: RequestInit,\n\toptions: FetchRetryOptions,\n): Promise<{ response: Response; text: string }> {\n\tconst retries = options.retries ?? 2;\n\tconst label = options.label ?? \"HTTP\";\n\tlet lastError: Error | undefined;\n\n\tfor (let attemptIndex = 0; attemptIndex <= retries; attemptIndex++) {\n\t\tif (attemptIndex > 0) await delay(1000 * attemptIndex, label, options.signal);\n\t\tconst outcome = await attemptOnce(url, request, options.timeoutMs, label, options.signal);\n\t\tif (!outcome.ok) {\n\t\t\tlastError = outcome.error;\n\t\t\tif (!outcome.retryable) throw outcome.error;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst { response, text } = outcome;\n\t\tconst retryable = response.status >= 500 || response.status === 429;\n\t\tif (!response.ok && retryable && attemptIndex < retries) {\n\t\t\tlastError = new Error(`${label} HTTP ${response.status}`);\n\t\t\tcontinue;\n\t\t}\n\t\treturn { response, text };\n\t}\n\n\tthrow lastError ?? new Error(`${label} request failed`);\n}\n"]}
@@ -4,7 +4,7 @@ export declare const DeepWikiParamsSchema: Type.TObject<{
4
4
  action: Type.TUnsafe<"structure" | "contents" | "question">;
5
5
  repoName: Type.TUnion<[Type.TString, Type.TArray<Type.TString>]>;
6
6
  question: Type.TOptional<Type.TString>;
7
- page: Type.TOptional<Type.TUnion<[Type.TString, Type.TNumber]>>;
7
+ page: Type.TOptional<Type.TUnion<[Type.TString, Type.TInteger]>>;
8
8
  }>;
9
9
  export type DeepWikiAction = (typeof DEEPWIKI_ACTIONS)[number];
10
10
  export type DeepWikiParams = Static<typeof DeepWikiParamsSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/deepwiki/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,eAAO,MAAM,gBAAgB,gDAAiD,CAAC;AAQ/E,eAAO,MAAM,oBAAoB;;;;;EAkC/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC/D,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAgFjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAkCxD;AA4BD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAsCtE"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/deepwiki/schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,eAAO,MAAM,gBAAgB,gDAAiD,CAAC;AAQ/E,eAAO,MAAM,oBAAoB;;;;;EAkC/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC/D,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAqFjE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAkCxD;AA4BD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAsCtE"}
@@ -26,7 +26,7 @@ export const DeepWikiParamsSchema = Type.Object({
26
26
  minLength: 1,
27
27
  description: "Required for action question. Ask a focused question about architecture, APIs, implementation patterns, extension points, onboarding, or cross-repo comparison.",
28
28
  })),
29
- page: Type.Optional(Type.Union([Type.String({ minLength: 1 }), Type.Number()], {
29
+ page: Type.Optional(Type.Union([Type.String({ minLength: 1 }), Type.Integer({ minimum: 1 })], {
30
30
  description: 'Only for action contents: one page by 1-based index or by title from structure (e.g. "Extension System") — titles do not include outline numbers like "4.4". Run structure first if unsure. Prefer page reads over omitting page.',
31
31
  })),
32
32
  });
@@ -59,10 +59,16 @@ function readString(record, keys) {
59
59
  function readPageRef(record) {
60
60
  for (const key of ["page", "pageName", "pageTitle"]) {
61
61
  const value = record[key];
62
- if (typeof value === "number" && Number.isFinite(value))
62
+ if (value === undefined || value === null)
63
+ continue;
64
+ if (typeof value === "number") {
65
+ if (!Number.isInteger(value) || value < 1)
66
+ throw new Error("page must be a positive 1-based integer");
63
67
  return value;
68
+ }
64
69
  if (typeof value === "string" && value.trim())
65
70
  return normalizePageRef(value.trim());
71
+ throw new Error("page must be a non-empty title or positive 1-based integer");
66
72
  }
67
73
  return undefined;
68
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/deepwiki/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\n\nimport { normalizePageRef } from \"./contents.ts\";\n\nexport const DEEPWIKI_ACTIONS = [\"structure\", \"contents\", \"question\"] as const;\nconst MAX_QUESTION_REPOS = 10;\n\nconst DeepWikiActionSchema = StringEnum(DEEPWIKI_ACTIONS, {\n\tdescription:\n\t\t\"DeepWiki operation: structure lists a repo's generated wiki pages, contents reads one page of that wiki (or all of it when page is omitted), and question answers focused repo/reference/comparison questions across up to 10 repos.\",\n});\n\nexport const DeepWikiParamsSchema = Type.Object({\n\taction: DeepWikiActionSchema,\n\trepoName: Type.Union([\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t'One public GitHub repo as \"owner/repo\" (preferred), or a GitHub/DeepWiki URL. Required for structure and contents; for question with a single repo use this string form — not a one-element array.',\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tdescription: 'Public GitHub repo in \"owner/repo\" format.',\n\t\t\t}),\n\t\t\t{\n\t\t\t\tminItems: 1,\n\t\t\t\tmaxItems: MAX_QUESTION_REPOS,\n\t\t\t\tdescription:\n\t\t\t\t\t\"Only for action question when comparing 2-10 repos. Each entry is owner/repo. For one repo use a string repoName instead.\",\n\t\t\t},\n\t\t),\n\t]),\n\tquestion: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Required for action question. Ask a focused question about architecture, APIs, implementation patterns, extension points, onboarding, or cross-repo comparison.\",\n\t\t}),\n\t),\n\tpage: Type.Optional(\n\t\tType.Union([Type.String({ minLength: 1 }), Type.Number()], {\n\t\t\tdescription:\n\t\t\t\t'Only for action contents: one page by 1-based index or by title from structure (e.g. \"Extension System\") — titles do not include outline numbers like \"4.4\". Run structure first if unsure. Prefer page reads over omitting page.',\n\t\t}),\n\t),\n});\n\nexport type DeepWikiAction = (typeof DEEPWIKI_ACTIONS)[number];\nexport type DeepWikiParams = Static<typeof DeepWikiParamsSchema>;\n\nconst ACTION_ALIASES: Record<string, DeepWikiAction> = {\n\task: \"question\",\n\task_question: \"question\",\n\tcontents: \"contents\",\n\tcontent: \"contents\",\n\tfull: \"contents\",\n\tmap: \"structure\",\n\tpages: \"structure\",\n\tquestion: \"question\",\n\tquery: \"question\",\n\tread_wiki_contents: \"contents\",\n\tread_wiki_structure: \"structure\",\n\tstructure: \"structure\",\n\ttopics: \"structure\",\n\twiki: \"contents\",\n};\n\nconst GITHUB_OWNER_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38}[A-Za-z0-9])?$/;\nconst GITHUB_REPO_RE = /^[A-Za-z0-9._-]{1,100}$/;\n\nfunction readString(record: Record<string, unknown>, keys: string[]): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) return value.trim();\n\t}\n\treturn undefined;\n}\n\nfunction readPageRef(record: Record<string, unknown>): string | number | undefined {\n\tfor (const key of [\"page\", \"pageName\", \"pageTitle\"]) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\t\tif (typeof value === \"string\" && value.trim()) return normalizePageRef(value.trim());\n\t}\n\treturn undefined;\n}\n\nfunction coerceRepoNameInput(value: unknown): unknown {\n\tif (Array.isArray(value)) return value;\n\tif (typeof value !== \"string\") return value;\n\tconst trimmed = value.trim();\n\tif (!trimmed.startsWith(\"[\")) return value;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(trimmed);\n\t\tif (Array.isArray(parsed)) return parsed;\n\t} catch {\n\t\t/* keep original string */\n\t}\n\treturn value;\n}\n\nfunction normalizeAction(\n\taction: unknown,\n\tquestion: string | undefined,\n\tpage: string | number | undefined,\n): DeepWikiAction {\n\tif (typeof action === \"string\") {\n\t\tconst key = action\n\t\t\t.trim()\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[\\s-]+/g, \"_\");\n\t\tif (!key) return inferAction(question, page);\n\t\tconst normalized = ACTION_ALIASES[key];\n\t\tif (normalized) return normalized;\n\t\tthrow new Error(`unsupported deepwiki action: ${action}`);\n\t}\n\tif (action !== undefined && action !== null) {\n\t\tthrow new Error(\"deepwiki action must be a string\");\n\t}\n\treturn inferAction(question, page);\n}\n\nfunction inferAction(question: string | undefined, page: string | number | undefined): DeepWikiAction {\n\tif (question) return \"question\";\n\tif (page !== undefined) return \"contents\";\n\treturn \"structure\";\n}\n\nexport function normalizeRepoName(value: unknown): string {\n\tif (typeof value !== \"string\") throw new Error(\"repoName is required\");\n\n\tconst input = value.trim().replace(/^\\/+|\\/+$/g, \"\");\n\tif (!input) throw new Error(\"repoName is required\");\n\n\tlet owner: string | undefined;\n\tlet repo: string | undefined;\n\n\tconst sshMatch = input.match(/^git@github\\.com:([^/\\s]+)\\/([^/\\s]+?)(?:\\.git)?$/i);\n\tif (sshMatch) {\n\t\towner = sshMatch[1];\n\t\trepo = sshMatch[2];\n\t} else if (/^(?:https?:\\/\\/)?(?:www\\.)?(?:github\\.com|deepwiki\\.com)\\//i.test(input)) {\n\t\tconst urlInput = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(input) ? input : `https://${input}`;\n\t\ttry {\n\t\t\tconst url = new URL(urlInput);\n\t\t\tconst [urlOwner, urlRepo] = url.pathname.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n\t\t\towner = urlOwner;\n\t\t\trepo = urlRepo?.replace(/\\.git$/i, \"\");\n\t\t} catch {\n\t\t\tthrow new Error('repoName must use \"owner/repo\" format');\n\t\t}\n\t} else {\n\t\tconst parts = input.replace(/\\.git$/i, \"\").split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\t[owner, repo] = parts;\n\t\t}\n\t}\n\n\tif (!owner || !repo || !GITHUB_OWNER_RE.test(owner) || !GITHUB_REPO_RE.test(repo)) {\n\t\tthrow new Error('repoName must use a public GitHub \"owner/repo\" identifier');\n\t}\n\treturn `${owner}/${repo}`;\n}\n\nfunction normalizeRepoNames(value: unknown): string[] {\n\tvalue = coerceRepoNameInput(value);\n\tconst values =\n\t\ttypeof value === \"string\" && value.includes(\",\")\n\t\t\t? value\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((repo) => repo.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: Array.isArray(value)\n\t\t\t\t? value\n\t\t\t\t: [value];\n\tconst repos = values.map((item) => normalizeRepoName(item));\n\tconst seen = new Set<string>();\n\tconst unique: string[] = [];\n\tfor (const repo of repos) {\n\t\tconst key = repo.toLowerCase();\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tunique.push(repo);\n\t}\n\tif (unique.length > MAX_QUESTION_REPOS) {\n\t\tthrow new Error(`question action supports at most ${MAX_QUESTION_REPOS} repositories`);\n\t}\n\treturn unique;\n}\n\nexport function normalizeDeepWikiParams(input: unknown): DeepWikiParams {\n\tif (!input || typeof input !== \"object\") {\n\t\tthrow new Error(\"deepwiki arguments must be an object\");\n\t}\n\n\tconst record = input as Record<string, unknown>;\n\tconst question = readString(record, [\"question\", \"query\", \"prompt\"]);\n\tconst page = readPageRef(record);\n\tconst action = normalizeAction(record.action, question, page);\n\tconst rawRepoName =\n\t\trecord.repoName ??\n\t\trecord.repo ??\n\t\trecord.repos ??\n\t\trecord.repository ??\n\t\trecord.repositories ??\n\t\trecord.githubRepo ??\n\t\trecord.githubRepos;\n\tconst repoNames = normalizeRepoNames(rawRepoName);\n\n\tif (action === \"question\") {\n\t\tif (!question) throw new Error(\"question is required when action is question\");\n\t\treturn {\n\t\t\taction,\n\t\t\trepoName: repoNames.length > 1 ? repoNames : repoNames[0],\n\t\t\tquestion,\n\t\t};\n\t}\n\n\tif (repoNames.length !== 1) {\n\t\tthrow new Error(`${action} action requires exactly one repository`);\n\t}\n\n\t// page is meaningful only for contents; structure/question ignore it.\n\treturn {\n\t\taction,\n\t\trepoName: repoNames[0],\n\t\t...(action === \"contents\" && page !== undefined ? { page } : {}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/deepwiki/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\n\nimport { normalizePageRef } from \"./contents.ts\";\n\nexport const DEEPWIKI_ACTIONS = [\"structure\", \"contents\", \"question\"] as const;\nconst MAX_QUESTION_REPOS = 10;\n\nconst DeepWikiActionSchema = StringEnum(DEEPWIKI_ACTIONS, {\n\tdescription:\n\t\t\"DeepWiki operation: structure lists a repo's generated wiki pages, contents reads one page of that wiki (or all of it when page is omitted), and question answers focused repo/reference/comparison questions across up to 10 repos.\",\n});\n\nexport const DeepWikiParamsSchema = Type.Object({\n\taction: DeepWikiActionSchema,\n\trepoName: Type.Union([\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t'One public GitHub repo as \"owner/repo\" (preferred), or a GitHub/DeepWiki URL. Required for structure and contents; for question with a single repo use this string form — not a one-element array.',\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tdescription: 'Public GitHub repo in \"owner/repo\" format.',\n\t\t\t}),\n\t\t\t{\n\t\t\t\tminItems: 1,\n\t\t\t\tmaxItems: MAX_QUESTION_REPOS,\n\t\t\t\tdescription:\n\t\t\t\t\t\"Only for action question when comparing 2-10 repos. Each entry is owner/repo. For one repo use a string repoName instead.\",\n\t\t\t},\n\t\t),\n\t]),\n\tquestion: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Required for action question. Ask a focused question about architecture, APIs, implementation patterns, extension points, onboarding, or cross-repo comparison.\",\n\t\t}),\n\t),\n\tpage: Type.Optional(\n\t\tType.Union([Type.String({ minLength: 1 }), Type.Integer({ minimum: 1 })], {\n\t\t\tdescription:\n\t\t\t\t'Only for action contents: one page by 1-based index or by title from structure (e.g. \"Extension System\") — titles do not include outline numbers like \"4.4\". Run structure first if unsure. Prefer page reads over omitting page.',\n\t\t}),\n\t),\n});\n\nexport type DeepWikiAction = (typeof DEEPWIKI_ACTIONS)[number];\nexport type DeepWikiParams = Static<typeof DeepWikiParamsSchema>;\n\nconst ACTION_ALIASES: Record<string, DeepWikiAction> = {\n\task: \"question\",\n\task_question: \"question\",\n\tcontents: \"contents\",\n\tcontent: \"contents\",\n\tfull: \"contents\",\n\tmap: \"structure\",\n\tpages: \"structure\",\n\tquestion: \"question\",\n\tquery: \"question\",\n\tread_wiki_contents: \"contents\",\n\tread_wiki_structure: \"structure\",\n\tstructure: \"structure\",\n\ttopics: \"structure\",\n\twiki: \"contents\",\n};\n\nconst GITHUB_OWNER_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38}[A-Za-z0-9])?$/;\nconst GITHUB_REPO_RE = /^[A-Za-z0-9._-]{1,100}$/;\n\nfunction readString(record: Record<string, unknown>, keys: string[]): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = record[key];\n\t\tif (typeof value === \"string\" && value.trim()) return value.trim();\n\t}\n\treturn undefined;\n}\n\nfunction readPageRef(record: Record<string, unknown>): string | number | undefined {\n\tfor (const key of [\"page\", \"pageName\", \"pageTitle\"]) {\n\t\tconst value = record[key];\n\t\tif (value === undefined || value === null) continue;\n\t\tif (typeof value === \"number\") {\n\t\t\tif (!Number.isInteger(value) || value < 1) throw new Error(\"page must be a positive 1-based integer\");\n\t\t\treturn value;\n\t\t}\n\t\tif (typeof value === \"string\" && value.trim()) return normalizePageRef(value.trim());\n\t\tthrow new Error(\"page must be a non-empty title or positive 1-based integer\");\n\t}\n\treturn undefined;\n}\n\nfunction coerceRepoNameInput(value: unknown): unknown {\n\tif (Array.isArray(value)) return value;\n\tif (typeof value !== \"string\") return value;\n\tconst trimmed = value.trim();\n\tif (!trimmed.startsWith(\"[\")) return value;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(trimmed);\n\t\tif (Array.isArray(parsed)) return parsed;\n\t} catch {\n\t\t/* keep original string */\n\t}\n\treturn value;\n}\n\nfunction normalizeAction(\n\taction: unknown,\n\tquestion: string | undefined,\n\tpage: string | number | undefined,\n): DeepWikiAction {\n\tif (typeof action === \"string\") {\n\t\tconst key = action\n\t\t\t.trim()\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[\\s-]+/g, \"_\");\n\t\tif (!key) return inferAction(question, page);\n\t\tconst normalized = ACTION_ALIASES[key];\n\t\tif (normalized) return normalized;\n\t\tthrow new Error(`unsupported deepwiki action: ${action}`);\n\t}\n\tif (action !== undefined && action !== null) {\n\t\tthrow new Error(\"deepwiki action must be a string\");\n\t}\n\treturn inferAction(question, page);\n}\n\nfunction inferAction(question: string | undefined, page: string | number | undefined): DeepWikiAction {\n\tif (question) return \"question\";\n\tif (page !== undefined) return \"contents\";\n\treturn \"structure\";\n}\n\nexport function normalizeRepoName(value: unknown): string {\n\tif (typeof value !== \"string\") throw new Error(\"repoName is required\");\n\n\tconst input = value.trim().replace(/^\\/+|\\/+$/g, \"\");\n\tif (!input) throw new Error(\"repoName is required\");\n\n\tlet owner: string | undefined;\n\tlet repo: string | undefined;\n\n\tconst sshMatch = input.match(/^git@github\\.com:([^/\\s]+)\\/([^/\\s]+?)(?:\\.git)?$/i);\n\tif (sshMatch) {\n\t\towner = sshMatch[1];\n\t\trepo = sshMatch[2];\n\t} else if (/^(?:https?:\\/\\/)?(?:www\\.)?(?:github\\.com|deepwiki\\.com)\\//i.test(input)) {\n\t\tconst urlInput = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(input) ? input : `https://${input}`;\n\t\ttry {\n\t\t\tconst url = new URL(urlInput);\n\t\t\tconst [urlOwner, urlRepo] = url.pathname.replace(/^\\/+|\\/+$/g, \"\").split(\"/\");\n\t\t\towner = urlOwner;\n\t\t\trepo = urlRepo?.replace(/\\.git$/i, \"\");\n\t\t} catch {\n\t\t\tthrow new Error('repoName must use \"owner/repo\" format');\n\t\t}\n\t} else {\n\t\tconst parts = input.replace(/\\.git$/i, \"\").split(\"/\");\n\t\tif (parts.length === 2) {\n\t\t\t[owner, repo] = parts;\n\t\t}\n\t}\n\n\tif (!owner || !repo || !GITHUB_OWNER_RE.test(owner) || !GITHUB_REPO_RE.test(repo)) {\n\t\tthrow new Error('repoName must use a public GitHub \"owner/repo\" identifier');\n\t}\n\treturn `${owner}/${repo}`;\n}\n\nfunction normalizeRepoNames(value: unknown): string[] {\n\tvalue = coerceRepoNameInput(value);\n\tconst values =\n\t\ttypeof value === \"string\" && value.includes(\",\")\n\t\t\t? value\n\t\t\t\t\t.split(\",\")\n\t\t\t\t\t.map((repo) => repo.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: Array.isArray(value)\n\t\t\t\t? value\n\t\t\t\t: [value];\n\tconst repos = values.map((item) => normalizeRepoName(item));\n\tconst seen = new Set<string>();\n\tconst unique: string[] = [];\n\tfor (const repo of repos) {\n\t\tconst key = repo.toLowerCase();\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tunique.push(repo);\n\t}\n\tif (unique.length > MAX_QUESTION_REPOS) {\n\t\tthrow new Error(`question action supports at most ${MAX_QUESTION_REPOS} repositories`);\n\t}\n\treturn unique;\n}\n\nexport function normalizeDeepWikiParams(input: unknown): DeepWikiParams {\n\tif (!input || typeof input !== \"object\") {\n\t\tthrow new Error(\"deepwiki arguments must be an object\");\n\t}\n\n\tconst record = input as Record<string, unknown>;\n\tconst question = readString(record, [\"question\", \"query\", \"prompt\"]);\n\tconst page = readPageRef(record);\n\tconst action = normalizeAction(record.action, question, page);\n\tconst rawRepoName =\n\t\trecord.repoName ??\n\t\trecord.repo ??\n\t\trecord.repos ??\n\t\trecord.repository ??\n\t\trecord.repositories ??\n\t\trecord.githubRepo ??\n\t\trecord.githubRepos;\n\tconst repoNames = normalizeRepoNames(rawRepoName);\n\n\tif (action === \"question\") {\n\t\tif (!question) throw new Error(\"question is required when action is question\");\n\t\treturn {\n\t\t\taction,\n\t\t\trepoName: repoNames.length > 1 ? repoNames : repoNames[0],\n\t\t\tquestion,\n\t\t};\n\t}\n\n\tif (repoNames.length !== 1) {\n\t\tthrow new Error(`${action} action requires exactly one repository`);\n\t}\n\n\t// page is meaningful only for contents; structure/question ignore it.\n\treturn {\n\t\taction,\n\t\trepoName: repoNames[0],\n\t\t...(action === \"contents\" && page !== undefined ? { page } : {}),\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,GAAG,EAAiC,MAAM,wBAAwB,CAAC;AACrH,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAYtF,OAAO,KAAK,EAAE,YAAY,EAA4B,QAAQ,EAAkC,MAAM,YAAY,CAAC;AAanH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,IACvE,KAAK,GAAG,EAAE,OAAO,KAAK,EAAE,aAAa,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI;aAkpBhF,OAAO;oBArQJ,MAAM,KAAG,MAAM,EAAE;;wBALb,MAAM,KAAG,IAAI;;EAwRzC"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/extensions/question/dialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,GAAG,EAAiC,MAAM,wBAAwB,CAAC;AACrH,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEhF,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAYtF,OAAO,KAAK,EAAE,YAAY,EAA4B,QAAQ,EAAkC,MAAM,YAAY,CAAC;AA0DnH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,MAAM,CAAC,EAAE,WAAW,IACvE,KAAK,GAAG,EAAE,OAAO,KAAK,EAAE,aAAa,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI;aA4rBhF,OAAO;oBA/RJ,MAAM,KAAG,MAAM,EAAE;;wBALb,MAAM,KAAG,IAAI;;EAkTzC"}
@@ -13,6 +13,38 @@ const PREVIEW_MAX_LINES = 16;
13
13
  * Reserved so the preview clamps short of the terminal height.
14
14
  */
15
15
  const PREVIEW_CHROME_ROWS = 18;
16
+ const VIEWPORT_SCROLL_STEP = 4;
17
+ function applyVerticalViewport(lines, width, terminalRows, theme, options = {}) {
18
+ const maxRows = Math.max(1, Math.floor(terminalRows));
19
+ if (lines.length <= maxRows)
20
+ return { lines, start: 0, maxStart: 0 };
21
+ if (maxRows < 5) {
22
+ const anchor = Math.max(0, Math.min(lines.length - 1, options.anchorLine ?? 0));
23
+ const start = Math.max(0, Math.min(lines.length - maxRows, anchor - Math.floor(maxRows / 2)));
24
+ return { lines: lines.slice(start, start + maxRows), start, maxStart: lines.length - maxRows };
25
+ }
26
+ const first = lines[0] ?? "";
27
+ const last = lines[lines.length - 1] ?? "";
28
+ const body = lines.slice(1, -1);
29
+ const contentRows = maxRows - 4;
30
+ const maxStart = Math.max(0, body.length - contentRows);
31
+ const anchorInBody = Math.max(0, Math.min(body.length - 1, (options.anchorLine ?? 1) - 1));
32
+ const requestedStart = options.preferredStart ?? Math.max(0, anchorInBody - Math.floor(contentRows / 2));
33
+ const start = Math.max(0, Math.min(maxStart, requestedStart));
34
+ const bottomHidden = Math.max(0, body.length - start - contentRows);
35
+ const indicator = (text) => theme.fg("dim", truncateToWidth(text, Math.max(1, width)));
36
+ return {
37
+ lines: [
38
+ first,
39
+ start > 0 ? indicator(`↑ ${start} rows hidden`) : "",
40
+ ...body.slice(start, start + contentRows),
41
+ bottomHidden > 0 ? indicator(`↓ ${bottomHidden} rows hidden`) : "",
42
+ last,
43
+ ],
44
+ start,
45
+ maxStart,
46
+ };
47
+ }
16
48
  export function createQuestionDialog(questions, signal) {
17
49
  return (tui, theme, keybindings, done) => {
18
50
  let currentIdx = 0;
@@ -20,6 +52,8 @@ export function createQuestionDialog(questions, signal) {
20
52
  let inputMode;
21
53
  let noteTarget;
22
54
  let notesSnapshot;
55
+ let reviewScrollOffset = 0;
56
+ let reviewMaxScrollOffset = 0;
23
57
  let dialogFocused = false;
24
58
  let footerFocused = false;
25
59
  let finished = false;
@@ -80,6 +114,8 @@ export function createQuestionDialog(questions, signal) {
80
114
  view = "question";
81
115
  inputMode = undefined;
82
116
  noteTarget = undefined;
117
+ reviewScrollOffset = 0;
118
+ reviewMaxScrollOffset = 0;
83
119
  notesSnapshot = undefined;
84
120
  footerFocused = false;
85
121
  editor.setText("");
@@ -103,6 +139,8 @@ export function createQuestionDialog(questions, signal) {
103
139
  view = "review";
104
140
  inputMode = undefined;
105
141
  noteTarget = undefined;
142
+ reviewScrollOffset = 0;
143
+ reviewMaxScrollOffset = 0;
106
144
  footerFocused = false;
107
145
  editor.setText("");
108
146
  syncEditorFocus();
@@ -273,6 +311,16 @@ export function createQuestionDialog(questions, signal) {
273
311
  }
274
312
  };
275
313
  function handleReviewInput(data) {
314
+ if (keyMatches(data, "tui.select.up")) {
315
+ reviewScrollOffset = Math.max(0, reviewScrollOffset - VIEWPORT_SCROLL_STEP);
316
+ refresh();
317
+ return;
318
+ }
319
+ if (keyMatches(data, "tui.select.down")) {
320
+ reviewScrollOffset = Math.min(reviewMaxScrollOffset, reviewScrollOffset + VIEWPORT_SCROLL_STEP);
321
+ refresh();
322
+ return;
323
+ }
276
324
  if (keyMatches(data, "tui.editor.cursorLeft")) {
277
325
  setCurrentIdx(questions.length - 1);
278
326
  return;
@@ -459,7 +507,7 @@ export function createQuestionDialog(questions, signal) {
459
507
  }
460
508
  lines.push("");
461
509
  }
462
- wrapWithPrefix(" ", theme.fg("dim", joinHints(keyAction("tui.select.confirm", "submit"), keyAction("tui.select.cancel", "edit last question"))), renderWidth, lines);
510
+ wrapWithPrefix(" ", theme.fg("dim", joinHints(keyAction("tui.select.confirm", "submit"), keyAction("tui.select.cancel", "edit last question"), keyGroupAction(["tui.select.up", "tui.select.down"], "scroll"))), renderWidth, lines);
463
511
  }
464
512
  function compute(width, terminalRows) {
465
513
  const lines = [];
@@ -469,7 +517,12 @@ export function createQuestionDialog(questions, signal) {
469
517
  if (view === "review") {
470
518
  renderReview(renderWidth, lines);
471
519
  lines.push(ruleBorder(theme, renderWidth));
472
- return lines;
520
+ const viewport = applyVerticalViewport(lines, renderWidth, terminalRows, theme, {
521
+ preferredStart: reviewScrollOffset,
522
+ });
523
+ reviewMaxScrollOffset = viewport.maxStart;
524
+ reviewScrollOffset = viewport.start;
525
+ return viewport.lines;
473
526
  }
474
527
  const question = currentQuestion();
475
528
  const state = currentState();
@@ -479,7 +532,10 @@ export function createQuestionDialog(questions, signal) {
479
532
  wrapWithPrefix(" ", `${theme.fg("accent", question.header)} ${theme.fg("text", question.question)}`, renderWidth, lines);
480
533
  lines.push("");
481
534
  const optionLines = [];
482
- const hasPreview = !isMulti && options.some((option) => option.kind === "option" && option.preview);
535
+ let focusedOptionStart = 0;
536
+ let focusedOptionEnd = 0;
537
+ let inputAnchorOffset;
538
+ const hasPreview = inputMode === undefined && !isMulti && options.some((option) => option.kind === "option" && option.preview);
483
539
  const showPreviewSideBySide = hasPreview && renderWidth >= 60;
484
540
  const listWidth = showPreviewSideBySide ? Math.max(20, Math.floor(renderWidth * 0.4)) : renderWidth;
485
541
  for (let index = 0; index < options.length; index++) {
@@ -502,10 +558,14 @@ export function createQuestionDialog(questions, signal) {
502
558
  const labelText = customText ? `${option.label} ✎ ${customText}` : option.label;
503
559
  const label = `${index + 1}. ${labelText}${selectedSingle ? " ✓" : ""}${note}`;
504
560
  const color = focused ? "accent" : selectedSingle || checked ? "success" : "text";
561
+ if (focused)
562
+ focusedOptionStart = optionLines.length;
505
563
  wrapWithPrefix(`${focusArrow} ${marker} `, theme.fg(color, label), listWidth, optionLines);
506
564
  if (option.kind === "option" && option.description) {
507
565
  wrapWithPrefix(" ", theme.fg("muted", option.description), listWidth, optionLines);
508
566
  }
567
+ if (focused)
568
+ focusedOptionEnd = optionLines.length;
509
569
  }
510
570
  if (inputMode) {
511
571
  optionLines.push("");
@@ -513,15 +573,18 @@ export function createQuestionDialog(questions, signal) {
513
573
  wrapWithPrefix(" ", theme.fg("muted", label), listWidth, optionLines);
514
574
  for (const line of editor.render(Math.max(1, listWidth - 2)))
515
575
  optionLines.push(` ${line}`);
576
+ inputAnchorOffset = Math.max(0, optionLines.length - 1);
516
577
  }
578
+ const optionListStartLine = lines.length;
517
579
  if (showPreviewSideBySide) {
518
580
  const gap = 2;
519
581
  const rightWidth = renderWidth - listWidth - gap;
520
582
  const focused = options[state.optionIndex];
521
583
  const previewText = focused?.kind === "option" && focused.preview ? focused.preview : "";
522
- const rightLines = previewText
584
+ const preview = previewText
523
585
  ? previewLines(previewText, rightWidth, terminalRows)
524
586
  : [theme.fg("dim", "(no preview)")];
587
+ const rightLines = [...Array.from({ length: focusedOptionStart }, () => ""), ...preview];
525
588
  const maxRows = Math.max(optionLines.length, rightLines.length);
526
589
  const pad = " ".repeat(gap);
527
590
  for (let row = 0; row < maxRows; row++) {
@@ -532,22 +595,23 @@ export function createQuestionDialog(questions, signal) {
532
595
  }
533
596
  }
534
597
  else {
535
- lines.push(...optionLines);
536
598
  if (hasPreview) {
537
599
  const focused = options[state.optionIndex];
538
600
  const previewText = focused?.kind === "option" && focused.preview ? focused.preview : "";
539
601
  if (previewText) {
540
- lines.push("");
541
- lines.push(ruleBorder(theme, renderWidth, "dim"));
542
- lines.push(...previewLines(previewText, renderWidth, terminalRows));
602
+ optionLines.splice(focusedOptionEnd, 0, "", ruleBorder(theme, renderWidth, "dim"), ...previewLines(previewText, renderWidth, terminalRows));
543
603
  }
544
604
  }
605
+ lines.push(...optionLines);
545
606
  }
546
607
  lines.push("");
608
+ const chatLine = lines.length;
547
609
  const chatPrefix = footerFocused ? theme.fg("accent", "→") : " ";
548
610
  wrapWithPrefix(`${chatPrefix} `, theme.fg(footerFocused ? "accent" : "text", "Chat about this"), renderWidth, lines);
611
+ let warningLine;
549
612
  if (state.warning) {
550
613
  lines.push("");
614
+ warningLine = lines.length;
551
615
  wrapWithPrefix(" ", theme.fg("warning", state.warning), renderWidth, lines);
552
616
  }
553
617
  lines.push("");
@@ -568,7 +632,14 @@ export function createQuestionDialog(questions, signal) {
568
632
  wrapWithPrefix(" ", theme.fg("dim", joinHints(`${digitRange} select`, keyAction("tui.input.tab", "notes/custom"), keyAction("tui.select.confirm", "select"), keyGroupAction(["tui.editor.cursorLeft", "tui.editor.cursorRight"], "questions"), keyAction("tui.select.cancel", "cancel"))), renderWidth, lines);
569
633
  }
570
634
  lines.push(ruleBorder(theme, renderWidth));
571
- return lines;
635
+ let anchorLine = optionListStartLine + focusedOptionStart;
636
+ if (inputAnchorOffset !== undefined)
637
+ anchorLine = optionListStartLine + inputAnchorOffset;
638
+ else if (warningLine !== undefined)
639
+ anchorLine = warningLine;
640
+ else if (footerFocused)
641
+ anchorLine = chatLine;
642
+ return applyVerticalViewport(lines, renderWidth, terminalRows, theme, { anchorLine }).lines;
572
643
  }
573
644
  return {
574
645
  get focused() {