@abacus-ai/cli 2.0.0-canary.0 → 2.0.0-canary.2

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 (198) hide show
  1. package/dist/index.mjs +807 -561
  2. package/package.json +4 -1
  3. package/.oxlintrc.json +0 -8
  4. package/resources/abacus.ico +0 -0
  5. package/resources/entitlements.plist +0 -9
  6. package/src/__e2e__/README.md +0 -196
  7. package/src/__e2e__/agent-interactions.e2e.test.tsx +0 -61
  8. package/src/__e2e__/cli-commands.e2e.test.tsx +0 -77
  9. package/src/__e2e__/conversation-throttle.e2e.test.ts +0 -453
  10. package/src/__e2e__/conversation.e2e.test.tsx +0 -56
  11. package/src/__e2e__/diff-preview.e2e.test.tsx +0 -3399
  12. package/src/__e2e__/file-creation.e2e.test.tsx +0 -149
  13. package/src/__e2e__/helpers/test-helpers.ts +0 -450
  14. package/src/__e2e__/keyboard-navigation.e2e.test.tsx +0 -34
  15. package/src/__e2e__/llm-models.e2e.test.ts +0 -402
  16. package/src/__e2e__/mcp/mcp-callback-flow.e2e.test.tsx +0 -71
  17. package/src/__e2e__/mcp/mcp-full-app-ui.e2e.test.tsx +0 -167
  18. package/src/__e2e__/mcp/mcp-ui-rendering.e2e.test.tsx +0 -185
  19. package/src/__e2e__/repl.e2e.test.tsx +0 -78
  20. package/src/__e2e__/shell-compatibility.e2e.test.tsx +0 -76
  21. package/src/__e2e__/theme-mcp.e2e.test.tsx +0 -98
  22. package/src/__e2e__/tool-permissions.e2e.test.tsx +0 -66
  23. package/src/args.ts +0 -22
  24. package/src/components/__tests__/react-compiler.test.tsx +0 -78
  25. package/src/components/__tests__/status-indicator.test.tsx +0 -403
  26. package/src/components/composer/__tests__/bash-runner.test.tsx +0 -263
  27. package/src/components/composer/agent-mode-indicator.tsx +0 -63
  28. package/src/components/composer/bash-runner.tsx +0 -54
  29. package/src/components/composer/commands/default-commands.tsx +0 -615
  30. package/src/components/composer/commands/handler.tsx +0 -59
  31. package/src/components/composer/commands/picker.tsx +0 -273
  32. package/src/components/composer/commands/registry.ts +0 -233
  33. package/src/components/composer/commands/types.ts +0 -33
  34. package/src/components/composer/context.tsx +0 -88
  35. package/src/components/composer/file-mention-picker.tsx +0 -83
  36. package/src/components/composer/help.tsx +0 -44
  37. package/src/components/composer/index.tsx +0 -1006
  38. package/src/components/composer/mentions.ts +0 -57
  39. package/src/components/composer/message-queue.tsx +0 -70
  40. package/src/components/composer/mode-panel.tsx +0 -35
  41. package/src/components/composer/modes/__tests__/bash-handler.test.tsx +0 -755
  42. package/src/components/composer/modes/__tests__/bash-renderer.test.tsx +0 -1108
  43. package/src/components/composer/modes/bash-handler.tsx +0 -132
  44. package/src/components/composer/modes/bash-renderer.tsx +0 -175
  45. package/src/components/composer/modes/default-handlers.tsx +0 -33
  46. package/src/components/composer/modes/index.ts +0 -41
  47. package/src/components/composer/modes/types.ts +0 -21
  48. package/src/components/composer/persistent-shell.ts +0 -283
  49. package/src/components/composer/process.ts +0 -65
  50. package/src/components/composer/types.ts +0 -9
  51. package/src/components/composer/use-mention-search.ts +0 -68
  52. package/src/components/error-boundry.tsx +0 -60
  53. package/src/components/exit-message.tsx +0 -29
  54. package/src/components/expanded-view.tsx +0 -74
  55. package/src/components/file-completion.tsx +0 -127
  56. package/src/components/header.tsx +0 -47
  57. package/src/components/logo.tsx +0 -37
  58. package/src/components/segments.tsx +0 -356
  59. package/src/components/status-indicator.tsx +0 -306
  60. package/src/components/tool-group-summary.tsx +0 -263
  61. package/src/components/tool-permissions/ask-user-question-permission-ui.tsx +0 -312
  62. package/src/components/tool-permissions/diff-preview.tsx +0 -355
  63. package/src/components/tool-permissions/index.ts +0 -5
  64. package/src/components/tool-permissions/permission-options.tsx +0 -375
  65. package/src/components/tool-permissions/permission-preview-header.tsx +0 -57
  66. package/src/components/tool-permissions/tool-permission-ui.tsx +0 -398
  67. package/src/components/tools/agent/ask-user-question.tsx +0 -101
  68. package/src/components/tools/agent/enter-plan-mode.tsx +0 -49
  69. package/src/components/tools/agent/exit-plan-mode.tsx +0 -75
  70. package/src/components/tools/agent/handoff-to-main.tsx +0 -27
  71. package/src/components/tools/agent/subagent.tsx +0 -37
  72. package/src/components/tools/agent/todo-write.tsx +0 -104
  73. package/src/components/tools/browser/close-tab.tsx +0 -58
  74. package/src/components/tools/browser/computer.tsx +0 -70
  75. package/src/components/tools/browser/get-interactive-elements.tsx +0 -54
  76. package/src/components/tools/browser/get-tab-content.tsx +0 -51
  77. package/src/components/tools/browser/navigate-to.tsx +0 -59
  78. package/src/components/tools/browser/new-tab.tsx +0 -60
  79. package/src/components/tools/browser/perform-action.tsx +0 -63
  80. package/src/components/tools/browser/refresh-tab.tsx +0 -43
  81. package/src/components/tools/browser/switch-tab.tsx +0 -58
  82. package/src/components/tools/filesystem/delete-file.tsx +0 -104
  83. package/src/components/tools/filesystem/edit.tsx +0 -220
  84. package/src/components/tools/filesystem/list-dir.tsx +0 -78
  85. package/src/components/tools/filesystem/read-file.tsx +0 -180
  86. package/src/components/tools/filesystem/upload-image.tsx +0 -76
  87. package/src/components/tools/ide/ide-diagnostics.tsx +0 -62
  88. package/src/components/tools/index.ts +0 -91
  89. package/src/components/tools/mcp/mcp-tool.tsx +0 -158
  90. package/src/components/tools/search/fetch-url.tsx +0 -73
  91. package/src/components/tools/search/file-search.tsx +0 -78
  92. package/src/components/tools/search/grep.tsx +0 -90
  93. package/src/components/tools/search/semantic-search.tsx +0 -66
  94. package/src/components/tools/search/web-search.tsx +0 -71
  95. package/src/components/tools/shared/index.tsx +0 -48
  96. package/src/components/tools/shared/zod-coercion.ts +0 -35
  97. package/src/components/tools/terminal/bash-tool-output.tsx +0 -174
  98. package/src/components/tools/terminal/get-terminal-output.tsx +0 -85
  99. package/src/components/tools/terminal/run-in-terminal.tsx +0 -106
  100. package/src/components/tools/types.ts +0 -16
  101. package/src/components/tools.tsx +0 -66
  102. package/src/components/ui/__tests__/divider.test.tsx +0 -61
  103. package/src/components/ui/__tests__/gradient.test.tsx +0 -125
  104. package/src/components/ui/__tests__/input.test.tsx +0 -166
  105. package/src/components/ui/__tests__/select.test.tsx +0 -273
  106. package/src/components/ui/__tests__/shimmer.test.tsx +0 -99
  107. package/src/components/ui/blinking-indicator.tsx +0 -25
  108. package/src/components/ui/divider.tsx +0 -162
  109. package/src/components/ui/gradient.tsx +0 -56
  110. package/src/components/ui/input.tsx +0 -228
  111. package/src/components/ui/select.tsx +0 -151
  112. package/src/components/ui/shimmer.tsx +0 -84
  113. package/src/context/agent-mode.tsx +0 -95
  114. package/src/context/extension-file.tsx +0 -136
  115. package/src/context/network-activity.tsx +0 -45
  116. package/src/context/notification.tsx +0 -62
  117. package/src/context/shell-size.tsx +0 -49
  118. package/src/context/shell-title.tsx +0 -38
  119. package/src/entrypoints/print-mode.ts +0 -312
  120. package/src/entrypoints/repl.tsx +0 -401
  121. package/src/hooks/use-agent.ts +0 -15
  122. package/src/hooks/use-api-client.ts +0 -1
  123. package/src/hooks/use-available-height.ts +0 -8
  124. package/src/hooks/use-cleanup.ts +0 -29
  125. package/src/hooks/use-interrupt-manager.ts +0 -242
  126. package/src/hooks/use-models.ts +0 -22
  127. package/src/index.ts +0 -217
  128. package/src/lib/__tests__/ansi.test.ts +0 -255
  129. package/src/lib/__tests__/cli.test.ts +0 -122
  130. package/src/lib/__tests__/commands.test.ts +0 -325
  131. package/src/lib/__tests__/constants.test.ts +0 -15
  132. package/src/lib/__tests__/focusables.test.ts +0 -25
  133. package/src/lib/__tests__/fs.test.ts +0 -231
  134. package/src/lib/__tests__/markdown.test.tsx +0 -348
  135. package/src/lib/__tests__/mcpCommandHandler.test.ts +0 -173
  136. package/src/lib/__tests__/mcpManagement.test.ts +0 -38
  137. package/src/lib/__tests__/path-paste.test.ts +0 -144
  138. package/src/lib/__tests__/path.test.ts +0 -300
  139. package/src/lib/__tests__/queries.test.ts +0 -39
  140. package/src/lib/__tests__/standaloneMcpService.test.ts +0 -71
  141. package/src/lib/__tests__/text-buffer.test.ts +0 -328
  142. package/src/lib/__tests__/text-utils.test.ts +0 -32
  143. package/src/lib/__tests__/timing.test.ts +0 -78
  144. package/src/lib/__tests__/utils.test.ts +0 -238
  145. package/src/lib/__tests__/vim-buffer-actions.test.ts +0 -154
  146. package/src/lib/ansi.ts +0 -150
  147. package/src/lib/cli-push-server.ts +0 -112
  148. package/src/lib/cli.ts +0 -44
  149. package/src/lib/clipboard.ts +0 -226
  150. package/src/lib/command-utils.ts +0 -93
  151. package/src/lib/commands.ts +0 -270
  152. package/src/lib/constants.ts +0 -3
  153. package/src/lib/extension-connection.ts +0 -181
  154. package/src/lib/focusables.ts +0 -7
  155. package/src/lib/fs.ts +0 -533
  156. package/src/lib/markdown/code-block.tsx +0 -63
  157. package/src/lib/markdown/index.ts +0 -4
  158. package/src/lib/markdown/link.tsx +0 -19
  159. package/src/lib/markdown/markdown.tsx +0 -372
  160. package/src/lib/markdown/types.ts +0 -15
  161. package/src/lib/mcpCommandHandler.ts +0 -121
  162. package/src/lib/mcpManagement.ts +0 -44
  163. package/src/lib/path-paste.ts +0 -185
  164. package/src/lib/path.ts +0 -179
  165. package/src/lib/queries.ts +0 -15
  166. package/src/lib/standaloneMcpService.ts +0 -688
  167. package/src/lib/status-utils.ts +0 -237
  168. package/src/lib/test-utils.tsx +0 -72
  169. package/src/lib/text-buffer.ts +0 -2415
  170. package/src/lib/text-utils.ts +0 -272
  171. package/src/lib/timing.ts +0 -63
  172. package/src/lib/types.ts +0 -295
  173. package/src/lib/utils.ts +0 -182
  174. package/src/lib/vim-buffer-actions.ts +0 -732
  175. package/src/providers/agent.tsx +0 -1075
  176. package/src/providers/api-client.tsx +0 -43
  177. package/src/services/logger.ts +0 -85
  178. package/src/terminal/detection.ts +0 -187
  179. package/src/terminal/exit.ts +0 -279
  180. package/src/terminal/notification.ts +0 -83
  181. package/src/terminal/progress.ts +0 -201
  182. package/src/terminal/setup.ts +0 -797
  183. package/src/terminal/suspend.ts +0 -58
  184. package/src/terminal/types.ts +0 -51
  185. package/src/theme/context.tsx +0 -57
  186. package/src/theme/index.ts +0 -4
  187. package/src/theme/themed.tsx +0 -35
  188. package/src/theme/themes.json +0 -546
  189. package/src/theme/types.ts +0 -110
  190. package/src/tools/types.ts +0 -59
  191. package/src/tools/utils/__tests__/zod-coercion.test.ts +0 -33
  192. package/src/tools/utils/tool-ui-components.tsx +0 -631
  193. package/src/tools/utils/zod-coercion.ts +0 -35
  194. package/tsconfig.json +0 -11
  195. package/tsconfig.node.json +0 -29
  196. package/tsconfig.test.json +0 -27
  197. package/tsdown.config.ts +0 -17
  198. package/vitest.config.ts +0 -76
