@abacus-ai/cli 1.106.25007 → 2.0.0-canary.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 (200) hide show
  1. package/.oxlintrc.json +8 -0
  2. package/dist/index.mjs +12603 -0
  3. package/package.json +7 -39
  4. package/resources/abacus.ico +0 -0
  5. package/resources/entitlements.plist +9 -0
  6. package/src/__e2e__/README.md +196 -0
  7. package/src/__e2e__/agent-interactions.e2e.test.tsx +61 -0
  8. package/src/__e2e__/cli-commands.e2e.test.tsx +77 -0
  9. package/src/__e2e__/conversation-throttle.e2e.test.ts +453 -0
  10. package/src/__e2e__/conversation.e2e.test.tsx +56 -0
  11. package/src/__e2e__/diff-preview.e2e.test.tsx +3399 -0
  12. package/src/__e2e__/file-creation.e2e.test.tsx +149 -0
  13. package/src/__e2e__/helpers/test-helpers.ts +450 -0
  14. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +34 -0
  15. package/src/__e2e__/llm-models.e2e.test.ts +402 -0
  16. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +71 -0
  17. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +167 -0
  18. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +185 -0
  19. package/src/__e2e__/repl.e2e.test.tsx +78 -0
  20. package/src/__e2e__/shell-compatibility.e2e.test.tsx +76 -0
  21. package/src/__e2e__/theme-mcp.e2e.test.tsx +98 -0
  22. package/src/__e2e__/tool-permissions.e2e.test.tsx +66 -0
  23. package/src/args.ts +22 -0
  24. package/src/components/__tests__/react-compiler.test.tsx +78 -0
  25. package/src/components/__tests__/status-indicator.test.tsx +403 -0
  26. package/src/components/composer/__tests__/bash-runner.test.tsx +263 -0
  27. package/src/components/composer/agent-mode-indicator.tsx +63 -0
  28. package/src/components/composer/bash-runner.tsx +54 -0
  29. package/src/components/composer/commands/default-commands.tsx +615 -0
  30. package/src/components/composer/commands/handler.tsx +59 -0
  31. package/src/components/composer/commands/picker.tsx +273 -0
  32. package/src/components/composer/commands/registry.ts +233 -0
  33. package/src/components/composer/commands/types.ts +33 -0
  34. package/src/components/composer/context.tsx +88 -0
  35. package/src/components/composer/file-mention-picker.tsx +83 -0
  36. package/src/components/composer/help.tsx +44 -0
  37. package/src/components/composer/index.tsx +1006 -0
  38. package/src/components/composer/mentions.ts +57 -0
  39. package/src/components/composer/message-queue.tsx +70 -0
  40. package/src/components/composer/mode-panel.tsx +35 -0
  41. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +755 -0
  42. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +1108 -0
  43. package/src/components/composer/modes/bash-handler.tsx +132 -0
  44. package/src/components/composer/modes/bash-renderer.tsx +175 -0
  45. package/src/components/composer/modes/default-handlers.tsx +33 -0
  46. package/src/components/composer/modes/index.ts +41 -0
  47. package/src/components/composer/modes/types.ts +21 -0
  48. package/src/components/composer/persistent-shell.ts +283 -0
  49. package/src/components/composer/process.ts +65 -0
  50. package/src/components/composer/types.ts +9 -0
  51. package/src/components/composer/use-mention-search.ts +68 -0
  52. package/src/components/error-boundry.tsx +60 -0
  53. package/src/components/exit-message.tsx +29 -0
  54. package/src/components/expanded-view.tsx +74 -0
  55. package/src/components/file-completion.tsx +127 -0
  56. package/src/components/header.tsx +47 -0
  57. package/src/components/logo.tsx +37 -0
  58. package/src/components/segments.tsx +356 -0
  59. package/src/components/status-indicator.tsx +306 -0
  60. package/src/components/tool-group-summary.tsx +263 -0
  61. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +312 -0
  62. package/src/components/tool-permissions/diff-preview.tsx +355 -0
  63. package/src/components/tool-permissions/index.ts +5 -0
  64. package/src/components/tool-permissions/permission-options.tsx +375 -0
  65. package/src/components/tool-permissions/permission-preview-header.tsx +57 -0
  66. package/src/components/tool-permissions/tool-permission-ui.tsx +398 -0
  67. package/src/components/tools/agent/ask-user-question.tsx +101 -0
  68. package/src/components/tools/agent/enter-plan-mode.tsx +49 -0
  69. package/src/components/tools/agent/exit-plan-mode.tsx +75 -0
  70. package/src/components/tools/agent/handoff-to-main.tsx +27 -0
  71. package/src/components/tools/agent/subagent.tsx +37 -0
  72. package/src/components/tools/agent/todo-write.tsx +104 -0
  73. package/src/components/tools/browser/close-tab.tsx +58 -0
  74. package/src/components/tools/browser/computer.tsx +70 -0
  75. package/src/components/tools/browser/get-interactive-elements.tsx +54 -0
  76. package/src/components/tools/browser/get-tab-content.tsx +51 -0
  77. package/src/components/tools/browser/navigate-to.tsx +59 -0
  78. package/src/components/tools/browser/new-tab.tsx +60 -0
  79. package/src/components/tools/browser/perform-action.tsx +63 -0
  80. package/src/components/tools/browser/refresh-tab.tsx +43 -0
  81. package/src/components/tools/browser/switch-tab.tsx +58 -0
  82. package/src/components/tools/filesystem/delete-file.tsx +104 -0
  83. package/src/components/tools/filesystem/edit.tsx +220 -0
  84. package/src/components/tools/filesystem/list-dir.tsx +78 -0
  85. package/src/components/tools/filesystem/read-file.tsx +180 -0
  86. package/src/components/tools/filesystem/upload-image.tsx +76 -0
  87. package/src/components/tools/ide/ide-diagnostics.tsx +62 -0
  88. package/src/components/tools/index.ts +91 -0
  89. package/src/components/tools/mcp/mcp-tool.tsx +158 -0
  90. package/src/components/tools/search/fetch-url.tsx +73 -0
  91. package/src/components/tools/search/file-search.tsx +78 -0
  92. package/src/components/tools/search/grep.tsx +90 -0
  93. package/src/components/tools/search/semantic-search.tsx +66 -0
  94. package/src/components/tools/search/web-search.tsx +71 -0
  95. package/src/components/tools/shared/index.tsx +48 -0
  96. package/src/components/tools/shared/zod-coercion.ts +35 -0
  97. package/src/components/tools/terminal/bash-tool-output.tsx +174 -0
  98. package/src/components/tools/terminal/get-terminal-output.tsx +85 -0
  99. package/src/components/tools/terminal/run-in-terminal.tsx +106 -0
  100. package/src/components/tools/types.ts +16 -0
  101. package/src/components/tools.tsx +66 -0
  102. package/src/components/ui/__tests__/divider.test.tsx +61 -0
  103. package/src/components/ui/__tests__/gradient.test.tsx +125 -0
  104. package/src/components/ui/__tests__/input.test.tsx +166 -0
  105. package/src/components/ui/__tests__/select.test.tsx +273 -0
  106. package/src/components/ui/__tests__/shimmer.test.tsx +99 -0
  107. package/src/components/ui/blinking-indicator.tsx +25 -0
  108. package/src/components/ui/divider.tsx +162 -0
  109. package/src/components/ui/gradient.tsx +56 -0
  110. package/src/components/ui/input.tsx +228 -0
  111. package/src/components/ui/select.tsx +151 -0
  112. package/src/components/ui/shimmer.tsx +84 -0
  113. package/src/context/agent-mode.tsx +95 -0
  114. package/src/context/extension-file.tsx +136 -0
  115. package/src/context/network-activity.tsx +45 -0
  116. package/src/context/notification.tsx +62 -0
  117. package/src/context/shell-size.tsx +49 -0
  118. package/src/context/shell-title.tsx +38 -0
  119. package/src/entrypoints/print-mode.ts +312 -0
  120. package/src/entrypoints/repl.tsx +401 -0
  121. package/src/hooks/use-agent.ts +15 -0
  122. package/src/hooks/use-api-client.ts +1 -0
  123. package/src/hooks/use-available-height.ts +8 -0
  124. package/src/hooks/use-cleanup.ts +29 -0
  125. package/src/hooks/use-interrupt-manager.ts +242 -0
  126. package/src/hooks/use-models.ts +22 -0
  127. package/src/index.ts +217 -0
  128. package/src/lib/__tests__/ansi.test.ts +255 -0
  129. package/src/lib/__tests__/cli.test.ts +122 -0
  130. package/src/lib/__tests__/commands.test.ts +325 -0
  131. package/src/lib/__tests__/constants.test.ts +15 -0
  132. package/src/lib/__tests__/focusables.test.ts +25 -0
  133. package/src/lib/__tests__/fs.test.ts +231 -0
  134. package/src/lib/__tests__/markdown.test.tsx +348 -0
  135. package/src/lib/__tests__/mcpCommandHandler.test.ts +173 -0
  136. package/src/lib/__tests__/mcpManagement.test.ts +38 -0
  137. package/src/lib/__tests__/path-paste.test.ts +144 -0
  138. package/src/lib/__tests__/path.test.ts +300 -0
  139. package/src/lib/__tests__/queries.test.ts +39 -0
  140. package/src/lib/__tests__/standaloneMcpService.test.ts +71 -0
  141. package/src/lib/__tests__/text-buffer.test.ts +328 -0
  142. package/src/lib/__tests__/text-utils.test.ts +32 -0
  143. package/src/lib/__tests__/timing.test.ts +78 -0
  144. package/src/lib/__tests__/utils.test.ts +238 -0
  145. package/src/lib/__tests__/vim-buffer-actions.test.ts +154 -0
  146. package/src/lib/ansi.ts +150 -0
  147. package/src/lib/cli-push-server.ts +112 -0
  148. package/src/lib/cli.ts +44 -0
  149. package/src/lib/clipboard.ts +226 -0
  150. package/src/lib/command-utils.ts +93 -0
  151. package/src/lib/commands.ts +270 -0
  152. package/src/lib/constants.ts +3 -0
  153. package/src/lib/extension-connection.ts +181 -0
  154. package/src/lib/focusables.ts +7 -0
  155. package/src/lib/fs.ts +533 -0
  156. package/src/lib/markdown/code-block.tsx +63 -0
  157. package/src/lib/markdown/index.ts +4 -0
  158. package/src/lib/markdown/link.tsx +19 -0
  159. package/src/lib/markdown/markdown.tsx +372 -0
  160. package/src/lib/markdown/types.ts +15 -0
  161. package/src/lib/mcpCommandHandler.ts +121 -0
  162. package/src/lib/mcpManagement.ts +44 -0
  163. package/src/lib/path-paste.ts +185 -0
  164. package/src/lib/path.ts +179 -0
  165. package/src/lib/queries.ts +15 -0
  166. package/src/lib/standaloneMcpService.ts +688 -0
  167. package/src/lib/status-utils.ts +237 -0
  168. package/src/lib/test-utils.tsx +72 -0
  169. package/src/lib/text-buffer.ts +2415 -0
  170. package/src/lib/text-utils.ts +272 -0
  171. package/src/lib/timing.ts +63 -0
  172. package/src/lib/types.ts +295 -0
  173. package/src/lib/utils.ts +182 -0
  174. package/src/lib/vim-buffer-actions.ts +732 -0
  175. package/src/providers/agent.tsx +1075 -0
  176. package/src/providers/api-client.tsx +43 -0
  177. package/src/services/logger.ts +85 -0
  178. package/src/terminal/detection.ts +187 -0
  179. package/src/terminal/exit.ts +279 -0
  180. package/src/terminal/notification.ts +83 -0
  181. package/src/terminal/progress.ts +201 -0
  182. package/src/terminal/setup.ts +797 -0
  183. package/src/terminal/suspend.ts +58 -0
  184. package/src/terminal/types.ts +51 -0
  185. package/src/theme/context.tsx +57 -0
  186. package/src/theme/index.ts +4 -0
  187. package/src/theme/themed.tsx +35 -0
  188. package/src/theme/themes.json +546 -0
  189. package/src/theme/types.ts +110 -0
  190. package/src/tools/types.ts +59 -0
  191. package/src/tools/utils/__tests__/zod-coercion.test.ts +33 -0
  192. package/src/tools/utils/tool-ui-components.tsx +631 -0
  193. package/src/tools/utils/zod-coercion.ts +35 -0
  194. package/tsconfig.json +11 -0
  195. package/tsconfig.node.json +29 -0
  196. package/tsconfig.test.json +27 -0
  197. package/tsdown.config.ts +17 -0
  198. package/vitest.config.ts +76 -0
  199. package/README.md +0 -28
  200. package/dist/index.js +0 -26
