@within-7/minto 0.1.5 → 0.1.6

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 (264) hide show
  1. package/dist/commands/agents/AgentsCommand.js +2342 -0
  2. package/dist/commands/agents/AgentsCommand.js.map +7 -0
  3. package/dist/commands/agents/constants.js +58 -0
  4. package/dist/commands/agents/constants.js.map +7 -0
  5. package/dist/commands/agents/index.js +37 -0
  6. package/dist/commands/agents/index.js.map +7 -0
  7. package/dist/commands/agents/types.js +10 -0
  8. package/dist/commands/agents/types.js.map +7 -0
  9. package/dist/commands/agents/utils/fileOperations.js +185 -0
  10. package/dist/commands/agents/utils/fileOperations.js.map +7 -0
  11. package/dist/commands/agents/utils/index.js +21 -0
  12. package/dist/commands/agents/utils/index.js.map +7 -0
  13. package/dist/commands/bug.js +2 -2
  14. package/dist/commands/bug.js.map +2 -2
  15. package/dist/commands/compact.js +5 -5
  16. package/dist/commands/compact.js.map +2 -2
  17. package/dist/commands/ctx_viz.js +55 -22
  18. package/dist/commands/ctx_viz.js.map +2 -2
  19. package/dist/commands/mcp-interactive.js +11 -11
  20. package/dist/commands/mcp-interactive.js.map +2 -2
  21. package/dist/commands/model.js +94 -32
  22. package/dist/commands/model.js.map +3 -3
  23. package/dist/commands/plugin/AddMarketplaceForm.js +49 -21
  24. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  25. package/dist/commands/plugin/ConfirmDialog.js +38 -26
  26. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  27. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +24 -8
  28. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  29. package/dist/commands/plugin/InstalledPluginsManager.js +3 -1
  30. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  31. package/dist/commands/plugin/MainMenu.js +16 -7
  32. package/dist/commands/plugin/MainMenu.js.map +2 -2
  33. package/dist/commands/plugin/MarketplaceManager.js +84 -39
  34. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  35. package/dist/commands/plugin/MarketplaceSelector.js +7 -3
  36. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  37. package/dist/commands/plugin/PlaceholderScreen.js +16 -2
  38. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  39. package/dist/commands/plugin/PluginBrowser.js +4 -2
  40. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  41. package/dist/commands/plugin/PluginDetailsInstall.js +12 -6
  42. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  43. package/dist/commands/plugin/PluginDetailsManage.js +14 -5
  44. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  45. package/dist/commands/plugin/example-usage.js.map +2 -2
  46. package/dist/commands/plugin/utils.js.map +2 -2
  47. package/dist/commands/plugin.js +226 -46
  48. package/dist/commands/plugin.js.map +2 -2
  49. package/dist/commands/refreshCommands.js +6 -3
  50. package/dist/commands/refreshCommands.js.map +2 -2
  51. package/dist/commands/resume.js +2 -1
  52. package/dist/commands/resume.js.map +2 -2
  53. package/dist/commands/setup.js +19 -5
  54. package/dist/commands/setup.js.map +2 -2
  55. package/dist/commands/terminalSetup.js +2 -2
  56. package/dist/commands/terminalSetup.js.map +1 -1
  57. package/dist/commands.js +14 -30
  58. package/dist/commands.js.map +2 -2
  59. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  60. package/dist/components/AskUserQuestionDialog/QuestionView.js +10 -1
  61. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  62. package/dist/components/BackgroundTasksPanel.js +5 -1
  63. package/dist/components/BackgroundTasksPanel.js.map +2 -2
  64. package/dist/components/Config.js +17 -4
  65. package/dist/components/Config.js.map +2 -2
  66. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  67. package/dist/components/CustomSelect/select-option.js +4 -1
  68. package/dist/components/CustomSelect/select-option.js.map +2 -2
  69. package/dist/components/Help.js +6 -8
  70. package/dist/components/Help.js.map +2 -2
  71. package/dist/components/Logo.js +1 -1
  72. package/dist/components/Logo.js.map +2 -2
  73. package/dist/components/ModelListManager.js.map +2 -2
  74. package/dist/components/ModelSelector/ModelSelector.js +2030 -0
  75. package/dist/components/ModelSelector/ModelSelector.js.map +7 -0
  76. package/dist/components/ModelSelector/ScreenContainer.js +27 -0
  77. package/dist/components/ModelSelector/ScreenContainer.js.map +7 -0
  78. package/dist/components/ModelSelector/constants.js +37 -0
  79. package/dist/components/ModelSelector/constants.js.map +7 -0
  80. package/dist/components/ModelSelector/hooks/index.js +5 -0
  81. package/dist/components/ModelSelector/hooks/index.js.map +7 -0
  82. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js +21 -0
  83. package/dist/components/ModelSelector/hooks/useEscapeNavigation.js.map +7 -0
  84. package/dist/components/ModelSelector/index.js +17 -0
  85. package/dist/components/ModelSelector/index.js.map +7 -0
  86. package/dist/components/ModelSelector/types.js +1 -0
  87. package/dist/components/ModelSelector/types.js.map +7 -0
  88. package/dist/components/PressEnterToContinue.js +1 -1
  89. package/dist/components/PressEnterToContinue.js.map +2 -2
  90. package/dist/components/ProjectOnboarding.js +1 -1
  91. package/dist/components/ProjectOnboarding.js.map +2 -2
  92. package/dist/components/PromptInput.js +88 -37
  93. package/dist/components/PromptInput.js.map +2 -2
  94. package/dist/components/QuitSummary.js +17 -10
  95. package/dist/components/QuitSummary.js.map +2 -2
  96. package/dist/components/SentryErrorBoundary.js.map +2 -2
  97. package/dist/components/StreamingBashOutput.js.map +2 -2
  98. package/dist/components/StructuredDiff.js.map +2 -2
  99. package/dist/components/SubagentProgress.js.map +2 -2
  100. package/dist/components/TaskCard.js.map +2 -2
  101. package/dist/components/TextInput.js.map +1 -1
  102. package/dist/components/TodoItem.js.map +1 -1
  103. package/dist/components/binary-feedback/BinaryFeedbackOption.js +1 -3
  104. package/dist/components/binary-feedback/BinaryFeedbackOption.js.map +2 -2
  105. package/dist/components/messages/AssistantLocalCommandOutputMessage.js.map +1 -1
  106. package/dist/components/messages/AssistantToolUseMessage.js +3 -1
  107. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  108. package/dist/components/messages/TaskProgressMessage.js.map +2 -2
  109. package/dist/components/messages/TaskToolMessage.js.map +2 -2
  110. package/dist/components/messages/UserToolResultMessage/utils.js.map +2 -2
  111. package/dist/components/permissions/FileEditPermissionRequest/FileEditToolDiff.js.map +2 -2
  112. package/dist/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.js.map +2 -2
  113. package/dist/components/permissions/hooks.js.map +2 -2
  114. package/dist/constants/modelCapabilities.js +1 -1
  115. package/dist/constants/modelCapabilities.js.map +2 -2
  116. package/dist/constants/prompts.js.map +1 -1
  117. package/dist/constants/timing.js +34 -0
  118. package/dist/constants/timing.js.map +7 -0
  119. package/dist/entrypoints/cli.js +128 -33
  120. package/dist/entrypoints/cli.js.map +3 -3
  121. package/dist/entrypoints/mcp.js +13 -18
  122. package/dist/entrypoints/mcp.js.map +2 -2
  123. package/dist/hooks/useCanUseTool.js.map +2 -2
  124. package/dist/hooks/useCancelRequest.js.map +1 -1
  125. package/dist/hooks/useHistorySearch.js.map +2 -2
  126. package/dist/hooks/useLogStartupTime.js.map +2 -2
  127. package/dist/hooks/usePermissionRequestLogging.js.map +2 -2
  128. package/dist/hooks/useTextInput.js.map +1 -1
  129. package/dist/hooks/useUnifiedCompletion.js +493 -394
  130. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  131. package/dist/index.js.map +2 -2
  132. package/dist/permissions.js +4 -7
  133. package/dist/permissions.js.map +2 -2
  134. package/dist/query.js +6 -1
  135. package/dist/query.js.map +2 -2
  136. package/dist/screens/REPL.js +72 -36
  137. package/dist/screens/REPL.js.map +2 -2
  138. package/dist/screens/ResumeConversation.js +2 -1
  139. package/dist/screens/ResumeConversation.js.map +2 -2
  140. package/dist/services/adapters/base.js.map +2 -2
  141. package/dist/services/adapters/chatCompletions.js.map +2 -2
  142. package/dist/services/adapters/responsesAPI.js +3 -1
  143. package/dist/services/adapters/responsesAPI.js.map +2 -2
  144. package/dist/services/claude.js +327 -328
  145. package/dist/services/claude.js.map +2 -2
  146. package/dist/services/customCommands.js +6 -1
  147. package/dist/services/customCommands.js.map +2 -2
  148. package/dist/services/fileFreshness.js.map +2 -2
  149. package/dist/services/gpt5ConnectionTest.js +20 -7
  150. package/dist/services/gpt5ConnectionTest.js.map +2 -2
  151. package/dist/services/hookExecutor.js +6 -12
  152. package/dist/services/hookExecutor.js.map +2 -2
  153. package/dist/services/mcpClient.js +29 -2
  154. package/dist/services/mcpClient.js.map +2 -2
  155. package/dist/services/mentionProcessor.js +23 -10
  156. package/dist/services/mentionProcessor.js.map +2 -2
  157. package/dist/services/modelAdapterFactory.js.map +2 -2
  158. package/dist/services/oauth.js.map +2 -2
  159. package/dist/services/openai.js +109 -72
  160. package/dist/services/openai.js.map +3 -3
  161. package/dist/services/responseStateManager.js.map +2 -2
  162. package/dist/services/systemReminder.js.map +2 -2
  163. package/dist/tools/ArchitectTool/ArchitectTool.js.map +1 -1
  164. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +14 -8
  165. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  166. package/dist/tools/BashOutputTool/BashOutputTool.js.map +2 -2
  167. package/dist/tools/BashTool/BashTool.js.map +2 -2
  168. package/dist/tools/FileReadTool/FileReadTool.js.map +1 -1
  169. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  170. package/dist/tools/GrepTool/GrepTool.js +1 -4
  171. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  172. package/dist/tools/MultiEditTool/MultiEditTool.js +4 -1
  173. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  174. package/dist/tools/NotebookReadTool/NotebookReadTool.js +3 -1
  175. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  176. package/dist/tools/SkillTool/SkillTool.js +12 -6
  177. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  178. package/dist/tools/TaskTool/TaskTool.js +14 -5
  179. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  180. package/dist/tools/TaskTool/prompt.js.map +2 -2
  181. package/dist/tools/ThinkTool/ThinkTool.js +6 -1
  182. package/dist/tools/ThinkTool/ThinkTool.js.map +2 -2
  183. package/dist/tools/TodoWriteTool/TodoWriteTool.js +23 -3
  184. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  185. package/dist/tools/URLFetcherTool/URLFetcherTool.js +2 -2
  186. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  187. package/dist/tools/URLFetcherTool/cache.js +6 -3
  188. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  189. package/dist/tools/URLFetcherTool/htmlToMarkdown.js +3 -1
  190. package/dist/tools/URLFetcherTool/htmlToMarkdown.js.map +2 -2
  191. package/dist/tools/WebSearchTool/WebSearchTool.js.map +2 -2
  192. package/dist/tools/WebSearchTool/prompt.js.map +2 -2
  193. package/dist/tools/WebSearchTool/searchProviders.js +15 -6
  194. package/dist/tools/WebSearchTool/searchProviders.js.map +2 -2
  195. package/dist/tools.js +4 -1
  196. package/dist/tools.js.map +2 -2
  197. package/dist/types/core.js +1 -0
  198. package/dist/types/core.js.map +7 -0
  199. package/dist/types/hooks.js +1 -4
  200. package/dist/types/hooks.js.map +2 -2
  201. package/dist/types/marketplace.js +8 -2
  202. package/dist/types/marketplace.js.map +2 -2
  203. package/dist/types/plugin.js +9 -6
  204. package/dist/types/plugin.js.map +2 -2
  205. package/dist/utils/BackgroundShellManager.js +76 -10
  206. package/dist/utils/BackgroundShellManager.js.map +2 -2
  207. package/dist/utils/PersistentShell.js +7 -2
  208. package/dist/utils/PersistentShell.js.map +2 -2
  209. package/dist/utils/advancedFuzzyMatcher.js +4 -1
  210. package/dist/utils/advancedFuzzyMatcher.js.map +2 -2
  211. package/dist/utils/agentLoader.js +69 -35
  212. package/dist/utils/agentLoader.js.map +2 -2
  213. package/dist/utils/agentStorage.js.map +2 -2
  214. package/dist/utils/async.js +163 -0
  215. package/dist/utils/async.js.map +7 -0
  216. package/dist/utils/autoUpdater.js +8 -2
  217. package/dist/utils/autoUpdater.js.map +2 -2
  218. package/dist/utils/commands.js +23 -11
  219. package/dist/utils/commands.js.map +2 -2
  220. package/dist/utils/commonUnixCommands.js +3 -1
  221. package/dist/utils/commonUnixCommands.js.map +2 -2
  222. package/dist/utils/compressionMode.js.map +2 -2
  223. package/dist/utils/config.js +30 -14
  224. package/dist/utils/config.js.map +2 -2
  225. package/dist/utils/debugLogger.js.map +2 -2
  226. package/dist/utils/env.js.map +2 -2
  227. package/dist/utils/envConfig.js +82 -0
  228. package/dist/utils/envConfig.js.map +7 -0
  229. package/dist/utils/errorHandling.js +89 -0
  230. package/dist/utils/errorHandling.js.map +7 -0
  231. package/dist/utils/expertChatStorage.js.map +2 -2
  232. package/dist/utils/fuzzyMatcher.js +13 -7
  233. package/dist/utils/fuzzyMatcher.js.map +2 -2
  234. package/dist/utils/hookManager.js +14 -4
  235. package/dist/utils/hookManager.js.map +2 -2
  236. package/dist/utils/log.js.map +2 -2
  237. package/dist/utils/marketplaceManager.js +44 -9
  238. package/dist/utils/marketplaceManager.js.map +2 -2
  239. package/dist/utils/messageContextManager.js.map +1 -1
  240. package/dist/utils/messages.js +6 -3
  241. package/dist/utils/messages.js.map +2 -2
  242. package/dist/utils/model.js +3 -1
  243. package/dist/utils/model.js.map +2 -2
  244. package/dist/utils/pluginInstaller.js +3 -15
  245. package/dist/utils/pluginInstaller.js.map +2 -2
  246. package/dist/utils/pluginLoader.js +41 -13
  247. package/dist/utils/pluginLoader.js.map +2 -2
  248. package/dist/utils/pluginRegistry.js.map +2 -2
  249. package/dist/utils/pluginValidator.js +71 -49
  250. package/dist/utils/pluginValidator.js.map +2 -2
  251. package/dist/utils/ptyCompat.js.map +2 -2
  252. package/dist/utils/roundConverter.js.map +2 -2
  253. package/dist/utils/secureFile.js +43 -14
  254. package/dist/utils/secureFile.js.map +2 -2
  255. package/dist/utils/sessionState.js.map +2 -2
  256. package/dist/utils/skillLoader.js.map +2 -2
  257. package/dist/utils/teamConfig.js +7 -4
  258. package/dist/utils/teamConfig.js.map +2 -2
  259. package/dist/utils/theme.js.map +2 -2
  260. package/dist/utils/thinking.js.map +2 -2
  261. package/dist/utils/unaryLogging.js.map +2 -2
  262. package/dist/version.js +2 -2
  263. package/dist/version.js.map +1 -1
  264. package/package.json +5 -5