@@ -1,154 +0,0 @@
1
- // @vitest-environment node
2
-
3
- import { describe, expect, it } from "vitest";
4
-
5
- import type { TextBufferState } from "../text-buffer.js";
6
-
7
- import { handleVimAction, assumeExhaustive } from "../vim-buffer-actions.js";
8
-
9
- describe.concurrent("vim-buffer-actions", () => {
10
- const createState = (
11
- lines: string[] = ["hello world"],
12
- cursorRow = 0,
13
- cursorCol = 0,
14
- ): TextBufferState => ({
15
- lines,
16
- cursorRow,
17
- cursorCol,
18
- preferredCol: null,
19
- undoStack: [],
20
- redoStack: [],
21
- clipboard: null,
22
- selectionAnchor: null,
23
- viewportWidth: 80,
24
- viewportHeight: 24,
25
- visualLayout: {
26
- visualLines: lines,
27
- logicalToVisualMap: lines.map((_, i) => [[i, 0]]),
28
- visualToLogicalMap: lines.map((_, i) => [i, 0]),
29
- },
30
- });
31
-
32
- describe.concurrent("assumeExhaustive", () => {
33
- it("should be a function", () => {
34
- expect(typeof assumeExhaustive).toBe("function");
35
- });
36
-
37
- it("should not throw when called", () => {
38
- expect(() => assumeExhaustive(null as never)).not.toThrow();
39
- });
40
- });
41
-
42
- describe.concurrent("handleVimAction", () => {
43
- it("should handle vim_move_left", () => {
44
- const state = createState(["hello"], 0, 5);
45
- const newState = handleVimAction(state, { type: "vim_move_left", payload: { count: 1 } });
46
- expect(newState.cursorCol).toBe(4);
47
- });
48
-
49
- it("should handle vim_move_right", () => {
50
- const state = createState(["hello"], 0, 0);
51
- const newState = handleVimAction(state, { type: "vim_move_right", payload: { count: 1 } });
52
- expect(newState.cursorCol).toBe(1);
53
- });
54
-
55
- it("should handle vim_move_to_line_start", () => {
56
- const state = createState(["hello world"], 0, 5);
57
- const newState = handleVimAction(state, { type: "vim_move_to_line_start" });
58
- expect(newState.cursorCol).toBe(0);
59
- });
60
-
61
- it("should handle vim_move_to_line_end", () => {
62
- // TODO: IMPROVEMENT - Test should verify exact position. For "hello" (5 chars),
63
- // cursor should be at position 4 (last character), not just > 0.
64
- // Current implementation: lineLength > 0 ? lineLength - 1 : 0
65
- const state = createState(["hello"], 0, 0);
66
- const newState = handleVimAction(state, { type: "vim_move_to_line_end" });
67
- expect(newState.cursorCol).toBeGreaterThan(0);
68
- });
69
-
70
- it("should handle vim_move_to_first_nonwhitespace", () => {
71
- const state = createState([" hello"], 0, 0);
72
- const newState = handleVimAction(state, { type: "vim_move_to_first_nonwhitespace" });
73
- expect(newState.cursorCol).toBe(3);
74
- });
75
-
76
- it("should handle vim_move_to_first_line", () => {
77
- const state = createState(["line1", "line2", "line3"], 2, 0);
78
- const newState = handleVimAction(state, { type: "vim_move_to_first_line" });
79
- expect(newState.cursorRow).toBe(0);
80
- });
81
-
82
- it("should handle vim_move_to_last_line", () => {
83
- const state = createState(["line1", "line2", "line3"], 0, 0);
84
- const newState = handleVimAction(state, { type: "vim_move_to_last_line" });
85
- expect(newState.cursorRow).toBe(2);
86
- });
87
-
88
- it("should handle vim_move_to_line", () => {
89
- // TODO: CLARIFICATION - The API is confusing: lineNumber is 1-indexed (user-facing)
90
- // but internally converted to 0-indexed. Consider documenting this clearly or
91
- // using a more explicit parameter name like `lineNumberOneIndexed`.
92
- // Current behavior: lineNumber 2 -> row index 1 (second line)
93
- const state = createState(["line1", "line2", "line3"], 0, 0);
94
- const newState = handleVimAction(state, {
95
- type: "vim_move_to_line",
96
- payload: { lineNumber: 2 },
97
- });
98
- expect(newState.cursorRow).toBe(1); // 0-indexed
99
- });
100
-
101
- it("should handle vim_delete_char", () => {
102
- const state = createState(["hello"], 0, 2);
103
- const newState = handleVimAction(state, { type: "vim_delete_char", payload: { count: 1 } });
104
- expect(newState.lines[0].length).toBeLessThan(state.lines[0].length);
105
- });
106
-
107
- it("should handle vim_move_word_forward", () => {
108
- const state = createState(["hello world test"], 0, 0);
109
- const newState = handleVimAction(state, {
110
- type: "vim_move_word_forward",
111
- payload: { count: 1 },
112
- });
113
- expect(newState.cursorCol).toBeGreaterThan(0);
114
- });
115
-
116
- it("should handle vim_move_word_backward", () => {
117
- const state = createState(["hello world"], 0, 11);
118
- const newState = handleVimAction(state, {
119
- type: "vim_move_word_backward",
120
- payload: { count: 1 },
121
- });
122
- expect(newState.cursorCol).toBeLessThan(11);
123
- });
124
-
125
- it("should handle vim_delete_word_forward", () => {
126
- const state = createState(["hello world"], 0, 0);
127
- const newState = handleVimAction(state, {
128
- type: "vim_delete_word_forward",
129
- payload: { count: 1 },
130
- });
131
- expect(newState.lines[0]).not.toContain("hello");
132
- });
133
-
134
- it("should handle vim_delete_to_end_of_line", () => {
135
- const state = createState(["hello world"], 0, 5);
136
- const newState = handleVimAction(state, { type: "vim_delete_to_end_of_line" });
137
- expect(newState.lines[0]).toBe("hello");
138
- });
139
-
140
- it("should handle vim_open_line_below", () => {
141
- const state = createState(["line1"], 0, 5);
142
- const newState = handleVimAction(state, { type: "vim_open_line_below" });
143
- expect(newState.lines.length).toBe(2);
144
- expect(newState.cursorRow).toBe(1);
145
- });
146
-
147
- it("should handle vim_open_line_above", () => {
148
- const state = createState(["line1"], 0, 5);
149
- const newState = handleVimAction(state, { type: "vim_open_line_above" });
150
- expect(newState.lines.length).toBe(2);
151
- expect(newState.cursorRow).toBe(0);
152
- });
153
- });
154
- });
package/src/lib/ansi.ts DELETED
@@ -1,150 +0,0 @@
1
- export enum ANSI {
2
- ESC = "\x1b",
3
- BEL = "\x07",
4
- ST = "\x1b\\",
5
- ENABLE_FOCUS_REPORTING = `${ANSI.ESC}[?1004h`,
6
- DISABLE_FOCUS_REPORTING = `${ANSI.ESC}[?1004l`,
7
- FOCUS_IN = `${ANSI.ESC}[I`,
8
- FOCUS_OUT = `${ANSI.ESC}[O`,
9
- SHOW_CURSOR = `${ANSI.ESC}[?25h`,
10
- HIDE_CURSOR = `${ANSI.ESC}[?25l`,
11
- SET_TITLE = `${ANSI.ESC}]2;`,
12
- RESET_SGR = `${ANSI.ESC}[0m`,
13
- SOFT_RESET = `${ANSI.ESC}[!p`,
14
- HARD_RESET = `${ANSI.ESC}c`,
15
- ENABLE_BRACKETED_PASTE = `${ANSI.ESC}[?2004h`,
16
- DISABLE_BRACKETED_PASTE = `${ANSI.ESC}[?2004l`,
17
- PASTE_MODE_PREFIX = `${ANSI.ESC}[200~`,
18
- PASTE_MODE_SUFFIX = `${ANSI.ESC}[201~`,
19
- DIM = `${ANSI.ESC}[2m`,
20
- BOLD = `${ANSI.ESC}[1m`,
21
- RESET_BOLD = `${ANSI.ESC}[22m`,
22
- ITALIC = `${ANSI.ESC}[3m`,
23
- RESET_ITALIC = `${ANSI.ESC}[23m`,
24
- STRIKETHROUGH = `${ANSI.ESC}[9m`,
25
- RESET_STRIKETHROUGH = `${ANSI.ESC}[29m`,
26
- UNDERLINE = `${ANSI.ESC}[4m`,
27
- RESET_UNDERLINE = `${ANSI.ESC}[24m`,
28
- }
29
-
30
- /**
31
- * OSC 9 - iTerm2 notification
32
- * Format: ESC ] 9 ; message BEL
33
- */
34
- export function osc9(message: string): string {
35
- return `${ANSI.ESC}]9;\n\n${message}${ANSI.BEL}`;
36
- }
37
-
38
- /**
39
- * OSC 777 - Ghostty/urxvt notification
40
- * Format: ESC ] 777 ; notify ; title ; body BEL
41
- */
42
- export function osc777(title: string, body: string): string {
43
- return `${ANSI.ESC}]777;notify;${title};${body}${ANSI.BEL}`;
44
- }
45
-
46
- /**
47
- * OSC 99 - Kitty desktop notification
48
- * Format: ESC ] 99 ; metadata ; payload ST
49
- */
50
- export function osc99(title: string, body: string): string {
51
- const notificationId = Math.floor(Math.random() * 10000);
52
- const titlePart = `${ANSI.ESC}]99;i=${notificationId}:d=0:p=title;${title}${ANSI.ST}`;
53
- const bodyPart = `${ANSI.ESC}]99;i=${notificationId}:p=body;${body}${ANSI.ST}`;
54
- const donePart = `${ANSI.ESC}]99;i=${notificationId}:d=1:a=focus;${ANSI.ST}`;
55
- return titlePart + bodyPart + donePart;
56
- }
57
-
58
- export type ProgressState = 0 | 1 | 2 | 3 | 4;
59
-
60
- /**
61
- * OSC 9;4 - Progress bar (ConEmu/Ghostty/iTerm2 style)
62
- * Format: ESC ] 9 ; 4 ; state ; progress [BEL/ST]
63
- * @param state - 0 (hide), 1 (default), 2 (error), 3 (indeterminate), 4 (warning)
64
- * @param progress - Progress percentage (0-100), required when state is 1, 2, or 4
65
- */
66
- export function osc9Progress(state: ProgressState, progress?: number): string {
67
- const p = progress !== undefined ? Math.max(0, Math.min(100, Math.round(progress))) : 0;
68
- const payload = `9;4;${state};${p}`;
69
- // Emit both terminators for maximum compatibility (iTerm2 prefers BEL, Ghostty prefers ST)
70
- return `${ANSI.ESC}]${payload}${ANSI.BEL}${ANSI.ESC}]${payload}${ANSI.ST}`;
71
- }
72
-
73
- /**
74
- * OSC 1337 - iTerm2 Progress indicator
75
- */
76
- function sanitizeItermValue(value: string): string {
77
- return value
78
- .replaceAll("\x1b", "")
79
- .replaceAll("\x07", "")
80
- .replaceAll("\n", " ")
81
- .replaceAll("\r", " ")
82
- .replaceAll(";", ":")
83
- .trim()
84
- .slice(0, 120);
85
- }
86
-
87
- function itermOsc(payload: string): string {
88
- // Emit both terminators for maximum compatibility: BEL and ST.
89
- return `${ANSI.ESC}]${payload}${ANSI.BEL}${ANSI.ESC}]${payload}${ANSI.ST}`;
90
- }
91
-
92
- export function osc1337ProgressClear(): string {
93
- return itermOsc("1337;Progress=");
94
- }
95
-
96
- export function osc1337ProgressState(state: 0 | 1 | 2): string {
97
- return itermOsc(`1337;Progress=${state}`);
98
- }
99
-
100
- export function osc1337ProgressPercent(percent: number, message?: string): string {
101
- const progressValue = Math.max(0, Math.min(100, Math.round(percent)));
102
- const msg = message ? sanitizeItermValue(message) : "";
103
-
104
- if (msg) {
105
- return (
106
- itermOsc(`1337;Progress=${progressValue};${msg}`) +
107
- itermOsc(`1337;Progress=message=${msg};percent=${progressValue}`)
108
- );
109
- }
110
-
111
- return itermOsc(`1337;Progress=${progressValue}`);
112
- }
113
-
114
- /**
115
- * Creates a regex pattern that matches ANSI escape sequences
116
- * @param options.onlyFirst - If true, matches only the first occurrence
117
- * @returns Regular expression for matching ANSI sequences
118
- */
119
- export function ansiRegex({ onlyFirst = false } = {}): RegExp {
120
- // Valid string terminator sequences are BEL, ESC\, and 0x9c
121
- const ST = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
122
-
123
- // OSC sequences only: ESC ] ... ST (non-greedy until the first ST)
124
- const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
125
-
126
- // CSI and related: ESC/C1, optional intermediates, optional params (supports ; and :) then final byte
127
- const csi = "[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]";
128
-
129
- const pattern = `${osc}|${csi}`;
130
-
131
- return new RegExp(pattern, onlyFirst ? undefined : "g");
132
- }
133
-
134
- const stripAnsiRegex = ansiRegex();
135
-
136
- /**
137
- * Strip ANSI escape codes from a string
138
- * @param string - The string to strip ANSI codes from
139
- * @returns The string with ANSI codes removed
140
- */
141
- export function stripAnsi(string: string): string {
142
- if (typeof string !== "string") {
143
- throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
144
- }
145
-
146
- // Even though the regex is global, we don't need to reset the `.lastIndex`
147
- // because unlike `.exec()` and `.test()`, `.replace()` does it automatically
148
- // and doing it manually has a performance penalty.
149
- return string.replace(stripAnsiRegex, "");
150
- }
@@ -1,112 +0,0 @@
1
- import type { SocketServer } from "@codellm/comms";
2
-
3
- import { cliContract, createSocketServer } from "@codellm/comms";
4
- import { product } from "@codellm/product";
5
- import { implement } from "@orpc/server";
6
- import { StandardRPCHandler } from "@orpc/server/standard";
7
- import * as fs from "node:fs";
8
- import * as os from "node:os";
9
- import * as path from "node:path";
10
-
11
- export interface EditorState {
12
- file: string | undefined;
13
- startLine: number | undefined;
14
- endLine: number | undefined;
15
- }
16
-
17
- export type ToolDecisionValue = "accept" | "reject" | "allowAlways";
18
-
19
- export class CliPushServer {
20
- private socketServer: SocketServer | undefined;
21
- readonly socketPath: string;
22
- private editorState: EditorState = {
23
- file: undefined,
24
- startLine: undefined,
25
- endLine: undefined,
26
- };
27
- private onEditorStateCallback: ((state: EditorState) => void) | null = null;
28
- private pendingDecisions = new Map<
29
- string,
30
- { resolve: (d: ToolDecisionValue) => void; cleanup: () => void }
31
- >();
32
-
33
- constructor() {
34
- const socketDir = path.join(os.homedir(), product.configDirName, "cli");
35
- this.socketPath = path.join(socketDir, `${process.pid}.sock`);
36
- }
37
-
38
- getEditorState(): EditorState {
39
- return { ...this.editorState };
40
- }
41
-
42
- onEditorChange(callback: (state: EditorState) => void): void {
43
- this.onEditorStateCallback = callback;
44
- }
45
-
46
- waitForDecision(toolId: string, signal?: AbortSignal): Promise<ToolDecisionValue> {
47
- return new Promise<ToolDecisionValue>((resolve, reject) => {
48
- if (signal?.aborted) {
49
- reject(new Error("Aborted"));
50
- return;
51
- }
52
-
53
- const cleanup = () => {
54
- signal?.removeEventListener("abort", onAbort);
55
- this.pendingDecisions.delete(toolId);
56
- };
57
- const onAbort = () => {
58
- cleanup();
59
- reject(new Error("Aborted"));
60
- };
61
- signal?.addEventListener("abort", onAbort, { once: true });
62
-
63
- this.pendingDecisions.set(toolId, {
64
- resolve: (d) => {
65
- cleanup();
66
- resolve(d);
67
- },
68
- cleanup,
69
- });
70
- });
71
- }
72
-
73
- async start(): Promise<void> {
74
- const socketDir = path.dirname(this.socketPath);
75
- if (!fs.existsSync(socketDir)) {
76
- fs.mkdirSync(socketDir, { recursive: true });
77
- }
78
-
79
- const cc = implement(cliContract);
80
-
81
- type EditorStateInput = { file: string | null; startLine?: number; endLine?: number };
82
- type ToolDecisionInput = { toolId: string; decision: "accept" | "reject" | "allowAlways" };
83
-
84
- const router = cc.router({
85
- editorStateChanged: cc.editorStateChanged.handler(
86
- async ({ input }: { input: EditorStateInput }) => {
87
- this.editorState = {
88
- file: input.file ?? undefined,
89
- startLine: input.startLine,
90
- endLine: input.endLine,
91
- };
92
- this.onEditorStateCallback?.(this.editorState);
93
- return { ok: true };
94
- },
95
- ),
96
- toolDecision: cc.toolDecision.handler(async ({ input }: { input: ToolDecisionInput }) => {
97
- const pending = this.pendingDecisions.get(input.toolId);
98
- if (pending) pending.resolve(input.decision);
99
- return { ok: true };
100
- }),
101
- });
102
-
103
- this.socketServer = await createSocketServer(this.socketPath, new StandardRPCHandler(router));
104
- }
105
-
106
- dispose(): void {
107
- for (const [, entry] of this.pendingDecisions) entry.cleanup();
108
- this.pendingDecisions.clear();
109
- this.socketServer?.dispose();
110
- this.socketServer = undefined;
111
- }
112
- }
package/src/lib/cli.ts DELETED
@@ -1,44 +0,0 @@
1
- import { spawn } from "child_process";
2
-
3
- export async function* runCli(command: string, args: string[]): AsyncGenerator<string> {
4
- const proc = spawn(command, args, {
5
- stdio: ["ignore", "pipe", "pipe"],
6
- cwd: process.cwd(),
7
- });
8
-
9
- const streams = [proc.stdout, proc.stderr];
10
-
11
- const readers = streams.map((stream) => stream[Symbol.asyncIterator]());
12
-
13
- try {
14
- while (true) {
15
- const results = await Promise.all(readers.map((reader) => reader.next()));
16
- let doneCount = 0;
17
- for (let i = 0; i < results.length; i++) {
18
- const { value, done } = results[i];
19
- if (done) {
20
- doneCount++;
21
- continue;
22
- }
23
- if (value) {
24
- yield value.toString();
25
- }
26
- }
27
- if (doneCount === readers.length) {
28
- break;
29
- }
30
- }
31
- } finally {
32
- // Attempt graceful shutdown
33
- proc.kill("SIGTERM");
34
-
35
- const timeout = setTimeout(() => {
36
- // Force termination if still running
37
- if (!proc.killed) {
38
- proc.kill("SIGKILL");
39
- }
40
- }, 5000);
41
-
42
- proc.on("close", () => clearTimeout(timeout));
43
- }
44
- }
@@ -1,226 +0,0 @@
1
- import { execFileSync } from "node:child_process";
2
- import { copyFileSync, existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
3
- import path from "node:path";
4
-
5
- // ── macOS screenshot detection ────────────────────────────────────────────
6
-
7
- const MACOS_TEMP_SCREENSHOT_PATTERN =
8
- /\/var\/folders\/.*\/T\/TemporaryItems\/.*screencaptureui.*\//i;
9
- const SCREENSHOTS_DIR = ".abacusai/screenshots";
10
-
11
- export function isMacOSTempScreenshot(filePath: string): boolean {
12
- return MACOS_TEMP_SCREENSHOT_PATTERN.test(filePath);
13
- }
14
-
15
- export function copyTempScreenshotToProject(tempPath: string): string | null {
16
- try {
17
- const unescaped = tempPath.replace(/\\ /g, " ");
18
- const cleaned = stripFileProtocol(unescaped);
19
-
20
- if (!existsSync(cleaned)) {
21
- return null;
22
- }
23
-
24
- const screenshotsDir = path.resolve(process.cwd(), SCREENSHOTS_DIR);
25
- if (!existsSync(screenshotsDir)) {
26
- mkdirSync(screenshotsDir, { recursive: true });
27
- }
28
-
29
- const filename = path.basename(cleaned);
30
- const destPath = path.join(screenshotsDir, filename);
31
-
32
- copyFileSync(cleaned, destPath);
33
- return destPath;
34
- } catch {
35
- return null;
36
- }
37
- }
38
-
39
- /**
40
- * Strips the file:// protocol prefix from a path if present.
41
- * Also percent-decodes URI-encoded characters (e.g. %20 → space).
42
- */
43
- export function stripFileProtocol(text: string): string {
44
- if (text.startsWith("file://")) {
45
- try {
46
- return decodeURIComponent(text.slice("file://".length));
47
- } catch {
48
- return text.slice("file://".length);
49
- }
50
- }
51
- return text;
52
- }
53
-
54
- // ── Linux clipboard tool detection ───────────────────────────────────────
55
-
56
- let linuxClipboardTool: "wl-paste" | "xclip" | null | undefined;
57
-
58
- function detectLinuxClipboardTool(): "wl-paste" | "xclip" | null {
59
- if (linuxClipboardTool !== undefined) return linuxClipboardTool;
60
- try {
61
- execFileSync("which", ["wl-paste"], { stdio: "ignore" });
62
- linuxClipboardTool = "wl-paste";
63
- return "wl-paste";
64
- } catch {
65
- // not found
66
- }
67
- try {
68
- execFileSync("which", ["xclip"], { stdio: "ignore" });
69
- linuxClipboardTool = "xclip";
70
- return "xclip";
71
- } catch {
72
- // not found
73
- }
74
- linuxClipboardTool = null;
75
- return null;
76
- }
77
-
78
- // ── Clipboard image detection ─────────────────────────────────────────────
79
-
80
- export async function clipboardHasImage(): Promise<boolean> {
81
- try {
82
- if (process.platform === "darwin") {
83
- const result = execFileSync("osascript", ["-e", "clipboard info"], {
84
- encoding: "utf8",
85
- timeout: 2000,
86
- });
87
- return /PNGf|JPEG|TIFF|GIF/i.test(result);
88
- }
89
- if (process.platform === "win32") {
90
- const result = execFileSync(
91
- "powershell",
92
- [
93
- "-NoProfile",
94
- "-Command",
95
- "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Clipboard]::ContainsImage()",
96
- ],
97
- { encoding: "utf8", timeout: 2000 },
98
- );
99
- return result.trim().toLowerCase() === "true";
100
- }
101
- // Linux
102
- const tool = detectLinuxClipboardTool();
103
- if (tool === "wl-paste") {
104
- const result = execFileSync("wl-paste", ["--list-types"], {
105
- encoding: "utf8",
106
- timeout: 2000,
107
- });
108
- return result.includes("image/");
109
- }
110
- if (tool === "xclip") {
111
- const result = execFileSync("xclip", ["-selection", "clipboard", "-t", "TARGETS", "-o"], {
112
- encoding: "utf8",
113
- timeout: 2000,
114
- });
115
- return result.includes("image/");
116
- }
117
- } catch {
118
- // ignore — clipboard tools may not be available
119
- }
120
- return false;
121
- }
122
-
123
- // ── Save clipboard image to disk ──────────────────────────────────────────
124
-
125
- export async function saveClipboardImage(cwd: string): Promise<string | null> {
126
- try {
127
- const screenshotsDir = path.resolve(cwd, SCREENSHOTS_DIR);
128
- if (!existsSync(screenshotsDir)) {
129
- mkdirSync(screenshotsDir, { recursive: true });
130
- }
131
-
132
- const timestamp = Date.now();
133
- const destPath = path.join(screenshotsDir, `clipboard-${timestamp}.png`);
134
-
135
- if (process.platform === "darwin") {
136
- // Try PNG first, then JPEG
137
- const script = `
138
- set destFile to "${destPath}"
139
- try
140
- set imgData to (clipboard info for «class PNGf»)
141
- set fileRef to open for access POSIX file destFile with write permission
142
- set eof of fileRef to 0
143
- write (clipboard as «class PNGf») to fileRef
144
- close access fileRef
145
- on error
146
- try
147
- set imgData to (clipboard info for «class JPEG»)
148
- set fileRef to open for access POSIX file destFile with write permission
149
- set eof of fileRef to 0
150
- write (clipboard as «class JPEG») to fileRef
151
- close access fileRef
152
- end try
153
- end try
154
- `;
155
- execFileSync("osascript", ["-e", script], { timeout: 5000 });
156
- } else if (process.platform === "win32") {
157
- execFileSync(
158
- "powershell",
159
- [
160
- "-NoProfile",
161
- "-Command",
162
- `Add-Type -Assembly System.Windows.Forms; [System.Windows.Forms.Clipboard]::GetImage().Save("${destPath}", [System.Drawing.Imaging.ImageFormat]::Png)`,
163
- ],
164
- { timeout: 5000 },
165
- );
166
- } else {
167
- const tool = detectLinuxClipboardTool();
168
- if (tool === "wl-paste") {
169
- const imgData = execFileSync("wl-paste", ["--no-newline", "--type", "image/png"], {
170
- timeout: 5000,
171
- });
172
- writeFileSync(destPath, imgData);
173
- } else if (tool === "xclip") {
174
- const imgData = execFileSync(
175
- "xclip",
176
- ["-selection", "clipboard", "-t", "image/png", "-o"],
177
- { timeout: 5000 },
178
- );
179
- writeFileSync(destPath, imgData);
180
- } else {
181
- return null;
182
- }
183
- }
184
-
185
- // Verify file exists and has content (prevents empty-file false positives)
186
- try {
187
- const stat = statSync(destPath);
188
- if (stat.size === 0) return null;
189
- } catch {
190
- return null;
191
- }
192
-
193
- return destPath;
194
- } catch {
195
- return null;
196
- }
197
- }
198
-
199
- // ── Read clipboard text ───────────────────────────────────────────────────
200
-
201
- export async function readClipboardText(): Promise<string | null> {
202
- try {
203
- if (process.platform === "darwin") {
204
- return execFileSync("pbpaste", { encoding: "utf8", timeout: 2000 });
205
- }
206
- if (process.platform === "win32") {
207
- return execFileSync("powershell", ["-NoProfile", "-Command", "Get-Clipboard"], {
208
- encoding: "utf8",
209
- timeout: 2000,
210
- });
211
- }
212
- const tool = detectLinuxClipboardTool();
213
- if (tool === "wl-paste") {
214
- return execFileSync("wl-paste", ["--no-newline"], { encoding: "utf8", timeout: 2000 });
215
- }
216
- if (tool === "xclip") {
217
- return execFileSync("xclip", ["-selection", "clipboard", "-o"], {
218
- encoding: "utf8",
219
- timeout: 2000,
220
- });
221
- }
222
- } catch {
223
- // ignore
224
- }
225
- return null;
226
- }