longer-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (289) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/README.zh-CN.md +227 -0
  4. package/agent_templates/executor/agent.yaml +22 -0
  5. package/agent_templates/executor/system_prompt.md +17 -0
  6. package/agent_templates/explorer/agent.yaml +13 -0
  7. package/agent_templates/explorer/system_prompt.md +19 -0
  8. package/agent_templates/main/agent.yaml +7 -0
  9. package/agent_templates/main/system_prompt.md +45 -0
  10. package/configExample.yaml +83 -0
  11. package/dist/agents/agent.d.ts +79 -0
  12. package/dist/agents/agent.d.ts.map +1 -0
  13. package/dist/agents/agent.js +156 -0
  14. package/dist/agents/agent.js.map +1 -0
  15. package/dist/agents/tool-loop.d.ts +140 -0
  16. package/dist/agents/tool-loop.d.ts.map +1 -0
  17. package/dist/agents/tool-loop.js +465 -0
  18. package/dist/agents/tool-loop.js.map +1 -0
  19. package/dist/ask.d.ts +81 -0
  20. package/dist/ask.d.ts.map +1 -0
  21. package/dist/ask.js +34 -0
  22. package/dist/ask.js.map +1 -0
  23. package/dist/auth/openai-oauth.d.ts +66 -0
  24. package/dist/auth/openai-oauth.d.ts.map +1 -0
  25. package/dist/auth/openai-oauth.js +640 -0
  26. package/dist/auth/openai-oauth.js.map +1 -0
  27. package/dist/cli.d.ts +14 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +254 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/commands.d.ts +118 -0
  32. package/dist/commands.d.ts.map +1 -0
  33. package/dist/commands.js +862 -0
  34. package/dist/commands.js.map +1 -0
  35. package/dist/config.d.ts +130 -0
  36. package/dist/config.d.ts.map +1 -0
  37. package/dist/config.js +648 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/context-rendering.d.ts +69 -0
  40. package/dist/context-rendering.d.ts.map +1 -0
  41. package/dist/context-rendering.js +250 -0
  42. package/dist/context-rendering.js.map +1 -0
  43. package/dist/document-projection.d.ts +12 -0
  44. package/dist/document-projection.d.ts.map +1 -0
  45. package/dist/document-projection.js +75 -0
  46. package/dist/document-projection.js.map +1 -0
  47. package/dist/ephemeral-log.d.ts +15 -0
  48. package/dist/ephemeral-log.d.ts.map +1 -0
  49. package/dist/ephemeral-log.js +173 -0
  50. package/dist/ephemeral-log.js.map +1 -0
  51. package/dist/file-attach.d.ts +89 -0
  52. package/dist/file-attach.d.ts.map +1 -0
  53. package/dist/file-attach.js +571 -0
  54. package/dist/file-attach.js.map +1 -0
  55. package/dist/index.d.ts +29 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +43 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/init-wizard.d.ts +13 -0
  60. package/dist/init-wizard.d.ts.map +1 -0
  61. package/dist/init-wizard.js +328 -0
  62. package/dist/init-wizard.js.map +1 -0
  63. package/dist/log-entry.d.ts +104 -0
  64. package/dist/log-entry.d.ts.map +1 -0
  65. package/dist/log-entry.js +292 -0
  66. package/dist/log-entry.js.map +1 -0
  67. package/dist/log-projection.d.ts +73 -0
  68. package/dist/log-projection.d.ts.map +1 -0
  69. package/dist/log-projection.js +651 -0
  70. package/dist/log-projection.js.map +1 -0
  71. package/dist/mcp-client.d.ts +55 -0
  72. package/dist/mcp-client.d.ts.map +1 -0
  73. package/dist/mcp-client.js +402 -0
  74. package/dist/mcp-client.js.map +1 -0
  75. package/dist/model-selection.d.ts +16 -0
  76. package/dist/model-selection.d.ts.map +1 -0
  77. package/dist/model-selection.js +181 -0
  78. package/dist/model-selection.js.map +1 -0
  79. package/dist/network-retry.d.ts +38 -0
  80. package/dist/network-retry.d.ts.map +1 -0
  81. package/dist/network-retry.js +140 -0
  82. package/dist/network-retry.js.map +1 -0
  83. package/dist/persistence.d.ts +104 -0
  84. package/dist/persistence.d.ts.map +1 -0
  85. package/dist/persistence.js +644 -0
  86. package/dist/persistence.js.map +1 -0
  87. package/dist/primitives/context.d.ts +29 -0
  88. package/dist/primitives/context.d.ts.map +1 -0
  89. package/dist/primitives/context.js +85 -0
  90. package/dist/primitives/context.js.map +1 -0
  91. package/dist/progress.d.ts +51 -0
  92. package/dist/progress.d.ts.map +1 -0
  93. package/dist/progress.js +229 -0
  94. package/dist/progress.js.map +1 -0
  95. package/dist/provider-presets.d.ts +34 -0
  96. package/dist/provider-presets.d.ts.map +1 -0
  97. package/dist/provider-presets.js +181 -0
  98. package/dist/provider-presets.js.map +1 -0
  99. package/dist/providers/anthropic.d.ts +32 -0
  100. package/dist/providers/anthropic.d.ts.map +1 -0
  101. package/dist/providers/anthropic.js +450 -0
  102. package/dist/providers/anthropic.js.map +1 -0
  103. package/dist/providers/base.d.ts +135 -0
  104. package/dist/providers/base.d.ts.map +1 -0
  105. package/dist/providers/base.js +104 -0
  106. package/dist/providers/base.js.map +1 -0
  107. package/dist/providers/glm.d.ts +18 -0
  108. package/dist/providers/glm.d.ts.map +1 -0
  109. package/dist/providers/glm.js +59 -0
  110. package/dist/providers/glm.js.map +1 -0
  111. package/dist/providers/kimi.d.ts +23 -0
  112. package/dist/providers/kimi.d.ts.map +1 -0
  113. package/dist/providers/kimi.js +89 -0
  114. package/dist/providers/kimi.js.map +1 -0
  115. package/dist/providers/minimax.d.ts +20 -0
  116. package/dist/providers/minimax.d.ts.map +1 -0
  117. package/dist/providers/minimax.js +192 -0
  118. package/dist/providers/minimax.js.map +1 -0
  119. package/dist/providers/openai-chat.d.ts +33 -0
  120. package/dist/providers/openai-chat.d.ts.map +1 -0
  121. package/dist/providers/openai-chat.js +543 -0
  122. package/dist/providers/openai-chat.js.map +1 -0
  123. package/dist/providers/openai-responses.d.ts +26 -0
  124. package/dist/providers/openai-responses.d.ts.map +1 -0
  125. package/dist/providers/openai-responses.js +443 -0
  126. package/dist/providers/openai-responses.js.map +1 -0
  127. package/dist/providers/openrouter.d.ts +24 -0
  128. package/dist/providers/openrouter.d.ts.map +1 -0
  129. package/dist/providers/openrouter.js +177 -0
  130. package/dist/providers/openrouter.js.map +1 -0
  131. package/dist/providers/registry.d.ts +7 -0
  132. package/dist/providers/registry.d.ts.map +1 -0
  133. package/dist/providers/registry.js +38 -0
  134. package/dist/providers/registry.js.map +1 -0
  135. package/dist/security/path.d.ts +51 -0
  136. package/dist/security/path.d.ts.map +1 -0
  137. package/dist/security/path.js +187 -0
  138. package/dist/security/path.js.map +1 -0
  139. package/dist/security/sensitive-files.d.ts +3 -0
  140. package/dist/security/sensitive-files.d.ts.map +1 -0
  141. package/dist/security/sensitive-files.js +41 -0
  142. package/dist/security/sensitive-files.js.map +1 -0
  143. package/dist/session.d.ts +446 -0
  144. package/dist/session.d.ts.map +1 -0
  145. package/dist/session.js +4595 -0
  146. package/dist/session.js.map +1 -0
  147. package/dist/settings.d.ts +46 -0
  148. package/dist/settings.d.ts.map +1 -0
  149. package/dist/settings.js +134 -0
  150. package/dist/settings.js.map +1 -0
  151. package/dist/show-context.d.ts +35 -0
  152. package/dist/show-context.d.ts.map +1 -0
  153. package/dist/show-context.js +320 -0
  154. package/dist/show-context.js.map +1 -0
  155. package/dist/skills/loader.d.ts +49 -0
  156. package/dist/skills/loader.d.ts.map +1 -0
  157. package/dist/skills/loader.js +166 -0
  158. package/dist/skills/loader.js.map +1 -0
  159. package/dist/summarize-context.d.ts +29 -0
  160. package/dist/summarize-context.d.ts.map +1 -0
  161. package/dist/summarize-context.js +247 -0
  162. package/dist/summarize-context.js.map +1 -0
  163. package/dist/templates/loader.d.ts +104 -0
  164. package/dist/templates/loader.d.ts.map +1 -0
  165. package/dist/templates/loader.js +514 -0
  166. package/dist/templates/loader.js.map +1 -0
  167. package/dist/tools/basic.d.ts +29 -0
  168. package/dist/tools/basic.d.ts.map +1 -0
  169. package/dist/tools/basic.js +2079 -0
  170. package/dist/tools/basic.js.map +1 -0
  171. package/dist/tools/comm.d.ts +17 -0
  172. package/dist/tools/comm.d.ts.map +1 -0
  173. package/dist/tools/comm.js +192 -0
  174. package/dist/tools/comm.js.map +1 -0
  175. package/dist/tools/web-fetch.d.ts +11 -0
  176. package/dist/tools/web-fetch.d.ts.map +1 -0
  177. package/dist/tools/web-fetch.js +237 -0
  178. package/dist/tools/web-fetch.js.map +1 -0
  179. package/dist/tools/web-search.d.ts +24 -0
  180. package/dist/tools/web-search.d.ts.map +1 -0
  181. package/dist/tools/web-search.js +51 -0
  182. package/dist/tools/web-search.js.map +1 -0
  183. package/dist/tui/app.d.ts +35 -0
  184. package/dist/tui/app.d.ts.map +1 -0
  185. package/dist/tui/app.js +1042 -0
  186. package/dist/tui/app.js.map +1 -0
  187. package/dist/tui/checkbox-picker.d.ts +35 -0
  188. package/dist/tui/checkbox-picker.d.ts.map +1 -0
  189. package/dist/tui/checkbox-picker.js +85 -0
  190. package/dist/tui/checkbox-picker.js.map +1 -0
  191. package/dist/tui/command-picker.d.ts +31 -0
  192. package/dist/tui/command-picker.d.ts.map +1 -0
  193. package/dist/tui/command-picker.js +113 -0
  194. package/dist/tui/command-picker.js.map +1 -0
  195. package/dist/tui/components/ask-panel.d.ts +21 -0
  196. package/dist/tui/components/ask-panel.d.ts.map +1 -0
  197. package/dist/tui/components/ask-panel.js +81 -0
  198. package/dist/tui/components/ask-panel.js.map +1 -0
  199. package/dist/tui/components/conversation-panel.d.ts +68 -0
  200. package/dist/tui/components/conversation-panel.d.ts.map +1 -0
  201. package/dist/tui/components/conversation-panel.js +611 -0
  202. package/dist/tui/components/conversation-panel.js.map +1 -0
  203. package/dist/tui/components/input-panel.d.ts +27 -0
  204. package/dist/tui/components/input-panel.d.ts.map +1 -0
  205. package/dist/tui/components/input-panel.js +725 -0
  206. package/dist/tui/components/input-panel.js.map +1 -0
  207. package/dist/tui/components/logo-panel.d.ts +14 -0
  208. package/dist/tui/components/logo-panel.d.ts.map +1 -0
  209. package/dist/tui/components/logo-panel.js +37 -0
  210. package/dist/tui/components/logo-panel.js.map +1 -0
  211. package/dist/tui/components/plan-panel.d.ts +10 -0
  212. package/dist/tui/components/plan-panel.d.ts.map +1 -0
  213. package/dist/tui/components/plan-panel.js +8 -0
  214. package/dist/tui/components/plan-panel.js.map +1 -0
  215. package/dist/tui/components/status-bar.d.ts +24 -0
  216. package/dist/tui/components/status-bar.d.ts.map +1 -0
  217. package/dist/tui/components/status-bar.js +80 -0
  218. package/dist/tui/components/status-bar.js.map +1 -0
  219. package/dist/tui/input/editor-state.d.ts +22 -0
  220. package/dist/tui/input/editor-state.d.ts.map +1 -0
  221. package/dist/tui/input/editor-state.js +157 -0
  222. package/dist/tui/input/editor-state.js.map +1 -0
  223. package/dist/tui/input/keymap.d.ts +3 -0
  224. package/dist/tui/input/keymap.d.ts.map +1 -0
  225. package/dist/tui/input/keymap.js +72 -0
  226. package/dist/tui/input/keymap.js.map +1 -0
  227. package/dist/tui/input/paste-slots.d.ts +17 -0
  228. package/dist/tui/input/paste-slots.d.ts.map +1 -0
  229. package/dist/tui/input/paste-slots.js +46 -0
  230. package/dist/tui/input/paste-slots.js.map +1 -0
  231. package/dist/tui/input/paste.d.ts +15 -0
  232. package/dist/tui/input/paste.d.ts.map +1 -0
  233. package/dist/tui/input/paste.js +35 -0
  234. package/dist/tui/input/paste.js.map +1 -0
  235. package/dist/tui/input/protocol.d.ts +9 -0
  236. package/dist/tui/input/protocol.d.ts.map +1 -0
  237. package/dist/tui/input/protocol.js +387 -0
  238. package/dist/tui/input/protocol.js.map +1 -0
  239. package/dist/tui/input/sanitize.d.ts +6 -0
  240. package/dist/tui/input/sanitize.d.ts.map +1 -0
  241. package/dist/tui/input/sanitize.js +20 -0
  242. package/dist/tui/input/sanitize.js.map +1 -0
  243. package/dist/tui/input/types.d.ts +18 -0
  244. package/dist/tui/input/types.d.ts.map +1 -0
  245. package/dist/tui/input/types.js +2 -0
  246. package/dist/tui/input/types.js.map +1 -0
  247. package/dist/tui/launch.d.ts +23 -0
  248. package/dist/tui/launch.d.ts.map +1 -0
  249. package/dist/tui/launch.js +104 -0
  250. package/dist/tui/launch.js.map +1 -0
  251. package/dist/tui/theme.d.ts +20 -0
  252. package/dist/tui/theme.d.ts.map +1 -0
  253. package/dist/tui/theme.js +29 -0
  254. package/dist/tui/theme.js.map +1 -0
  255. package/dist/tui/types.d.ts +136 -0
  256. package/dist/tui/types.d.ts.map +1 -0
  257. package/dist/tui/types.js +9 -0
  258. package/dist/tui/types.js.map +1 -0
  259. package/package.json +76 -0
  260. package/prompts/sections/agents_md.md +23 -0
  261. package/prompts/sections/important_log.md +16 -0
  262. package/prompts/sections/system_mechanisms.md +18 -0
  263. package/prompts/tools/apply_patch.md +31 -0
  264. package/prompts/tools/ask.md +18 -0
  265. package/prompts/tools/bash.md +13 -0
  266. package/prompts/tools/bash_background.md +9 -0
  267. package/prompts/tools/bash_output.md +9 -0
  268. package/prompts/tools/check_status.md +3 -0
  269. package/prompts/tools/diff.md +5 -0
  270. package/prompts/tools/edit_file.md +11 -0
  271. package/prompts/tools/glob.md +7 -0
  272. package/prompts/tools/grep.md +20 -0
  273. package/prompts/tools/kill_agent.md +3 -0
  274. package/prompts/tools/kill_shell.md +5 -0
  275. package/prompts/tools/list_dir.md +5 -0
  276. package/prompts/tools/plan.md +252 -0
  277. package/prompts/tools/read_file.md +9 -0
  278. package/prompts/tools/show_context.md +12 -0
  279. package/prompts/tools/skill.md +7 -0
  280. package/prompts/tools/spawn_agent.md +195 -0
  281. package/prompts/tools/summarize_context.md +122 -0
  282. package/prompts/tools/test.md +5 -0
  283. package/prompts/tools/wait.md +17 -0
  284. package/prompts/tools/web_fetch.md +9 -0
  285. package/prompts/tools/web_search.md +5 -0
  286. package/prompts/tools/write_file.md +11 -0
  287. package/skills/.staging/.gitkeep +0 -0
  288. package/skills/explain-code/SKILL.md +15 -0
  289. package/skills/skill-manager/SKILL.md +83 -0