@@ -44,33 +44,39 @@ function useUnifiedCompletion({
44
44
  emptyDirMessage: ""
45
45
  }));
46
46
  }, []);
47
- const activateCompletion = useCallback((suggestions2, context) => {
48
- setState((prev) => ({
49
- ...prev,
50
- suggestions: suggestions2,
51
- // Keep the order from generateSuggestions (already sorted with weights)
52
- selectedIndex: 0,
53
- isActive: true,
54
- context,
55
- preview: null
56
- }));
57
- }, []);
47
+ const activateCompletion = useCallback(
48
+ (suggestions2, context) => {
49
+ setState((prev) => ({
50
+ ...prev,
51
+ suggestions: suggestions2,
52
+ // Keep the order from generateSuggestions (already sorted with weights)
53
+ selectedIndex: 0,
54
+ isActive: true,
55
+ context,
56
+ preview: null
57
+ }));
58
+ },
59
+ []
60
+ );
58
61
  const { suggestions, selectedIndex, isActive, emptyDirMessage } = state;
59
- const findCommonPrefix = useCallback((suggestions2) => {
60
- if (suggestions2.length === 0) return "";
61
- if (suggestions2.length === 1) return suggestions2[0].value;
62
- let prefix = suggestions2[0].value;
63
- for (let i = 1; i < suggestions2.length; i++) {
64
- const str = suggestions2[i].value;
65
- let j = 0;
66
- while (j < prefix.length && j < str.length && prefix[j] === str[j]) {
67
- j++;
62
+ const findCommonPrefix = useCallback(
63
+ (suggestions2) => {
64
+ if (suggestions2.length === 0) return "";
65
+ if (suggestions2.length === 1) return suggestions2[0].value;
66
+ let prefix = suggestions2[0].value;
67
+ for (let i = 1; i < suggestions2.length; i++) {
68
+ const str = suggestions2[i].value;
69
+ let j = 0;
70
+ while (j < prefix.length && j < str.length && prefix[j] === str[j]) {
71
+ j++;
72
+ }
73
+ prefix = prefix.slice(0, j);
74
+ if (prefix.length === 0) return "";
68
75
  }
69
- prefix = prefix.slice(0, j);
70
- if (prefix.length === 0) return "";
71
- }
72
- return prefix;
73
- }, []);
76
+ return prefix;
77
+ },
78
+ []
79
+ );
74
80
  const getWordAtCursor = useCallback(() => {
75
81
  if (!input) return null;
76
82
  let start = cursorOffset;
@@ -147,38 +153,67 @@ function useUnifiedCompletion({
147
153
  }, [input, cursorOffset]);
148
154
  const [systemCommands, setSystemCommands] = useState([]);
149
155
  const [isLoadingCommands, setIsLoadingCommands] = useState(false);
150
- const classifyCommand = useCallback((cmd) => {
151
- const lowerCmd = cmd.toLowerCase();
152
- let score = 0;
153
- if (cmd.length <= 4) score += 40;
154
- else if (cmd.length <= 6) score += 20;
155
- else if (cmd.length <= 8) score += 10;
156
- else if (cmd.length > 15) score -= 30;
157
- if (/^[a-z]+$/.test(lowerCmd)) score += 30;
158
- if (/[A-Z]/.test(cmd)) score -= 15;
159
- if (/\d/.test(cmd)) score -= 20;
160
- if (cmd.includes(".")) score -= 25;
161
- if (cmd.includes("-")) score -= 10;
162
- if (cmd.includes("_")) score -= 15;
163
- const commonWords = ["list", "copy", "move", "find", "print", "show", "edit", "view"];
164
- if (commonWords.some((word) => lowerCmd.includes(word.slice(0, 3)))) score += 25;
165
- const devPrefixes = ["git", "npm", "node", "py", "docker", "kubectl"];
166
- if (devPrefixes.some((prefix) => lowerCmd.startsWith(prefix))) score += 15;
167
- const systemIndicators = ["daemon", "helper", "responder", "service", "d$", "ctl$"];
168
- if (systemIndicators.some(
169
- (indicator) => indicator.endsWith("$") ? lowerCmd.endsWith(indicator.slice(0, -1)) : lowerCmd.includes(indicator)
170
- )) score -= 40;
171
- if (/\.(pl|py|sh|rb|js)$/.test(lowerCmd)) score -= 35;
172
- const buildToolPatterns = ["bindep", "render", "mako", "webpack", "babel", "eslint"];
173
- if (buildToolPatterns.some((pattern) => lowerCmd.includes(pattern))) score -= 25;
174
- const vowelRatio = (lowerCmd.match(/[aeiou]/g) || []).length / lowerCmd.length;
175
- if (vowelRatio < 0.2) score += 15;
176
- if (vowelRatio > 0.5) score -= 10;
177
- if (score >= 50) return "core";
178
- if (score >= 20) return "common";
179
- if (score >= -10) return "dev";
180
- return "system";
181
- }, []);
156
+ const classifyCommand = useCallback(
157
+ (cmd) => {
158
+ const lowerCmd = cmd.toLowerCase();
159
+ let score = 0;
160
+ if (cmd.length <= 4) score += 40;
161
+ else if (cmd.length <= 6) score += 20;
162
+ else if (cmd.length <= 8) score += 10;
163
+ else if (cmd.length > 15) score -= 30;
164
+ if (/^[a-z]+$/.test(lowerCmd)) score += 30;
165
+ if (/[A-Z]/.test(cmd)) score -= 15;
166
+ if (/\d/.test(cmd)) score -= 20;
167
+ if (cmd.includes(".")) score -= 25;
168
+ if (cmd.includes("-")) score -= 10;
169
+ if (cmd.includes("_")) score -= 15;
170
+ const commonWords = [
171
+ "list",
172
+ "copy",
173
+ "move",
174
+ "find",
175
+ "print",
176
+ "show",
177
+ "edit",
178
+ "view"
179
+ ];
180
+ if (commonWords.some((word) => lowerCmd.includes(word.slice(0, 3))))
181
+ score += 25;
182
+ const devPrefixes = ["git", "npm", "node", "py", "docker", "kubectl"];
183
+ if (devPrefixes.some((prefix) => lowerCmd.startsWith(prefix))) score += 15;
184
+ const systemIndicators = [
185
+ "daemon",
186
+ "helper",
187
+ "responder",
188
+ "service",
189
+ "d$",
190
+ "ctl$"
191
+ ];
192
+ if (systemIndicators.some(
193
+ (indicator) => indicator.endsWith("$") ? lowerCmd.endsWith(indicator.slice(0, -1)) : lowerCmd.includes(indicator)
194
+ ))
195
+ score -= 40;
196
+ if (/\.(pl|py|sh|rb|js)$/.test(lowerCmd)) score -= 35;
197
+ const buildToolPatterns = [
198
+ "bindep",
199
+ "render",
200
+ "mako",
201
+ "webpack",
202
+ "babel",
203
+ "eslint"
204
+ ];
205
+ if (buildToolPatterns.some((pattern) => lowerCmd.includes(pattern)))
206
+ score -= 25;
207
+ const vowelRatio = (lowerCmd.match(/[aeiou]/g) || []).length / lowerCmd.length;
208
+ if (vowelRatio < 0.2) score += 15;
209
+ if (vowelRatio > 0.5) score -= 10;
210
+ if (score >= 50) return "core";
211
+ if (score >= 20) return "common";
212
+ if (score >= -10) return "dev";
213
+ return "system";
214
+ },
215
+ []
216
+ );
182
217
  const loadSystemCommands = useCallback(async () => {
183
218
  if (systemCommands.length > 0 || isLoadingCommands) return;
184
219
  setIsLoadingCommands(true);
@@ -218,72 +253,89 @@ function useUnifiedCompletion({
218
253
  useEffect(() => {
219
254
  loadSystemCommands();
220
255
  }, [loadSystemCommands]);
221
- const generateCommandSuggestions = useCallback((prefix) => {
222
- const filteredCommands = commands.filter((cmd) => !cmd.isHidden);
223
- if (!prefix) {
224
- return filteredCommands.map((cmd) => ({
256
+ const generateCommandSuggestions = useCallback(
257
+ (prefix) => {
258
+ const filteredCommands = commands.filter((cmd) => !cmd.isHidden);
259
+ if (!prefix) {
260
+ return filteredCommands.map((cmd) => ({
261
+ value: cmd.userFacingName(),
262
+ displayValue: `/${cmd.userFacingName()}`,
263
+ type: "command",
264
+ score: 100
265
+ }));
266
+ }
267
+ return filteredCommands.filter((cmd) => {
268
+ const names = [cmd.userFacingName(), ...cmd.aliases || []];
269
+ return names.some(
270
+ (name) => name.toLowerCase().startsWith(prefix.toLowerCase())
271
+ );
272
+ }).map((cmd) => ({
225
273
  value: cmd.userFacingName(),
226
274
  displayValue: `/${cmd.userFacingName()}`,
227
275
  type: "command",
228
- score: 100
276
+ score: 100 - prefix.length + (cmd.userFacingName().startsWith(prefix) ? 10 : 0)
229
277
  }));
230
- }
231
- return filteredCommands.filter((cmd) => {
232
- const names = [cmd.userFacingName(), ...cmd.aliases || []];
233
- return names.some((name) => name.toLowerCase().startsWith(prefix.toLowerCase()));
234
- }).map((cmd) => ({
235
- value: cmd.userFacingName(),
236
- displayValue: `/${cmd.userFacingName()}`,
237
- type: "command",
238
- score: 100 - prefix.length + (cmd.userFacingName().startsWith(prefix) ? 10 : 0)
239
- }));
240
- }, [commands]);
241
- const calculateUnixCommandScore = useCallback((cmd, prefix) => {
242
- const result = matchCommands([cmd], prefix);
243
- return result.length > 0 ? result[0].score : 0;
244
- }, []);
245
- const generateUnixCommandSuggestions = useCallback((prefix) => {
246
- if (!prefix) return [];
247
- if (isLoadingCommands) {
248
- return [{
249
- value: "loading...",
250
- displayValue: `\u23F3 Loading system commands...`,
251
- type: "file",
252
- score: 0,
253
- metadata: { isLoading: true }
254
- }];
255
- }
256
- const commonCommands = getCommonSystemCommands(systemCommands);
257
- const uniqueCommands = Array.from(new Set(commonCommands));
258
- const matches = matchCommands(uniqueCommands, prefix);
259
- const boostedMatches = matches.map((match) => {
260
- const priority = getCommandPriority(match.command);
261
- return {
262
- ...match,
263
- score: match.score + priority * 0.5
264
- // Add priority boost
265
- };
266
- }).sort((a, b) => b.score - a.score);
267
- let results = boostedMatches.slice(0, 8);
268
- const perfectMatches = boostedMatches.filter((m) => m.score >= 900);
269
- if (perfectMatches.length > 0 && perfectMatches.length <= 3) {
270
- results = perfectMatches;
271
- } else if (boostedMatches.length > 8) {
272
- const goodMatches = boostedMatches.filter((m) => m.score >= 100);
273
- if (goodMatches.length <= 5) {
274
- results = goodMatches;
278
+ },
279
+ [commands]
280
+ );
281
+ const calculateUnixCommandScore = useCallback(
282
+ (cmd, prefix) => {
283
+ const result = matchCommands([cmd], prefix);
284
+ return result.length > 0 ? result[0].score : 0;
285
+ },
286
+ []
287
+ );
288
+ const generateUnixCommandSuggestions = useCallback(
289
+ (prefix) => {
290
+ if (!prefix) return [];
291
+ if (isLoadingCommands) {
292
+ return [
293
+ {
294
+ value: "loading...",
295
+ displayValue: `\u23F3 Loading system commands...`,
296
+ type: "file",
297
+ score: 0,
298
+ metadata: { isLoading: true }
299
+ }
300
+ ];
275
301
  }
276
- }
277
- return results.map((item) => ({
278
- value: item.command,
279
- displayValue: `$ ${item.command}`,
280
- type: "command",
281
- score: item.score,
282
- metadata: { isUnixCommand: true }
283
- }));
284
- }, [systemCommands, isLoadingCommands]);
285
- const [agentSuggestions, setAgentSuggestions] = useState([]);
286
- const [modelSuggestions, setModelSuggestions] = useState([]);
302
+ const commonCommands = getCommonSystemCommands(systemCommands);
303
+ const uniqueCommands = Array.from(new Set(commonCommands));
304
+ const matches = matchCommands(uniqueCommands, prefix);
305
+ const boostedMatches = matches.map((match) => {
306
+ const priority = getCommandPriority(match.command);
307
+ return {
308
+ ...match,
309
+ score: match.score + priority * 0.5
310
+ // Add priority boost
311
+ };
312
+ }).sort((a, b) => b.score - a.score);
313
+ let results = boostedMatches.slice(0, 8);
314
+ const perfectMatches = boostedMatches.filter((m) => m.score >= 900);
315
+ if (perfectMatches.length > 0 && perfectMatches.length <= 3) {
316
+ results = perfectMatches;
317
+ } else if (boostedMatches.length > 8) {
318
+ const goodMatches = boostedMatches.filter((m) => m.score >= 100);
319
+ if (goodMatches.length <= 5) {
320
+ results = goodMatches;
321
+ }
322
+ }
323
+ return results.map((item) => ({
324
+ value: item.command,
325
+ displayValue: `$ ${item.command}`,
326
+ type: "command",
327
+ score: item.score,
328
+ metadata: { isUnixCommand: true }
329
+ }));
330
+ },
331
+ [systemCommands, isLoadingCommands]
332
+ );
333
+ const [agentSuggestions, setAgentSuggestions] = useState(
334
+ []
335
+ );
336
+ const [modelSuggestions, setModelSuggestions] = useState(
337
+ []
338
+ );
287
339
  useEffect(() => {
288
340
  try {
289
341
  const modelManager = getModelManager();
@@ -370,260 +422,292 @@ function useUnifiedCompletion({
370
422
  setAgentSuggestions([]);
371
423
  });
372
424
  }, []);
373
- const generateMentionSuggestions = useCallback((prefix) => {
374
- const allSuggestions = [...agentSuggestions, ...modelSuggestions];
375
- if (!prefix) {
376
- return allSuggestions.sort((a, b) => {
425
+ const generateMentionSuggestions = useCallback(
426
+ (prefix) => {
427
+ const allSuggestions = [...agentSuggestions, ...modelSuggestions];
428
+ if (!prefix) {
429
+ return allSuggestions.sort((a, b) => {
430
+ if (a.type === "ask" && b.type === "agent") return -1;
431
+ if (a.type === "agent" && b.type === "ask") return 1;
432
+ return b.score - a.score;
433
+ });
434
+ }
435
+ const candidates = allSuggestions.map((s) => s.value);
436
+ const matches = matchCommands(candidates, prefix);
437
+ const fuzzyResults = matches.map((match) => {
438
+ const suggestion = allSuggestions.find(
439
+ (s) => s.value === match.command
440
+ );
441
+ return {
442
+ ...suggestion,
443
+ score: match.score
444
+ // Use fuzzy match score instead of simple scoring
445
+ };
446
+ }).sort((a, b) => {
377
447
  if (a.type === "ask" && b.type === "agent") return -1;
378
448
  if (a.type === "agent" && b.type === "ask") return 1;
379
449
  return b.score - a.score;
380
450
  });
381
- }
382
- const candidates = allSuggestions.map((s) => s.value);
383
- const matches = matchCommands(candidates, prefix);
384
- const fuzzyResults = matches.map((match) => {
385
- const suggestion = allSuggestions.find((s) => s.value === match.command);
386
- return {
387
- ...suggestion,
388
- score: match.score
389
- // Use fuzzy match score instead of simple scoring
390
- };
391
- }).sort((a, b) => {
392
- if (a.type === "ask" && b.type === "agent") return -1;
393
- if (a.type === "agent" && b.type === "ask") return 1;
394
- return b.score - a.score;
395
- });
396
- return fuzzyResults;
397
- }, [agentSuggestions, modelSuggestions]);
398
- const generateFileSuggestions = useCallback((prefix, isAtReference = false) => {
399
- try {
400
- const cwd = getCwd();
401
- const userPath = prefix || ".";
402
- const isAbsolutePath = userPath.startsWith("/");
403
- const isHomePath = userPath.startsWith("~");
404
- let searchPath;
405
- if (isHomePath) {
406
- searchPath = userPath.replace("~", process.env.HOME || "");
407
- } else if (isAbsolutePath) {
408
- searchPath = userPath;
409
- } else {
410
- searchPath = resolve(cwd, userPath);
411
- }
412
- const endsWithSlash = userPath.endsWith("/");
413
- const searchStat = existsSync(searchPath) ? statSync(searchPath) : null;
414
- let searchDir;
415
- let nameFilter;
416
- if (endsWithSlash || searchStat?.isDirectory()) {
417
- searchDir = searchPath;
418
- nameFilter = "";
419
- } else {
420
- searchDir = dirname(searchPath);
421
- nameFilter = basename(searchPath);
422
- }
423
- if (!existsSync(searchDir)) return [];
424
- const showHidden = nameFilter.startsWith(".") || userPath.includes("/.");
425
- const entries = readdirSync(searchDir).filter((entry) => {
426
- if (!showHidden && entry.startsWith(".")) return false;
427
- if (nameFilter && !entry.toLowerCase().startsWith(nameFilter.toLowerCase())) return false;
428
- return true;
429
- }).sort((a, b) => {
430
- const aPath = join(searchDir, a);
431
- const bPath = join(searchDir, b);
432
- const aIsDir = statSync(aPath).isDirectory();
433
- const bIsDir = statSync(bPath).isDirectory();
434
- if (aIsDir && !bIsDir) return -1;
435
- if (!aIsDir && bIsDir) return 1;
436
- return a.toLowerCase().localeCompare(b.toLowerCase());
437
- }).slice(0, 25);
438
- return entries.map((entry) => {
439
- const entryPath = join(searchDir, entry);
440
- const isDir = statSync(entryPath).isDirectory();
441
- const icon = isDir ? "\u{1F4C1}" : "\u{1F4C4}";
442
- let value;
443
- if (userPath.includes("/")) {
444
- if (endsWithSlash) {
445
- value = userPath + entry + (isDir ? "/" : "");
446
- } else if (searchStat?.isDirectory()) {
447
- value = userPath + "/" + entry + (isDir ? "/" : "");
448
- } else {
449
- const userDir = userPath.includes("/") ? userPath.substring(0, userPath.lastIndexOf("/")) : "";
450
- value = userDir ? userDir + "/" + entry + (isDir ? "/" : "") : entry + (isDir ? "/" : "");
451
- }
451
+ return fuzzyResults;
452
+ },
453
+ [agentSuggestions, modelSuggestions]
454
+ );
455
+ const generateFileSuggestions = useCallback(
456
+ (prefix, isAtReference = false) => {
457
+ try {
458
+ const cwd = getCwd();
459
+ const userPath = prefix || ".";
460
+ const isAbsolutePath = userPath.startsWith("/");
461
+ const isHomePath = userPath.startsWith("~");
462
+ let searchPath;
463
+ if (isHomePath) {
464
+ searchPath = userPath.replace("~", process.env.HOME || "");
465
+ } else if (isAbsolutePath) {
466
+ searchPath = userPath;
467
+ } else {
468
+ searchPath = resolve(cwd, userPath);
469
+ }
470
+ const endsWithSlash = userPath.endsWith("/");
471
+ const searchStat = existsSync(searchPath) ? statSync(searchPath) : null;
472
+ let searchDir;
473
+ let nameFilter;
474
+ if (endsWithSlash || searchStat?.isDirectory()) {
475
+ searchDir = searchPath;
476
+ nameFilter = "";
452
477
  } else {
453
- if (searchStat?.isDirectory()) {
454
- value = userPath + "/" + entry + (isDir ? "/" : "");
478
+ searchDir = dirname(searchPath);
479
+ nameFilter = basename(searchPath);
480
+ }
481
+ if (!existsSync(searchDir)) return [];
482
+ const showHidden = nameFilter.startsWith(".") || userPath.includes("/.");
483
+ const entries = readdirSync(searchDir).filter((entry) => {
484
+ if (!showHidden && entry.startsWith(".")) return false;
485
+ if (nameFilter && !entry.toLowerCase().startsWith(nameFilter.toLowerCase()))
486
+ return false;
487
+ return true;
488
+ }).sort((a, b) => {
489
+ const aPath = join(searchDir, a);
490
+ const bPath = join(searchDir, b);
491
+ const aIsDir = statSync(aPath).isDirectory();
492
+ const bIsDir = statSync(bPath).isDirectory();
493
+ if (aIsDir && !bIsDir) return -1;
494
+ if (!aIsDir && bIsDir) return 1;
495
+ return a.toLowerCase().localeCompare(b.toLowerCase());
496
+ }).slice(0, 25);
497
+ return entries.map((entry) => {
498
+ const entryPath = join(searchDir, entry);
499
+ const isDir = statSync(entryPath).isDirectory();
500
+ const icon = isDir ? "\u{1F4C1}" : "\u{1F4C4}";
501
+ let value;
502
+ if (userPath.includes("/")) {
503
+ if (endsWithSlash) {
504
+ value = userPath + entry + (isDir ? "/" : "");
505
+ } else if (searchStat?.isDirectory()) {
506
+ value = userPath + "/" + entry + (isDir ? "/" : "");
507
+ } else {
508
+ const userDir = userPath.includes("/") ? userPath.substring(0, userPath.lastIndexOf("/")) : "";
509
+ value = userDir ? userDir + "/" + entry + (isDir ? "/" : "") : entry + (isDir ? "/" : "");
510
+ }
455
511
  } else {
456
- value = entry + (isDir ? "/" : "");
512
+ if (searchStat?.isDirectory()) {
513
+ value = userPath + "/" + entry + (isDir ? "/" : "");
514
+ } else {
515
+ value = entry + (isDir ? "/" : "");
516
+ }
457
517
  }
458
- }
459
- return {
460
- value,
461
- displayValue: `${icon} ${entry}${isDir ? "/" : ""}`,
462
- type: "file",
463
- score: isDir ? 80 : 70
464
- };
465
- });
466
- } catch {
467
- return [];
468
- }
469
- }, []);
470
- const calculateMatchScore = useCallback((suggestion, prefix) => {
471
- const lowerPrefix = prefix.toLowerCase();
472
- const value = suggestion.value.toLowerCase();
473
- const displayValue = suggestion.displayValue.toLowerCase();
474
- let matchFound = false;
475
- let score = 0;
476
- if (value.startsWith(lowerPrefix)) {
477
- matchFound = true;
478
- score = 100;
479
- } else if (value.includes(lowerPrefix)) {
480
- matchFound = true;
481
- score = 95;
482
- } else if (displayValue.includes(lowerPrefix)) {
483
- matchFound = true;
484
- score = 90;
485
- } else {
486
- const words = value.split(/[-_]/);
487
- if (words.some((word) => word.startsWith(lowerPrefix))) {
518
+ return {
519
+ value,
520
+ displayValue: `${icon} ${entry}${isDir ? "/" : ""}`,
521
+ type: "file",
522
+ score: isDir ? 80 : 70
523
+ };
524
+ });
525
+ } catch {
526
+ return [];
527
+ }
528
+ },
529
+ []
530
+ );
531
+ const calculateMatchScore = useCallback(
532
+ (suggestion, prefix) => {
533
+ const lowerPrefix = prefix.toLowerCase();
534
+ const value = suggestion.value.toLowerCase();
535
+ const displayValue = suggestion.displayValue.toLowerCase();
536
+ let matchFound = false;
537
+ let score = 0;
538
+ if (value.startsWith(lowerPrefix)) {
488
539
  matchFound = true;
489
- score = 93;
540
+ score = 100;
541
+ } else if (value.includes(lowerPrefix)) {
542
+ matchFound = true;
543
+ score = 95;
544
+ } else if (displayValue.includes(lowerPrefix)) {
545
+ matchFound = true;
546
+ score = 90;
490
547
  } else {
491
- const acronym = words.map((word) => word[0]).join("");
492
- if (acronym.startsWith(lowerPrefix)) {
548
+ const words = value.split(/[-_]/);
549
+ if (words.some((word) => word.startsWith(lowerPrefix))) {
493
550
  matchFound = true;
494
- score = 88;
551
+ score = 93;
552
+ } else {
553
+ const acronym = words.map((word) => word[0]).join("");
554
+ if (acronym.startsWith(lowerPrefix)) {
555
+ matchFound = true;
556
+ score = 88;
557
+ }
495
558
  }
496
559
  }
497
- }
498
- if (!matchFound) return 0;
499
- if (suggestion.type === "ask") score += 2;
500
- if (suggestion.type === "agent") score += 1;
501
- return score;
502
- }, []);
503
- const generateSmartMentionSuggestions = useCallback((prefix, sourceContext = "file") => {
504
- if (!prefix || prefix.length < 2) return [];
505
- const allSuggestions = [...agentSuggestions, ...modelSuggestions];
506
- return allSuggestions.map((suggestion) => {
507
- const matchScore = calculateMatchScore(suggestion, prefix);
508
- if (matchScore === 0) return null;
509
- return {
510
- ...suggestion,
511
- score: matchScore,
512
- isSmartMatch: true,
513
- originalContext: sourceContext,
514
- // Only modify display for clarity, keep value clean
515
- displayValue: `\u{1F3AF} ${suggestion.displayValue}`
516
- };
517
- }).filter(Boolean).sort((a, b) => b.score - a.score).slice(0, 5);
518
- }, [agentSuggestions, modelSuggestions, calculateMatchScore]);
519
- const generateSuggestions = useCallback((context) => {
520
- switch (context.type) {
521
- case "command":
522
- return generateCommandSuggestions(context.prefix);
523
- case "agent": {
524
- const mentionSuggestions = generateMentionSuggestions(context.prefix);
525
- const fileSuggestions = generateFileSuggestions(context.prefix, true);
526
- const weightedSuggestions = [
527
- ...mentionSuggestions.map((s) => ({
528
- ...s,
529
- // In @ context, agents/models get high priority
530
- weightedScore: s.score + 150
531
- })),
532
- ...fileSuggestions.map((s) => ({
533
- ...s,
534
- // Files get lower priority but still visible
535
- weightedScore: s.score + 10
536
- // Small boost to ensure visibility
537
- }))
538
- ];
539
- return weightedSuggestions.sort((a, b) => b.weightedScore - a.weightedScore).map(({ weightedScore, ...suggestion }) => suggestion);
540
- }
541
- case "file": {
542
- const fileSuggestions = generateFileSuggestions(context.prefix, false);
543
- const unixSuggestions = generateUnixCommandSuggestions(context.prefix);
544
- const mentionMatches = generateMentionSuggestions(context.prefix).map((s) => ({
545
- ...s,
560
+ if (!matchFound) return 0;
561
+ if (suggestion.type === "ask") score += 2;
562
+ if (suggestion.type === "agent") score += 1;
563
+ return score;
564
+ },
565
+ []
566
+ );
567
+ const generateSmartMentionSuggestions = useCallback(
568
+ (prefix, sourceContext = "file") => {
569
+ if (!prefix || prefix.length < 2) return [];
570
+ const allSuggestions = [...agentSuggestions, ...modelSuggestions];
571
+ return allSuggestions.map((suggestion) => {
572
+ const matchScore = calculateMatchScore(suggestion, prefix);
573
+ if (matchScore === 0) return null;
574
+ return {
575
+ ...suggestion,
576
+ score: matchScore,
546
577
  isSmartMatch: true,
547
- // Show that @ will be added when selected
548
- displayValue: `\u2192 ${s.displayValue}`
549
- // Arrow to indicate it will transform
550
- }));
551
- const weightedSuggestions = [
552
- ...unixSuggestions.map((s) => ({
553
- ...s,
554
- // Unix commands get boost, but exact matches get huge boost
555
- sourceWeight: s.score >= 1e4 ? 5e3 : 200,
556
- // Exact match gets massive boost
557
- weightedScore: s.score >= 1e4 ? s.score + 5e3 : s.score + 200
558
- })),
559
- ...mentionMatches.map((s) => ({
560
- ...s,
561
- // Agents/models get medium priority boost (but less to avoid overriding exact Unix)
562
- sourceWeight: 50,
563
- weightedScore: s.score + 50
564
- })),
565
- ...fileSuggestions.map((s) => ({
566
- ...s,
567
- // Files get no boost (baseline)
568
- sourceWeight: 0,
569
- weightedScore: s.score
570
- }))
571
- ];
572
- const seen = /* @__PURE__ */ new Set();
573
- const deduplicatedResults = weightedSuggestions.sort((a, b) => b.weightedScore - a.weightedScore).filter((item) => {
574
- if (seen.has(item.value)) return false;
575
- seen.add(item.value);
576
- return true;
577
- }).map(({ weightedScore, sourceWeight, ...suggestion }) => suggestion);
578
- return deduplicatedResults;
578
+ originalContext: sourceContext,
579
+ // Only modify display for clarity, keep value clean
580
+ displayValue: `\u{1F3AF} ${suggestion.displayValue}`
581
+ };
582
+ }).filter(Boolean).sort((a, b) => b.score - a.score).slice(0, 5);
583
+ },
584
+ [agentSuggestions, modelSuggestions, calculateMatchScore]
585
+ );
586
+ const generateSuggestions = useCallback(
587
+ (context) => {
588
+ switch (context.type) {
589
+ case "command":
590
+ return generateCommandSuggestions(context.prefix);
591
+ case "agent": {
592
+ const mentionSuggestions = generateMentionSuggestions(context.prefix);
593
+ const fileSuggestions = generateFileSuggestions(context.prefix, true);
594
+ const weightedSuggestions = [
595
+ ...mentionSuggestions.map((s) => ({
596
+ ...s,
597
+ // In @ context, agents/models get high priority
598
+ weightedScore: s.score + 150
599
+ })),
600
+ ...fileSuggestions.map((s) => ({
601
+ ...s,
602
+ // Files get lower priority but still visible
603
+ weightedScore: s.score + 10
604
+ // Small boost to ensure visibility
605
+ }))
606
+ ];
607
+ return weightedSuggestions.sort((a, b) => b.weightedScore - a.weightedScore).map(({ weightedScore, ...suggestion }) => suggestion);
608
+ }
609
+ case "file": {
610
+ const fileSuggestions = generateFileSuggestions(context.prefix, false);
611
+ const unixSuggestions = generateUnixCommandSuggestions(context.prefix);
612
+ const mentionMatches = generateMentionSuggestions(context.prefix).map(
613
+ (s) => ({
614
+ ...s,
615
+ isSmartMatch: true,
616
+ // Show that @ will be added when selected
617
+ displayValue: `\u2192 ${s.displayValue}`
618
+ // Arrow to indicate it will transform
619
+ })
620
+ );
621
+ const weightedSuggestions = [
622
+ ...unixSuggestions.map((s) => ({
623
+ ...s,
624
+ // Unix commands get boost, but exact matches get huge boost
625
+ sourceWeight: s.score >= 1e4 ? 5e3 : 200,
626
+ // Exact match gets massive boost
627
+ weightedScore: s.score >= 1e4 ? s.score + 5e3 : s.score + 200
628
+ })),
629
+ ...mentionMatches.map((s) => ({
630
+ ...s,
631
+ // Agents/models get medium priority boost (but less to avoid overriding exact Unix)
632
+ sourceWeight: 50,
633
+ weightedScore: s.score + 50
634
+ })),
635
+ ...fileSuggestions.map((s) => ({
636
+ ...s,
637
+ // Files get no boost (baseline)
638
+ sourceWeight: 0,
639
+ weightedScore: s.score
640
+ }))
641
+ ];
642
+ const seen = /* @__PURE__ */ new Set();
643
+ const deduplicatedResults = weightedSuggestions.sort((a, b) => b.weightedScore - a.weightedScore).filter((item) => {
644
+ if (seen.has(item.value)) return false;
645
+ seen.add(item.value);
646
+ return true;
647
+ }).map(({ weightedScore, sourceWeight, ...suggestion }) => suggestion);
648
+ return deduplicatedResults;
649
+ }
650
+ default:
651
+ return [];
579
652
  }
580
- default:
581
- return [];
582
- }
583
- }, [generateCommandSuggestions, generateMentionSuggestions, generateFileSuggestions, generateUnixCommandSuggestions, generateSmartMentionSuggestions]);
584
- const completeWith = useCallback((suggestion, context) => {
585
- let completion;
586
- if (context.type === "command") {
587
- completion = `/${suggestion.value} `;
588
- } else if (context.type === "agent") {
589
- if (suggestion.type === "agent") {
590
- completion = `@${suggestion.value} `;
591
- } else if (suggestion.type === "ask") {
592
- completion = `@${suggestion.value} `;
653
+ },
654
+ [
655
+ generateCommandSuggestions,
656
+ generateMentionSuggestions,
657
+ generateFileSuggestions,
658
+ generateUnixCommandSuggestions,
659
+ generateSmartMentionSuggestions
660
+ ]
661
+ );
662
+ const completeWith = useCallback(
663
+ (suggestion, context) => {
664
+ let completion;
665
+ if (context.type === "command") {
666
+ completion = `/${suggestion.value} `;
667
+ } else if (context.type === "agent") {
668
+ if (suggestion.type === "agent") {
669
+ completion = `@${suggestion.value} `;
670
+ } else if (suggestion.type === "ask") {
671
+ completion = `@${suggestion.value} `;
672
+ } else {
673
+ const isDirectory = suggestion.value.endsWith("/");
674
+ completion = `@${suggestion.value}${isDirectory ? "" : " "}`;
675
+ }
593
676
  } else {
594
- const isDirectory = suggestion.value.endsWith("/");
595
- completion = `@${suggestion.value}${isDirectory ? "" : " "}`;
677
+ if (suggestion.isSmartMatch) {
678
+ completion = `@${suggestion.value} `;
679
+ } else {
680
+ const isDirectory = suggestion.value.endsWith("/");
681
+ completion = suggestion.value + (isDirectory ? "" : " ");
682
+ }
596
683
  }
597
- } else {
598
- if (suggestion.isSmartMatch) {
599
- completion = `@${suggestion.value} `;
684
+ let actualEndPos;
685
+ if (context.type === "file" && suggestion.value.startsWith("/") && !suggestion.isSmartMatch) {
686
+ let end = context.startPos;
687
+ while (end < input.length && input[end] !== " " && input[end] !== "\n") {
688
+ end++;
689
+ }
690
+ actualEndPos = end;
600
691
  } else {
601
- const isDirectory = suggestion.value.endsWith("/");
602
- completion = suggestion.value + (isDirectory ? "" : " ");
603
- }
604
- }
605
- let actualEndPos;
606
- if (context.type === "file" && suggestion.value.startsWith("/") && !suggestion.isSmartMatch) {
607
- let end = context.startPos;
608
- while (end < input.length && input[end] !== " " && input[end] !== "\n") {
609
- end++;
692
+ const currentWord = input.slice(context.startPos);
693
+ const nextSpaceIndex = currentWord.indexOf(" ");
694
+ actualEndPos = nextSpaceIndex === -1 ? input.length : context.startPos + nextSpaceIndex;
610
695
  }
611
- actualEndPos = end;
612
- } else {
613
- const currentWord = input.slice(context.startPos);
614
- const nextSpaceIndex = currentWord.indexOf(" ");
615
- actualEndPos = nextSpaceIndex === -1 ? input.length : context.startPos + nextSpaceIndex;
616
- }
617
- const newInput = input.slice(0, context.startPos) + completion + input.slice(actualEndPos);
618
- onInputChange(newInput);
619
- setCursorOffset(context.startPos + completion.length);
620
- }, [input, onInputChange, setCursorOffset, onSubmit, commands]);
621
- const partialComplete = useCallback((prefix, context) => {
622
- const completion = context.type === "command" ? `/${prefix}` : context.type === "agent" ? `@${prefix}` : prefix;
623
- const newInput = input.slice(0, context.startPos) + completion + input.slice(context.endPos);
624
- onInputChange(newInput);
625
- setCursorOffset(context.startPos + completion.length);
626
- }, [input, onInputChange, setCursorOffset]);
696
+ const newInput = input.slice(0, context.startPos) + completion + input.slice(actualEndPos);
697
+ onInputChange(newInput);
698
+ setCursorOffset(context.startPos + completion.length);
699
+ },
700
+ [input, onInputChange, setCursorOffset, onSubmit, commands]
701
+ );
702
+ const partialComplete = useCallback(
703
+ (prefix, context) => {
704
+ const completion = context.type === "command" ? `/${prefix}` : context.type === "agent" ? `@${prefix}` : prefix;
705
+ const newInput = input.slice(0, context.startPos) + completion + input.slice(context.endPos);
706
+ onInputChange(newInput);
707
+ setCursorOffset(context.startPos + completion.length);
708
+ },
709
+ [input, onInputChange, setCursorOffset]
710
+ );
627
711
  useInput((input_str, key) => {
628
712
  if (!key.tab) return false;
629
713
  if (key.shift) return false;
@@ -654,7 +738,10 @@ function useUnifiedCompletion({
654
738
  preview: {
655
739
  isActive: true,
656
740
  originalInput: input,
657
- wordRange: [state.context.startPos, state.context.startPos + preview.length]
741
+ wordRange: [
742
+ state.context.startPos,
743
+ state.context.startPos + preview.length
744
+ ]
658
745
  }
659
746
  });
660
747
  }
@@ -736,7 +823,10 @@ function useUnifiedCompletion({
736
823
  selectedIndex: newIndex,
737
824
  preview: {
738
825
  ...state.preview,
739
- wordRange: [state.context.startPos, state.context.startPos + preview.length]
826
+ wordRange: [
827
+ state.context.startPos,
828
+ state.context.startPos + preview.length
829
+ ]
740
830
  }
741
831
  });
742
832
  } else {
@@ -843,9 +933,12 @@ function useUnifiedCompletion({
843
933
  const lastInputRef = useRef("");
844
934
  useEffect(() => {
845
935
  if (lastInputRef.current === input) return;
846
- const inputLengthChange = Math.abs(input.length - lastInputRef.current.length);
936
+ const inputLengthChange = Math.abs(
937
+ input.length - lastInputRef.current.length
938
+ );
847
939
  const isHistoryNavigation = (inputLengthChange > 10 || // Large content change
848
- inputLengthChange > 5 && !input.includes(lastInputRef.current.slice(-5))) && input !== lastInputRef.current;
940
+ inputLengthChange > 5 && !input.includes(lastInputRef.current.slice(-5))) && // Different content
941
+ input !== lastInputRef.current;
849
942
  lastInputRef.current = input;
850
943
  if (state.preview?.isActive || Date.now() < state.suppressUntil) {
851
944
  return;
@@ -871,51 +964,57 @@ function useUnifiedCompletion({
871
964
  }
872
965
  }
873
966
  }, [input, cursorOffset]);
874
- const shouldAutoTrigger = useCallback((context) => {
875
- switch (context.type) {
876
- case "command":
877
- return true;
878
- case "agent":
879
- return true;
880
- case "file":
881
- const prefix = context.prefix;
882
- if (prefix.startsWith("./") || prefix.startsWith("../") || prefix.startsWith("/") || prefix.startsWith("~") || prefix.includes("/")) {
967
+ const shouldAutoTrigger = useCallback(
968
+ (context) => {
969
+ switch (context.type) {
970
+ case "command":
883
971
  return true;
972
+ case "agent":
973
+ return true;
974
+ case "file":
975
+ const prefix = context.prefix;
976
+ if (prefix.startsWith("./") || prefix.startsWith("../") || prefix.startsWith("/") || prefix.startsWith("~") || prefix.includes("/")) {
977
+ return true;
978
+ }
979
+ if (prefix.startsWith(".") && prefix.length >= 2) {
980
+ return true;
981
+ }
982
+ return false;
983
+ default:
984
+ return false;
985
+ }
986
+ },
987
+ []
988
+ );
989
+ const shouldAutoHideSingleMatch = useCallback(
990
+ (suggestion, context) => {
991
+ const currentInput = input.slice(context.startPos, context.endPos);
992
+ if (context.type === "file") {
993
+ if (suggestion.value.endsWith("/")) {
994
+ return false;
884
995
  }
885
- if (prefix.startsWith(".") && prefix.length >= 2) {
996
+ if (currentInput === suggestion.value) {
997
+ return true;
998
+ }
999
+ if (currentInput.endsWith("/" + suggestion.value) || currentInput.endsWith(suggestion.value)) {
886
1000
  return true;
887
1001
  }
888
- return false;
889
- default:
890
- return false;
891
- }
892
- }, []);
893
- const shouldAutoHideSingleMatch = useCallback((suggestion, context) => {
894
- const currentInput = input.slice(context.startPos, context.endPos);
895
- if (context.type === "file") {
896
- if (suggestion.value.endsWith("/")) {
897
1002
  return false;
898
1003
  }
899
- if (currentInput === suggestion.value) {
900
- return true;
1004
+ if (context.type === "command") {
1005
+ const fullCommand = `/${suggestion.value}`;
1006
+ const matches = currentInput === fullCommand;
1007
+ return matches;
901
1008
  }
902
- if (currentInput.endsWith("/" + suggestion.value) || currentInput.endsWith(suggestion.value)) {
903
- return true;
1009
+ if (context.type === "agent") {
1010
+ const fullAgent = `@${suggestion.value}`;
1011
+ const matches = currentInput === fullAgent;
1012
+ return matches;
904
1013
  }
905
1014
  return false;
906
- }
907
- if (context.type === "command") {
908
- const fullCommand = `/${suggestion.value}`;
909
- const matches = currentInput === fullCommand;
910
- return matches;
911
- }
912
- if (context.type === "agent") {
913
- const fullAgent = `@${suggestion.value}`;
914
- const matches = currentInput === fullAgent;
915
- return matches;
916
- }
917
- return false;
918
- }, [input]);
1015
+ },
1016
+ [input]
1017
+ );
919
1018
  return {
920
1019
  suggestions,
921
1020
  selectedIndex,