@@ -0,0 +1,185 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ import { stripFileProtocol } from "./clipboard.js";
5
+ import { expandHome, abbreviatePath } from "./path.js";
6
+
7
+ export const IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp", ".heic", ".heif", ".gif"];
8
+
9
+ export function isImagePath(p: string): boolean {
10
+ return IMAGE_EXTENSIONS.includes(path.extname(p).toLowerCase());
11
+ }
12
+
13
+ /**
14
+ * Escapes a path for use in an @mention.
15
+ * Paths containing spaces are wrapped in single quotes (shell-safe).
16
+ */
17
+ export function escapePathForMention(p: string): string {
18
+ if (p.includes(" ")) return `'${p.replace(/'/g, "'\\''")}'`;
19
+ return p;
20
+ }
21
+
22
+ /**
23
+ * Abbreviates an absolute path for display, picking the shorter of
24
+ * CWD-relative (./src/foo.ts) or home-relative (~/work/bar.ts).
25
+ */
26
+ export function prettifyMentionPath(absolutePath: string): string {
27
+ return abbreviatePath(absolutePath);
28
+ }
29
+
30
+ // ── State-machine path splitter ───────────────────────────────────────────
31
+
32
+ type SplitMode = "NORMAL" | "SINGLE" | "DOUBLE";
33
+
34
+ function looksLikePathStart(text: string): boolean {
35
+ const unquoted = text.startsWith("'") || text.startsWith('"') ? text.slice(1) : text;
36
+ return (
37
+ unquoted.startsWith("/") ||
38
+ unquoted.startsWith("~") ||
39
+ unquoted.startsWith("./") ||
40
+ unquoted.startsWith("../") ||
41
+ /^file:\/\//i.test(unquoted) ||
42
+ /^[A-Za-z]:[/\\]/.test(unquoted) ||
43
+ unquoted.startsWith("\\\\") || // Windows UNC
44
+ text.startsWith("& '") // PowerShell & 'path'
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Generator that splits a drag-and-drop input string into resolved absolute paths.
50
+ *
51
+ * Handles all major terminal/OS path encodings:
52
+ * - Bare POSIX: /path/to/file.txt
53
+ * - Backslash-escaped: /path/to/my\ file.txt
54
+ * - Single-quoted: '/path/to/my file.txt'
55
+ * - Double-quoted (Windows): "C:\path\to\file.txt"
56
+ * - file:// URI: file:///path/to/my%20file.txt
57
+ * - Windows bare C:: c:\path\to\file.txt
58
+ * - Windows UNC: \\Mac\Home\file.txt
59
+ * - PowerShell & '...': & 'c:\New Text Document.txt'
60
+ */
61
+ export function* splitDragAndDropPaths(text: string): Generator<string> {
62
+ const trimmed = text.trim();
63
+
64
+ if (!looksLikePathStart(trimmed)) return;
65
+
66
+ // Strip PowerShell & prefix (e.g. & 'path')
67
+ const input = trimmed.replace(/^& /, "");
68
+
69
+ function resolveSeg(raw: string): string {
70
+ let s = raw.trim();
71
+ // Decode file:// URI
72
+ if (/^file:\/\//i.test(s)) {
73
+ s = stripFileProtocol(s);
74
+ }
75
+ // Unescape backslash-escaped spaces
76
+ s = s.replace(/\\ /g, " ");
77
+ // Expand ~ to home
78
+ s = expandHome(s);
79
+ return path.resolve(s);
80
+ }
81
+
82
+ let mode: SplitMode = "NORMAL";
83
+ let current = "";
84
+
85
+ let i = 0;
86
+ while (i < input.length) {
87
+ const ch = input[i];
88
+
89
+ if (mode === "SINGLE") {
90
+ // Check for '\'' (escaped single quote within single-quoted string)
91
+ if (ch === "'" && input.slice(i, i + 4) === "'\\''") {
92
+ current += "'";
93
+ i += 4;
94
+ continue;
95
+ }
96
+ if (ch === "'") {
97
+ // End of single-quoted segment
98
+ const resolved = resolveSeg(current);
99
+ if (resolved) yield resolved;
100
+ current = "";
101
+ mode = "NORMAL";
102
+ i++;
103
+ continue;
104
+ }
105
+ current += ch;
106
+ i++;
107
+ continue;
108
+ }
109
+
110
+ if (mode === "DOUBLE") {
111
+ if (ch === '"') {
112
+ const resolved = resolveSeg(current);
113
+ if (resolved) yield resolved;
114
+ current = "";
115
+ mode = "NORMAL";
116
+ i++;
117
+ continue;
118
+ }
119
+ current += ch;
120
+ i++;
121
+ continue;
122
+ }
123
+
124
+ // NORMAL mode
125
+ if (ch === "'") {
126
+ mode = "SINGLE";
127
+ i++;
128
+ continue;
129
+ }
130
+ if (ch === '"') {
131
+ mode = "DOUBLE";
132
+ i++;
133
+ continue;
134
+ }
135
+ // Backslash-escaped space
136
+ if (ch === "\\" && i + 1 < input.length && input[i + 1] === " ") {
137
+ current += "\\ ";
138
+ i += 2;
139
+ continue;
140
+ }
141
+ // Unescaped separator between paths
142
+ if (ch === " " || ch === "\n" || ch === "\t") {
143
+ if (current.trim()) {
144
+ const resolved = resolveSeg(current);
145
+ if (resolved) yield resolved;
146
+ current = "";
147
+ }
148
+ i++;
149
+ continue;
150
+ }
151
+ current += ch;
152
+ i++;
153
+ }
154
+
155
+ // Flush remaining segment
156
+ if (current.trim()) {
157
+ const resolved = resolveSeg(current);
158
+ if (resolved) yield resolved;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Validates pasted text as file paths and formats them as @mention strings.
164
+ *
165
+ * All-or-nothing: returns null if any segment doesn't exist on disk,
166
+ * or if no path segments are found.
167
+ *
168
+ * @returns "@path1 @path2 " (trailing space) or null
169
+ */
170
+ export function parsePastedPaths(text: string): string | null {
171
+ const segments: string[] = [];
172
+
173
+ for (const resolved of splitDragAndDropPaths(text)) {
174
+ if (!existsSync(resolved)) {
175
+ return null; // all-or-nothing: bail if any path is missing
176
+ }
177
+ const pretty = prettifyMentionPath(resolved);
178
+ const escaped = escapePathForMention(pretty);
179
+ segments.push(`@${escaped}`);
180
+ }
181
+
182
+ if (segments.length === 0) return null;
183
+
184
+ return segments.join(" ") + " ";
185
+ }
@@ -0,0 +1,179 @@
1
+ import { realpathSync } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * Special characters that need to be escaped in file paths for shell compatibility.
7
+ * Includes: spaces, parentheses, brackets, braces, semicolons, ampersands, pipes,
8
+ * asterisks, question marks, dollar signs, backticks, quotes, hash, and other shell metacharacters.
9
+ */
10
+ export const SHELL_SPECIAL_CHARS = /[ \t()[\]{};|*?$`'"#&<>!~]/;
11
+
12
+ /**
13
+ * Unescapes special characters in a file path.
14
+ * Removes backslash escaping from shell metacharacters.
15
+ */
16
+ export function unescapePath(filePath: string): string {
17
+ return filePath.replace(
18
+ new RegExp(`\\\\([${SHELL_SPECIAL_CHARS.source.slice(1, -1)}])`, "g"),
19
+ "$1",
20
+ );
21
+ }
22
+
23
+ const home = os.homedir();
24
+
25
+ export function expandHome(filePath: string): string {
26
+ if (filePath === "~") {
27
+ return home;
28
+ }
29
+ if (filePath.startsWith("~/")) {
30
+ return path.join(home, filePath.slice(2));
31
+ }
32
+ return filePath;
33
+ }
34
+
35
+ /**
36
+ * Abbreviates the home directory in a file path
37
+ * @param filePath - The file path to abbreviate.
38
+ * @param cwd - The current working directory (defaults to process.cwd())
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * abbreviateHome('/home/user/documents/file.txt') // ~/documents/file.txt
43
+ * abbreviateHome('./documents/file.txt', '/home/user') // ~/documents/file.txt
44
+ * ```
45
+ */
46
+ export function abbreviateHome(filePath: string, cwd: string = process.cwd()): string {
47
+ const resolved = path.resolve(cwd, filePath);
48
+ if (resolved === home || resolved.startsWith(`${home}${path.sep}`)) {
49
+ const suffix = resolved.slice(home.length);
50
+ const posix = suffix.split(path.sep).join("/");
51
+ return `~${posix}`;
52
+ }
53
+ return resolved;
54
+ }
55
+
56
+ export function abbreviateCwd(filePath: string): string {
57
+ const resolved = path.resolve(filePath);
58
+ const rel = path.relative(process.cwd(), resolved);
59
+ if (!rel) {
60
+ return ".";
61
+ }
62
+ const posixRel = rel.split(path.sep).join("/");
63
+ if (posixRel.startsWith(".")) {
64
+ return posixRel;
65
+ }
66
+ return `./${posixRel}`;
67
+ }
68
+
69
+ export function abbreviatePath(filePath: string): string {
70
+ const resolved = path.resolve(filePath);
71
+ const cwd = process.cwd();
72
+ const rel = path.relative(cwd, resolved);
73
+
74
+ if (!rel) {
75
+ return ".";
76
+ }
77
+
78
+ if (!rel.startsWith("..")) {
79
+ const posixRel = rel.split(path.sep).join("/");
80
+ return posixRel.startsWith(".") ? posixRel : `./${posixRel}`;
81
+ }
82
+
83
+ if (resolved === home || resolved.startsWith(`${home}${path.sep}`)) {
84
+ const suffix = resolved.slice(home.length);
85
+ const posix = suffix.split(path.sep).join("/");
86
+ return `~${posix}`;
87
+ }
88
+
89
+ return resolved;
90
+ }
91
+
92
+ export function resolvePath(filePath: string): string {
93
+ return path.resolve(expandHome(filePath));
94
+ }
95
+
96
+ /**
97
+ * Attempts to convert an absolute path to a relative path within a workspace,
98
+ * handling symlinks correctly. This is useful for SSH environments where workspace
99
+ * directories may be symlinked (e.g., /home/user/code -> /shared/home/user/code).
100
+ *
101
+ * @param absolutePath - The absolute path to convert
102
+ * @param workspacePath - The workspace base path
103
+ * @returns An object with:
104
+ * - success: true if conversion succeeded
105
+ * - relativePath: the relative path (only if success is true)
106
+ * - reason: explanation if conversion failed
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // Direct path match
111
+ * convertAbsoluteToRelativePath('/home/user/code/file.py', '/home/user/code')
112
+ * // => { success: true, relativePath: 'file.py' }
113
+ *
114
+ * // With symlinks: /home/user/code -> /shared/home/user/code
115
+ * convertAbsoluteToRelativePath('/home/user/code/file.py', '/shared/home/user/code')
116
+ * // => { success: true, relativePath: 'file.py' }
117
+ *
118
+ * // Outside workspace
119
+ * convertAbsoluteToRelativePath('/other/path/file.py', '/home/user/code')
120
+ * // => { success: false, reason: 'outside_workspace' }
121
+ * ```
122
+ */
123
+ export function convertAbsoluteToRelativePath(
124
+ absolutePath: string,
125
+ workspacePath: string,
126
+ ): { success: true; relativePath: string } | { success: false; reason: string } {
127
+ const normalizedTarget = path.resolve(absolutePath);
128
+ const normalizedWorkspace = path.resolve(workspacePath);
129
+
130
+ // Step 1: Try direct conversion (without symlink resolution)
131
+ let relativeToWorkspace = path.relative(normalizedWorkspace, normalizedTarget);
132
+ const canConvertDirect =
133
+ relativeToWorkspace &&
134
+ !relativeToWorkspace.startsWith("..") &&
135
+ !path.isAbsolute(relativeToWorkspace);
136
+
137
+ if (canConvertDirect) {
138
+ return { success: true, relativePath: relativeToWorkspace };
139
+ }
140
+
141
+ // Step 2: Try with symlink resolution
142
+ // This handles cases like /home/user/code vs /shared/home/user/code
143
+ let realTarget = normalizedTarget;
144
+ let realWorkspace = normalizedWorkspace;
145
+
146
+ try {
147
+ realTarget = realpathSync(normalizedTarget);
148
+ } catch {
149
+ // If realpath fails (file doesn't exist yet), try resolving parent directory
150
+ try {
151
+ const parentDir = path.resolve(normalizedTarget, "..");
152
+ const fileName = normalizedTarget.split(path.sep).pop() || "";
153
+ const realParent = realpathSync(parentDir);
154
+ realTarget = path.resolve(realParent, fileName);
155
+ } catch {
156
+ // If that also fails, use normalized path
157
+ }
158
+ }
159
+
160
+ try {
161
+ realWorkspace = realpathSync(normalizedWorkspace);
162
+ } catch {
163
+ // If realpath fails, use normalized path
164
+ }
165
+
166
+ // Try relative path calculation with resolved symlinks
167
+ relativeToWorkspace = path.relative(realWorkspace, realTarget);
168
+ const canConvertWithSymlinks =
169
+ relativeToWorkspace &&
170
+ !relativeToWorkspace.startsWith("..") &&
171
+ !path.isAbsolute(relativeToWorkspace);
172
+
173
+ if (canConvertWithSymlinks) {
174
+ return { success: true, relativePath: relativeToWorkspace };
175
+ }
176
+
177
+ // Could not convert - path is outside workspace
178
+ return { success: false, reason: "outside_workspace" };
179
+ }
@@ -0,0 +1,15 @@
1
+ export enum QueryKey {
2
+ Models = "models",
3
+ CreateConversation = "create-conversation",
4
+ GetConversation = "get-conversation",
5
+
6
+ Grep = "grep",
7
+ FileSearch = "file-search",
8
+ ListDir = "list-dir",
9
+ ReadFile = "read-file",
10
+ DeleteFile = "delete-file",
11
+ EditFile = "edit-file",
12
+ RunTerminal = "run-terminal",
13
+ GetTerminalOutput = "get-terminal-output",
14
+ MCPTool = "mcp-tool",
15
+ }