@@ -0,0 +1,725 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Chat input area with @filename and /command autocomplete overlays.
4
+ *
5
+ * Uses a custom multi-line editor with raw-stdin protocol parsing for
6
+ * better terminal compatibility (Ghostty/macOS Terminal/iTerm/Warp/etc).
7
+ */
8
+ import React, { useState, useCallback, useRef, useEffect } from "react";
9
+ import { Box, Text, useStdin } from "ink";
10
+ import { StringDecoder } from "node:string_decoder";
11
+ import { scanCandidates } from "../../file-attach.js";
12
+ import { InputProtocolParser } from "../input/protocol.js";
13
+ import { mapInputEventToCommand } from "../input/keymap.js";
14
+ import { withValueAndCursor, insertText, moveLeft, moveRight, moveWordLeft, moveWordRight, moveUp, moveDown, moveHome, moveEnd, deleteBackward, deleteForward, deleteWordBackward, deleteWordForward, deleteToLineStart, deleteToLineEnd, } from "../input/editor-state.js";
15
+ import { TurnPasteCounter, classifyPastedText } from "../input/paste.js";
16
+ import { TurnPasteSlotStore } from "../input/paste-slots.js";
17
+ import { sanitizeInputText, sanitizeSubmitText } from "../input/sanitize.js";
18
+ import { acceptCommandPickerSelection, createCommandPicker, exitCommandPickerLevel, getCommandPickerLevel, getCommandPickerPath, getCommandPickerVisibleRange, isCommandPickerActive, moveCommandPickerSelection, } from "../command-picker.js";
19
+ import { createCheckboxPicker, isCheckboxPickerActive, getCheckboxPickerVisibleRange, moveCheckboxSelection, toggleCheckboxItem, } from "../checkbox-picker.js";
20
+ import { theme } from "../theme.js";
21
+ const EMPTY_OVERLAY = {
22
+ visible: false,
23
+ mode: "",
24
+ items: [],
25
+ values: [],
26
+ selected: 0,
27
+ };
28
+ const ANSI_INVERSE_ON = "\u001B[7m";
29
+ const ANSI_INVERSE_OFF = "\u001B[27m";
30
+ const PROMPT = "❯ ";
31
+ const PROMPT_INDENT = " ".repeat(PROMPT.length);
32
+ const INPUT_VIEWPORT_MAX_LINES = 100;
33
+ const RESUME_PICKER_MAX_VISIBLE = 10;
34
+ function inverse(text) {
35
+ return `${ANSI_INVERSE_ON}${text}${ANSI_INVERSE_OFF}`;
36
+ }
37
+ function clamp(value, min, max) {
38
+ if (value < min)
39
+ return min;
40
+ if (value > max)
41
+ return max;
42
+ return value;
43
+ }
44
+ function lineStartOffsets(text) {
45
+ const starts = [0];
46
+ for (let i = 0; i < text.length; i++) {
47
+ if (text[i] === "\n")
48
+ starts.push(i + 1);
49
+ }
50
+ return starts;
51
+ }
52
+ function lineAtCursor(starts, cursor, textLength) {
53
+ for (let i = 0; i < starts.length; i++) {
54
+ const nextStart = i + 1 < starts.length ? starts[i + 1] : textLength + 1;
55
+ if (cursor < nextStart)
56
+ return i;
57
+ }
58
+ return starts.length - 1;
59
+ }
60
+ function sliceInputViewport(value, cursor, maxLines) {
61
+ const safeCursor = clamp(cursor, 0, value.length);
62
+ if (maxLines <= 0)
63
+ return { text: value, cursor: safeCursor };
64
+ const starts = lineStartOffsets(value);
65
+ if (starts.length <= maxLines)
66
+ return { text: value, cursor: safeCursor };
67
+ const cursorLine = lineAtCursor(starts, safeCursor, value.length);
68
+ let startLine = Math.max(0, cursorLine - maxLines + 1);
69
+ let endLine = Math.min(starts.length, startLine + maxLines);
70
+ if (cursorLine >= endLine) {
71
+ endLine = cursorLine + 1;
72
+ startLine = Math.max(0, endLine - maxLines);
73
+ }
74
+ const startOffset = starts[startLine];
75
+ const endOffset = endLine < starts.length ? starts[endLine] - 1 : value.length;
76
+ const text = value.slice(startOffset, Math.max(startOffset, endOffset));
77
+ const viewportCursor = clamp(safeCursor - startOffset, 0, text.length);
78
+ return { text, cursor: viewportCursor };
79
+ }
80
+ function renderValueWithCursor(value, cursor, showCursor, pasteSlots) {
81
+ if (!showCursor)
82
+ return value;
83
+ const safeCursor = clamp(cursor, 0, value.length);
84
+ if (value.length === 0)
85
+ return inverse(" ");
86
+ let rendered = "";
87
+ for (let i = 0; i < value.length; i++) {
88
+ const ch = value[i];
89
+ const display = pasteSlots.labelFor(ch) ?? ch;
90
+ if (i === safeCursor) {
91
+ if (display === "\n") {
92
+ rendered += `${inverse(" ")}\n`;
93
+ }
94
+ else {
95
+ rendered += inverse(display);
96
+ }
97
+ }
98
+ else {
99
+ rendered += display;
100
+ }
101
+ }
102
+ if (safeCursor === value.length)
103
+ rendered += inverse(" ");
104
+ return rendered;
105
+ }
106
+ function OverlayView({ state }) {
107
+ if (!state.visible || state.items.length === 0)
108
+ return null;
109
+ const maxVisible = state.items.length;
110
+ const start = 0;
111
+ const end = Math.min(state.items.length, start + maxVisible);
112
+ const visibleItems = state.items.slice(start, end);
113
+ return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: visibleItems.map((item, i) => {
114
+ const actualIndex = start + i;
115
+ return (_jsxs(Text, { color: actualIndex === state.selected ? theme.accent : "gray", bold: actualIndex === state.selected, children: [actualIndex === state.selected ? " > " : " ", item] }, `overlay-${actualIndex}`));
116
+ }) }));
117
+ }
118
+ function CommandPickerView({ picker }) {
119
+ if (!isCommandPickerActive(picker))
120
+ return null;
121
+ const level = getCommandPickerLevel(picker);
122
+ const path = getCommandPickerPath(picker);
123
+ const { start, end } = getCommandPickerVisibleRange(picker);
124
+ const visibleOptions = level.options.slice(start, end);
125
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: theme.accent, children: picker.commandName }), path.length > 0 ? (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", path.join(" · ")] })) : null, visibleOptions.map((item, i) => {
126
+ const actualIndex = start + i;
127
+ return (_jsxs(Text, { color: actualIndex === level.selected ? theme.accent : "gray", bold: actualIndex === level.selected, children: [actualIndex === level.selected ? " > " : " ", item.label] }, `picker-${actualIndex}`));
128
+ })] }));
129
+ }
130
+ function CheckboxPickerView({ picker }) {
131
+ if (!isCheckboxPickerActive(picker))
132
+ return null;
133
+ const { start, end } = getCheckboxPickerVisibleRange(picker);
134
+ const visibleItems = picker.items.slice(start, end);
135
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: theme.accent, children: picker.title }), visibleItems.map((item, i) => {
136
+ const actualIndex = start + i;
137
+ const checkbox = item.checked ? "[x]" : "[ ]";
138
+ return (_jsxs(Text, { color: actualIndex === picker.selected ? theme.accent : "gray", bold: actualIndex === picker.selected, children: [actualIndex === picker.selected ? " > " : " ", checkbox, " ", item.label] }, `checkbox-${actualIndex}`));
139
+ }), _jsx(Text, { color: "gray", dimColor: true, children: " Space toggle · Enter confirm · Esc cancel" })] }));
140
+ }
141
+ export const InputPanel = React.forwardRef(function InputPanel({ onSubmit, disabled, commandRegistry, store, hint = null, onHintRequested, session: sessionProp }, ref) {
142
+ const [value, setValue] = useState("");
143
+ const [cursor, setCursor] = useState(0);
144
+ const [overlay, setOverlay] = useState(EMPTY_OVERLAY);
145
+ const [picker, setPicker] = useState(null);
146
+ const [checkboxPicker, setCheckboxPicker] = useState(null);
147
+ const valueRef = useRef("");
148
+ const cursorRef = useRef(0);
149
+ const preferredColumnRef = useRef(null);
150
+ const parserRef = useRef(new InputProtocolParser());
151
+ const decoderRef = useRef(new StringDecoder("utf8"));
152
+ const turnPasteCounterRef = useRef(new TurnPasteCounter());
153
+ const turnPasteSlotsRef = useRef(new TurnPasteSlotStore());
154
+ const { stdin } = useStdin();
155
+ valueRef.current = value;
156
+ cursorRef.current = cursor;
157
+ const hideOverlay = useCallback(() => {
158
+ setOverlay(EMPTY_OVERLAY);
159
+ }, []);
160
+ const hidePicker = useCallback(() => {
161
+ setPicker(null);
162
+ setCheckboxPicker(null);
163
+ }, []);
164
+ const buildCommandOptions = useCallback((cmdName) => {
165
+ const cmd = commandRegistry.lookup(cmdName);
166
+ if (!cmd?.options)
167
+ return [];
168
+ return cmd.options({
169
+ session: sessionProp,
170
+ store: store ?? undefined,
171
+ });
172
+ }, [commandRegistry, sessionProp, store]);
173
+ const startCommandPicker = useCallback((cmdName) => {
174
+ const cmd = commandRegistry.lookup(cmdName);
175
+ const options = buildCommandOptions(cmdName);
176
+ if (options.length === 0)
177
+ return false;
178
+ hideOverlay();
179
+ if (cmd?.checkboxMode) {
180
+ // Use checkbox picker for multi-select commands like /skills
181
+ const items = options.map((o) => ({
182
+ label: o.label,
183
+ value: o.value,
184
+ checked: o.checked !== false,
185
+ }));
186
+ setCheckboxPicker(createCheckboxPicker(cmdName, items, Math.min(items.length, 15)));
187
+ return true;
188
+ }
189
+ setPicker(createCommandPicker(cmdName, options, cmdName === "/resume" ? RESUME_PICKER_MAX_VISIBLE : options.length));
190
+ return true;
191
+ }, [commandRegistry, buildCommandOptions, hideOverlay]);
192
+ // ----- Build overlay items for /command prefix ----- //
193
+ const showCommandOverlay = useCallback((prefix) => {
194
+ const commands = commandRegistry.getAll();
195
+ const items = [];
196
+ const values = [];
197
+ for (const cmd of commands) {
198
+ if (cmd.name.slice(1).startsWith(prefix)) {
199
+ items.push(`${cmd.name} ${cmd.description}`);
200
+ values.push(cmd.name);
201
+ }
202
+ }
203
+ if (items.length > 0) {
204
+ setOverlay({ visible: true, mode: "command", items, values, selected: 0 });
205
+ }
206
+ else {
207
+ hideOverlay();
208
+ }
209
+ }, [commandRegistry, hideOverlay]);
210
+ // ----- Build overlay items for @file prefix ----- //
211
+ const showFileOverlay = useCallback((prefix) => {
212
+ const candidates = scanCandidates(prefix);
213
+ if (candidates.length > 0) {
214
+ setOverlay({
215
+ visible: true,
216
+ mode: "file",
217
+ items: candidates,
218
+ values: candidates,
219
+ selected: 0,
220
+ });
221
+ }
222
+ else {
223
+ hideOverlay();
224
+ }
225
+ }, [hideOverlay]);
226
+ const updateOverlayForInput = useCallback((nextValue, nextCursor) => {
227
+ const beforeCursor = nextValue.slice(0, nextCursor);
228
+ // Check / command prefix first
229
+ if (beforeCursor.startsWith("/") && !beforeCursor.includes("\n")) {
230
+ const prefix = beforeCursor.slice(1);
231
+ if (!prefix.includes(" ")) {
232
+ showCommandOverlay(prefix);
233
+ return;
234
+ }
235
+ }
236
+ // Check @ file reference before cursor
237
+ const atIdx = beforeCursor.lastIndexOf("@");
238
+ if (atIdx >= 0) {
239
+ const before = atIdx === 0 ? "" : beforeCursor[atIdx - 1];
240
+ if (atIdx === 0 || before === " " || before === "\t" || before === "\n") {
241
+ const prefix = beforeCursor.slice(atIdx + 1);
242
+ if (!/\s/.test(prefix)) {
243
+ showFileOverlay(prefix);
244
+ return;
245
+ }
246
+ }
247
+ }
248
+ hideOverlay();
249
+ }, [showCommandOverlay, showFileOverlay, hideOverlay]);
250
+ const commitEditorState = useCallback((nextValue, nextCursor, preferredColumn) => {
251
+ const safeCursor = clamp(nextCursor, 0, nextValue.length);
252
+ turnPasteSlotsRef.current.prune(nextValue);
253
+ valueRef.current = nextValue;
254
+ cursorRef.current = safeCursor;
255
+ preferredColumnRef.current = preferredColumn;
256
+ setValue(nextValue);
257
+ setCursor(safeCursor);
258
+ updateOverlayForInput(nextValue, safeCursor);
259
+ }, [updateOverlayForInput]);
260
+ const resetTurnPasteState = useCallback(() => {
261
+ turnPasteCounterRef.current.reset();
262
+ turnPasteSlotsRef.current.reset();
263
+ }, []);
264
+ const clearInput = useCallback(() => {
265
+ parserRef.current.reset();
266
+ commitEditorState("", 0, null);
267
+ setOverlay(EMPTY_OVERLAY);
268
+ }, [commitEditorState]);
269
+ const dismissOverlay = useCallback(() => {
270
+ if (checkboxPicker) {
271
+ hidePicker();
272
+ return true;
273
+ }
274
+ if (picker) {
275
+ hidePicker();
276
+ return true;
277
+ }
278
+ if (!overlay.visible)
279
+ return false;
280
+ hideOverlay();
281
+ return true;
282
+ }, [checkboxPicker, picker, overlay.visible, hideOverlay, hidePicker]);
283
+ React.useImperativeHandle(ref, () => ({
284
+ clear: clearInput,
285
+ getValue: () => valueRef.current,
286
+ resetTurnPasteCounter: () => {
287
+ resetTurnPasteState();
288
+ },
289
+ dismissOverlay,
290
+ }), [clearInput, resetTurnPasteState, dismissOverlay]);
291
+ const maybeStartPickerFromSubmittedText = useCallback((submitted) => {
292
+ const trimmed = submitted.trim();
293
+ if (!trimmed.startsWith("/") || /\s/.test(trimmed))
294
+ return false;
295
+ const cmd = commandRegistry.lookup(trimmed);
296
+ if (!cmd?.options)
297
+ return false;
298
+ const started = startCommandPicker(trimmed);
299
+ if (!started)
300
+ return false;
301
+ clearInput();
302
+ resetTurnPasteState();
303
+ return true;
304
+ }, [commandRegistry, startCommandPicker, clearInput, resetTurnPasteState]);
305
+ const acceptOverlaySelection = useCallback(() => {
306
+ const sel = overlay.values[overlay.selected];
307
+ if (!sel) {
308
+ hideOverlay();
309
+ return;
310
+ }
311
+ if (overlay.mode === "file") {
312
+ const currentValue = valueRef.current;
313
+ const currentCursor = cursorRef.current;
314
+ const atIdx = currentValue.slice(0, currentCursor).lastIndexOf("@");
315
+ if (atIdx < 0) {
316
+ hideOverlay();
317
+ return;
318
+ }
319
+ let tokenEnd = currentCursor;
320
+ while (tokenEnd < currentValue.length && !/\s/.test(currentValue[tokenEnd])) {
321
+ tokenEnd += 1;
322
+ }
323
+ const replacement = sel.includes(" ") ? `@"${sel}" ` : `@${sel} `;
324
+ const newVal = currentValue.slice(0, atIdx) + replacement + currentValue.slice(tokenEnd);
325
+ commitEditorState(newVal, atIdx + replacement.length, null);
326
+ hideOverlay();
327
+ }
328
+ else if (overlay.mode === "command") {
329
+ hideOverlay();
330
+ const cmd = commandRegistry.lookup(sel);
331
+ if (cmd?.options && startCommandPicker(sel)) {
332
+ clearInput();
333
+ resetTurnPasteState();
334
+ return;
335
+ }
336
+ const accepted = onSubmit(sel);
337
+ if (accepted)
338
+ clearInput();
339
+ }
340
+ }, [
341
+ overlay,
342
+ hideOverlay,
343
+ onSubmit,
344
+ commitEditorState,
345
+ clearInput,
346
+ resetTurnPasteState,
347
+ startCommandPicker,
348
+ commandRegistry,
349
+ ]);
350
+ const completeCommandSelection = useCallback(() => {
351
+ const sel = overlay.values[overlay.selected];
352
+ if (!sel || overlay.mode !== "command") {
353
+ hideOverlay();
354
+ return;
355
+ }
356
+ const currentValue = valueRef.current;
357
+ const currentCursor = cursorRef.current;
358
+ let tokenEnd = currentCursor;
359
+ while (tokenEnd < currentValue.length && !/\s/.test(currentValue[tokenEnd])) {
360
+ tokenEnd += 1;
361
+ }
362
+ const replacement = `${sel} `;
363
+ const nextValue = replacement + currentValue.slice(tokenEnd);
364
+ commitEditorState(nextValue, replacement.length, null);
365
+ }, [overlay, commitEditorState, hideOverlay]);
366
+ const acceptPickerSelection = useCallback(() => {
367
+ if (!picker)
368
+ return;
369
+ const result = acceptCommandPickerSelection(picker);
370
+ if (!result) {
371
+ hidePicker();
372
+ return;
373
+ }
374
+ if (result.kind === "drill_down") {
375
+ setPicker(result.picker);
376
+ return;
377
+ }
378
+ hidePicker();
379
+ const accepted = onSubmit(result.command);
380
+ if (accepted)
381
+ clearInput();
382
+ }, [picker, onSubmit, clearInput, hidePicker]);
383
+ const applyCommand = useCallback((command) => {
384
+ const state = withValueAndCursor(valueRef.current, cursorRef.current, preferredColumnRef.current);
385
+ switch (command) {
386
+ case "submit": {
387
+ const expanded = turnPasteSlotsRef.current.expand(valueRef.current);
388
+ const safe = sanitizeSubmitText(expanded).trim();
389
+ if (!safe)
390
+ return;
391
+ if (maybeStartPickerFromSubmittedText(safe))
392
+ return;
393
+ const accepted = onSubmit(safe);
394
+ if (!accepted)
395
+ return;
396
+ clearInput();
397
+ resetTurnPasteState();
398
+ return;
399
+ }
400
+ case "newline": {
401
+ const next = insertText(state, "\n");
402
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
403
+ return;
404
+ }
405
+ case "move_left": {
406
+ const next = moveLeft(state);
407
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
408
+ return;
409
+ }
410
+ case "move_right": {
411
+ const next = moveRight(state);
412
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
413
+ return;
414
+ }
415
+ case "move_up": {
416
+ const next = moveUp(state);
417
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
418
+ return;
419
+ }
420
+ case "move_word_left": {
421
+ const next = moveWordLeft(state);
422
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
423
+ return;
424
+ }
425
+ case "move_word_right": {
426
+ const next = moveWordRight(state);
427
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
428
+ return;
429
+ }
430
+ case "move_down": {
431
+ const next = moveDown(state);
432
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
433
+ return;
434
+ }
435
+ case "move_home": {
436
+ const next = moveHome(state);
437
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
438
+ return;
439
+ }
440
+ case "move_end": {
441
+ const next = moveEnd(state);
442
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
443
+ return;
444
+ }
445
+ case "delete_backward": {
446
+ const next = deleteBackward(state);
447
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
448
+ return;
449
+ }
450
+ case "delete_forward": {
451
+ const next = deleteForward(state);
452
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
453
+ return;
454
+ }
455
+ case "delete_word_backward": {
456
+ const next = deleteWordBackward(state);
457
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
458
+ return;
459
+ }
460
+ case "delete_word_forward": {
461
+ const next = deleteWordForward(state);
462
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
463
+ return;
464
+ }
465
+ case "delete_to_line_start": {
466
+ const next = deleteToLineStart(state);
467
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
468
+ return;
469
+ }
470
+ case "delete_to_line_end": {
471
+ const next = deleteToLineEnd(state);
472
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
473
+ return;
474
+ }
475
+ case "overlay_hide": {
476
+ if (checkboxPicker) {
477
+ hidePicker();
478
+ return;
479
+ }
480
+ if (picker) {
481
+ const nextPicker = exitCommandPickerLevel(picker);
482
+ if (nextPicker) {
483
+ setPicker(nextPicker);
484
+ }
485
+ else {
486
+ hidePicker();
487
+ }
488
+ return;
489
+ }
490
+ hideOverlay();
491
+ return;
492
+ }
493
+ case "overlay_next":
494
+ case "overlay_prev":
495
+ case "overlay_accept": {
496
+ // Handled before command mapping when overlay is visible.
497
+ return;
498
+ }
499
+ }
500
+ }, [
501
+ onSubmit,
502
+ clearInput,
503
+ commitEditorState,
504
+ hideOverlay,
505
+ hidePicker,
506
+ maybeStartPickerFromSubmittedText,
507
+ picker,
508
+ checkboxPicker,
509
+ resetTurnPasteState,
510
+ ]);
511
+ const handleInsert = useCallback((rawText, source) => {
512
+ if (checkboxPicker || picker) {
513
+ onHintRequested?.("Picker is active. Press Esc to go back, Ctrl+C to close.", 2500);
514
+ return;
515
+ }
516
+ const safeText = sanitizeInputText(rawText);
517
+ if (!safeText)
518
+ return;
519
+ const shouldTreatAsPaste = source === "paste" || safeText.includes("\n");
520
+ let textToInsert = safeText;
521
+ if (shouldTreatAsPaste) {
522
+ const decision = classifyPastedText(safeText, turnPasteCounterRef.current);
523
+ if (decision.replacedWithPlaceholder && decision.index !== undefined) {
524
+ const marker = turnPasteSlotsRef.current.create(safeText, decision.index, decision.lineCount);
525
+ textToInsert = marker ?? safeText;
526
+ }
527
+ else {
528
+ textToInsert = decision.text;
529
+ }
530
+ }
531
+ const state = withValueAndCursor(valueRef.current, cursorRef.current, preferredColumnRef.current);
532
+ const next = insertText(state, textToInsert);
533
+ commitEditorState(next.value, next.cursor, next.preferredColumn);
534
+ }, [commitEditorState, onHintRequested, picker, checkboxPicker]);
535
+ const handleInputEvent = useCallback((event) => {
536
+ if (disabled)
537
+ return;
538
+ // Checkbox picker takes priority
539
+ if (checkboxPicker) {
540
+ if (event.type === "insert") {
541
+ // Space toggles the current item
542
+ if (event.text === " ") {
543
+ setCheckboxPicker((prev) => (prev ? toggleCheckboxItem(prev) : prev));
544
+ }
545
+ return;
546
+ }
547
+ if (event.key === "escape") {
548
+ hidePicker();
549
+ return;
550
+ }
551
+ if (event.key === "up" || (event.key === "tab" && event.shift)) {
552
+ setCheckboxPicker((prev) => (prev ? moveCheckboxSelection(prev, -1) : prev));
553
+ return;
554
+ }
555
+ if (event.key === "down" || event.key === "tab") {
556
+ setCheckboxPicker((prev) => (prev ? moveCheckboxSelection(prev, 1) : prev));
557
+ return;
558
+ }
559
+ if (event.key === "enter") {
560
+ // Submit: collect checked items and invoke command
561
+ const enabledValues = checkboxPicker.items
562
+ .filter((it) => it.checked)
563
+ .map((it) => it.value)
564
+ .join(",");
565
+ const cmdStr = `${checkboxPicker.title} ${enabledValues}`.trim();
566
+ hidePicker();
567
+ const accepted = onSubmit(cmdStr);
568
+ if (accepted)
569
+ clearInput();
570
+ return;
571
+ }
572
+ const command = mapInputEventToCommand(event);
573
+ if (command === "overlay_hide") {
574
+ hidePicker();
575
+ }
576
+ return;
577
+ }
578
+ if (picker) {
579
+ if (event.type === "insert") {
580
+ handleInsert(event.text, event.source);
581
+ return;
582
+ }
583
+ if (event.key === "escape") {
584
+ const nextPicker = exitCommandPickerLevel(picker);
585
+ if (nextPicker) {
586
+ setPicker(nextPicker);
587
+ }
588
+ else {
589
+ hidePicker();
590
+ }
591
+ return;
592
+ }
593
+ if (event.key === "up") {
594
+ setPicker((prev) => (prev ? moveCommandPickerSelection(prev, -1) : prev));
595
+ return;
596
+ }
597
+ if (event.key === "tab" && event.shift) {
598
+ setPicker((prev) => (prev ? moveCommandPickerSelection(prev, -1) : prev));
599
+ return;
600
+ }
601
+ if (event.key === "down" || event.key === "tab") {
602
+ setPicker((prev) => (prev ? moveCommandPickerSelection(prev, 1) : prev));
603
+ return;
604
+ }
605
+ if (event.key === "enter") {
606
+ acceptPickerSelection();
607
+ return;
608
+ }
609
+ const command = mapInputEventToCommand(event);
610
+ if (command === "overlay_hide") {
611
+ const nextPicker = exitCommandPickerLevel(picker);
612
+ if (nextPicker) {
613
+ setPicker(nextPicker);
614
+ }
615
+ else {
616
+ hidePicker();
617
+ }
618
+ return;
619
+ }
620
+ return;
621
+ }
622
+ if (event.type === "insert") {
623
+ handleInsert(event.text, event.source);
624
+ return;
625
+ }
626
+ // Overlay keyboard handling
627
+ if (overlay.visible) {
628
+ if (event.key === "escape") {
629
+ hideOverlay();
630
+ return;
631
+ }
632
+ if (overlay.mode === "command" && event.key === "tab" && !event.shift) {
633
+ completeCommandSelection();
634
+ return;
635
+ }
636
+ if (overlay.mode === "file" && event.key === "tab" && !event.shift) {
637
+ acceptOverlaySelection();
638
+ return;
639
+ }
640
+ if (event.key === "up") {
641
+ setOverlay((prev) => ({
642
+ ...prev,
643
+ selected: (prev.selected - 1 + prev.items.length) % prev.items.length,
644
+ }));
645
+ return;
646
+ }
647
+ if (event.key === "tab" && event.shift) {
648
+ setOverlay((prev) => ({
649
+ ...prev,
650
+ selected: (prev.selected - 1 + prev.items.length) % prev.items.length,
651
+ }));
652
+ return;
653
+ }
654
+ if (event.key === "down" || event.key === "tab") {
655
+ setOverlay((prev) => ({
656
+ ...prev,
657
+ selected: (prev.selected + 1) % prev.items.length,
658
+ }));
659
+ return;
660
+ }
661
+ if (event.key === "enter") {
662
+ acceptOverlaySelection();
663
+ return;
664
+ }
665
+ }
666
+ const command = mapInputEventToCommand(event);
667
+ if (!command)
668
+ return;
669
+ // Overlay actions do nothing when overlay is hidden.
670
+ if (!overlay.visible && command === "overlay_next")
671
+ return;
672
+ applyCommand(command);
673
+ }, [
674
+ disabled,
675
+ picker,
676
+ checkboxPicker,
677
+ overlay.visible,
678
+ overlay.mode,
679
+ acceptPickerSelection,
680
+ acceptOverlaySelection,
681
+ applyCommand,
682
+ handleInsert,
683
+ hideOverlay,
684
+ hidePicker,
685
+ clearInput,
686
+ onSubmit,
687
+ completeCommandSelection,
688
+ onHintRequested,
689
+ ]);
690
+ useEffect(() => {
691
+ if (!stdin || disabled)
692
+ return;
693
+ const onData = (data) => {
694
+ const chunk = typeof data === "string" ? data : decoderRef.current.write(data);
695
+ const events = parserRef.current.push(chunk);
696
+ for (const event of events) {
697
+ handleInputEvent(event);
698
+ }
699
+ };
700
+ stdin.on("data", onData);
701
+ return () => {
702
+ const tail = decoderRef.current.end();
703
+ if (tail.length > 0) {
704
+ const events = parserRef.current.push(tail);
705
+ for (const event of events) {
706
+ handleInputEvent(event);
707
+ }
708
+ }
709
+ stdin.off("data", onData);
710
+ };
711
+ }, [stdin, disabled, handleInputEvent]);
712
+ const viewport = sliceInputViewport(value, cursor, INPUT_VIEWPORT_MAX_LINES);
713
+ const renderedInput = renderValueWithCursor(viewport.text, viewport.cursor, !disabled && !picker && !checkboxPicker, turnPasteSlotsRef.current).replaceAll("\n", `\n${PROMPT_INDENT}`);
714
+ const pickerHint = checkboxPicker
715
+ ? " Space toggle · Enter confirm · Esc cancel"
716
+ : picker
717
+ ? " Enter select · Esc back/close · Ctrl+C close"
718
+ : null;
719
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 2, children: [checkboxPicker ? _jsx(CheckboxPickerView, { picker: checkboxPicker }) : picker ? _jsx(CommandPickerView, { picker: picker }) : _jsx(OverlayView, { state: overlay }), _jsx(Box, { borderStyle: "single", borderTop: true, borderBottom: true, borderLeft: false, borderRight: false, children: _jsx(Box, { paddingX: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: theme.accent, children: PROMPT }), _jsx(Text, { children: renderedInput })] }) }) }), _jsx(Box, { paddingX: 1, children: _jsx(Text, { color: "gray", children: pickerHint
720
+ ? pickerHint
721
+ : hint
722
+ ? ` ${hint}`
723
+ : " Opt+Enter/^N newline · ^G Markdown raw · ^C Cancel/Quit" }) })] }));
724
+ });
725
+ //# sourceMappingURL=input-panel.js.map