@travisennis/acai 0.0.1 → 0.0.3

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 (324) hide show
  1. package/README.md +3 -4
  2. package/dist/commands/health-command.d.ts +2 -0
  3. package/dist/commands/health-command.js +59 -0
  4. package/dist/commands/manager.js +2 -0
  5. package/dist/commands/paste-command.d.ts +1 -1
  6. package/dist/commands/paste-command.js +155 -11
  7. package/dist/commands/reset-command.js +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +2 -1
  10. package/dist/models/openrouter-provider.d.ts +4 -1
  11. package/dist/models/openrouter-provider.js +46 -4
  12. package/dist/models/providers.d.ts +1 -1
  13. package/dist/prompts/manager.d.ts +1 -0
  14. package/dist/prompts/manager.js +10 -0
  15. package/dist/prompts.js +8 -6
  16. package/dist/repl.js +49 -26
  17. package/dist/terminal/formatting.d.ts +16 -5
  18. package/dist/terminal/formatting.js +40 -6
  19. package/dist/terminal/index.d.ts +1 -1
  20. package/dist/terminal/index.js +54 -14
  21. package/dist/terminal/markdown.js +0 -1
  22. package/dist/terminal/supports-color.d.ts +16 -0
  23. package/dist/terminal/supports-color.js +121 -0
  24. package/dist/terminal/supports-hyperlinks.d.ts +7 -0
  25. package/dist/terminal/supports-hyperlinks.js +98 -0
  26. package/dist/tools/bash.js +95 -117
  27. package/dist/tools/code-interpreter.js +11 -1
  28. package/dist/tools/command-validation.d.ts +7 -3
  29. package/dist/tools/command-validation.js +67 -23
  30. package/dist/tools/delete-file.d.ts +4 -1
  31. package/dist/tools/delete-file.js +47 -3
  32. package/dist/tools/git-utils.d.ts +6 -0
  33. package/dist/tools/git-utils.js +89 -12
  34. package/dist/tools/grep.d.ts +20 -0
  35. package/dist/tools/grep.js +128 -40
  36. package/dist/tools/index.d.ts +2 -18
  37. package/dist/tools/index.js +4 -18
  38. package/package.json +30 -20
  39. package/.acai/acai.json +0 -9
  40. package/.acai/prompts/add-openrouter-model.md +0 -13
  41. package/.acai/prompts/project-status.md +0 -4
  42. package/.acai/prompts/update-architecture-document.md +0 -9
  43. package/.acai/rules/learned-rules.md +0 -9
  44. package/.ai/docs/available-tools.txt +0 -3
  45. package/.ai/docs/cognitive_complexity_refactoring_progress.md +0 -65
  46. package/.ai/docs/deleted_tools.md +0 -168
  47. package/.ai/docs/deleted_tools_88ced9ef.md +0 -56
  48. package/.ai/docs/image-pasting.md +0 -46
  49. package/.ai/docs/initialize-app.md +0 -117
  50. package/.ai/docs/issue-4-plan.md +0 -44
  51. package/.ai/docs/marked-renderer-debug.md +0 -15
  52. package/.ai/docs/marked-renderer-refactor-plan.md +0 -64
  53. package/.ai/docs/memory-use-cases.md +0 -55
  54. package/.ai/docs/prompt-consistency.md +0 -31
  55. package/.ai/docs/refactoring-tools.md +0 -98
  56. package/.ai/docs/system-prompt-update.md +0 -174
  57. package/.ai/docs/system_prompt.txt +0 -210
  58. package/.ai/docs/tasks.md +0 -49
  59. package/.ai/plan.md +0 -131
  60. package/.ai/prompt.md +0 -1
  61. package/.ai/scripts/fetch_models.js +0 -27
  62. package/.ai/scripts/generateSystemPrompt.ts +0 -15
  63. package/.ai/scripts/list-tools.mjs +0 -4
  64. package/.ai/scripts/p5_geometric_shapes.js +0 -149
  65. package/.husky/commit-msg +0 -1
  66. package/.husky/pre-commit +0 -3
  67. package/.husky/pre-push +0 -1
  68. package/.ignore +0 -4
  69. package/AGENTS.md +0 -25
  70. package/ARCHITECTURE.md +0 -304
  71. package/TODO.md +0 -2
  72. package/biome.json +0 -61
  73. package/commitlint.config.js +0 -3
  74. package/dist/source/cli.d.ts +0 -19
  75. package/dist/source/cli.js +0 -116
  76. package/dist/source/commands/application-log-command.d.ts +0 -2
  77. package/dist/source/commands/application-log-command.js +0 -43
  78. package/dist/source/commands/clear-command.d.ts +0 -2
  79. package/dist/source/commands/clear-command.js +0 -12
  80. package/dist/source/commands/compact-command.d.ts +0 -2
  81. package/dist/source/commands/compact-command.js +0 -51
  82. package/dist/source/commands/copy-command.d.ts +0 -2
  83. package/dist/source/commands/copy-command.js +0 -51
  84. package/dist/source/commands/edit-command.d.ts +0 -2
  85. package/dist/source/commands/edit-command.js +0 -53
  86. package/dist/source/commands/edit-prompt-command.d.ts +0 -2
  87. package/dist/source/commands/edit-prompt-command.js +0 -25
  88. package/dist/source/commands/exit-command.d.ts +0 -2
  89. package/dist/source/commands/exit-command.js +0 -14
  90. package/dist/source/commands/files-command.d.ts +0 -2
  91. package/dist/source/commands/files-command.js +0 -63
  92. package/dist/source/commands/generate-rules-command.d.ts +0 -2
  93. package/dist/source/commands/generate-rules-command.js +0 -61
  94. package/dist/source/commands/help-command.d.ts +0 -2
  95. package/dist/source/commands/help-command.js +0 -19
  96. package/dist/source/commands/init-command.d.ts +0 -2
  97. package/dist/source/commands/init-command.js +0 -40
  98. package/dist/source/commands/last-log-command.d.ts +0 -2
  99. package/dist/source/commands/last-log-command.js +0 -76
  100. package/dist/source/commands/manager.d.ts +0 -22
  101. package/dist/source/commands/manager.js +0 -123
  102. package/dist/source/commands/model-command.d.ts +0 -2
  103. package/dist/source/commands/model-command.js +0 -84
  104. package/dist/source/commands/paste-command.d.ts +0 -2
  105. package/dist/source/commands/paste-command.js +0 -40
  106. package/dist/source/commands/prompt-command.d.ts +0 -2
  107. package/dist/source/commands/prompt-command.js +0 -111
  108. package/dist/source/commands/reset-command.d.ts +0 -2
  109. package/dist/source/commands/reset-command.js +0 -16
  110. package/dist/source/commands/rules-command.d.ts +0 -2
  111. package/dist/source/commands/rules-command.js +0 -68
  112. package/dist/source/commands/save-command.d.ts +0 -2
  113. package/dist/source/commands/save-command.js +0 -14
  114. package/dist/source/commands/types.d.ts +0 -26
  115. package/dist/source/commands/types.js +0 -1
  116. package/dist/source/commands/usage-command.d.ts +0 -2
  117. package/dist/source/commands/usage-command.js +0 -21
  118. package/dist/source/config.d.ts +0 -60
  119. package/dist/source/config.js +0 -193
  120. package/dist/source/conversation-analyzer.d.ts +0 -10
  121. package/dist/source/conversation-analyzer.js +0 -88
  122. package/dist/source/dedent.d.ts +0 -3
  123. package/dist/source/dedent.js +0 -38
  124. package/dist/source/formatting.d.ts +0 -17
  125. package/dist/source/formatting.js +0 -103
  126. package/dist/source/index.d.ts +0 -18
  127. package/dist/source/index.js +0 -213
  128. package/dist/source/logger.d.ts +0 -2
  129. package/dist/source/logger.js +0 -24
  130. package/dist/source/mentions.d.ts +0 -9
  131. package/dist/source/mentions.js +0 -182
  132. package/dist/source/messages.d.ts +0 -69
  133. package/dist/source/messages.js +0 -261
  134. package/dist/source/middleware/audit-message.d.ts +0 -5
  135. package/dist/source/middleware/audit-message.js +0 -95
  136. package/dist/source/middleware/index.d.ts +0 -2
  137. package/dist/source/middleware/index.js +0 -2
  138. package/dist/source/middleware/rate-limit.d.ts +0 -4
  139. package/dist/source/middleware/rate-limit.js +0 -17
  140. package/dist/source/models/ai-config.d.ts +0 -12
  141. package/dist/source/models/ai-config.js +0 -87
  142. package/dist/source/models/anthropic-provider.d.ts +0 -25
  143. package/dist/source/models/anthropic-provider.js +0 -184
  144. package/dist/source/models/deepseek-provider.d.ts +0 -20
  145. package/dist/source/models/deepseek-provider.js +0 -42
  146. package/dist/source/models/google-provider.d.ts +0 -19
  147. package/dist/source/models/google-provider.js +0 -56
  148. package/dist/source/models/manager.d.ts +0 -15
  149. package/dist/source/models/manager.js +0 -48
  150. package/dist/source/models/openai-provider.d.ts +0 -22
  151. package/dist/source/models/openai-provider.js +0 -70
  152. package/dist/source/models/openrouter-provider.d.ts +0 -36
  153. package/dist/source/models/openrouter-provider.js +0 -276
  154. package/dist/source/models/providers.d.ts +0 -33
  155. package/dist/source/models/providers.js +0 -116
  156. package/dist/source/models/xai-provider.d.ts +0 -20
  157. package/dist/source/models/xai-provider.js +0 -47
  158. package/dist/source/parsing.d.ts +0 -2
  159. package/dist/source/parsing.js +0 -18
  160. package/dist/source/prompts/manager.d.ts +0 -19
  161. package/dist/source/prompts/manager.js +0 -71
  162. package/dist/source/prompts.d.ts +0 -4
  163. package/dist/source/prompts.js +0 -158
  164. package/dist/source/repl-prompt.d.ts +0 -14
  165. package/dist/source/repl-prompt.js +0 -147
  166. package/dist/source/repl.d.ts +0 -27
  167. package/dist/source/repl.js +0 -431
  168. package/dist/source/terminal/formatting.d.ts +0 -37
  169. package/dist/source/terminal/formatting.js +0 -106
  170. package/dist/source/terminal/index.d.ts +0 -94
  171. package/dist/source/terminal/index.js +0 -420
  172. package/dist/source/terminal/markdown-utils.d.ts +0 -2
  173. package/dist/source/terminal/markdown-utils.js +0 -81
  174. package/dist/source/terminal/markdown.d.ts +0 -1
  175. package/dist/source/terminal/markdown.js +0 -111
  176. package/dist/source/terminal/types.d.ts +0 -71
  177. package/dist/source/terminal/types.js +0 -1
  178. package/dist/source/terminal-output.d.ts +0 -8
  179. package/dist/source/terminal-output.js +0 -213
  180. package/dist/source/terminal-output.test.d.ts +0 -8
  181. package/dist/source/terminal-output.test.js +0 -213
  182. package/dist/source/token-tracker.d.ts +0 -14
  183. package/dist/source/token-tracker.js +0 -53
  184. package/dist/source/token-utils.d.ts +0 -7
  185. package/dist/source/token-utils.js +0 -13
  186. package/dist/source/tools/agent.d.ts +0 -17
  187. package/dist/source/tools/agent.js +0 -87
  188. package/dist/source/tools/bash.d.ts +0 -19
  189. package/dist/source/tools/bash.js +0 -294
  190. package/dist/source/tools/code-interpreter.d.ts +0 -12
  191. package/dist/source/tools/code-interpreter.js +0 -131
  192. package/dist/source/tools/command-validation.d.ts +0 -8
  193. package/dist/source/tools/command-validation.js +0 -69
  194. package/dist/source/tools/delete-file.d.ts +0 -12
  195. package/dist/source/tools/delete-file.js +0 -56
  196. package/dist/source/tools/directory-tree.d.ts +0 -12
  197. package/dist/source/tools/directory-tree.js +0 -38
  198. package/dist/source/tools/edit-file.d.ts +0 -19
  199. package/dist/source/tools/edit-file.js +0 -107
  200. package/dist/source/tools/filesystem-utils.d.ts +0 -22
  201. package/dist/source/tools/filesystem-utils.js +0 -191
  202. package/dist/source/tools/git-utils.d.ts +0 -14
  203. package/dist/source/tools/git-utils.js +0 -64
  204. package/dist/source/tools/grep.d.ts +0 -17
  205. package/dist/source/tools/grep.js +0 -138
  206. package/dist/source/tools/index.d.ts +0 -161
  207. package/dist/source/tools/index.js +0 -209
  208. package/dist/source/tools/memory-read.d.ts +0 -13
  209. package/dist/source/tools/memory-read.js +0 -135
  210. package/dist/source/tools/memory-write.d.ts +0 -12
  211. package/dist/source/tools/memory-write.js +0 -83
  212. package/dist/source/tools/move-file.d.ts +0 -13
  213. package/dist/source/tools/move-file.js +0 -44
  214. package/dist/source/tools/read-file.d.ts +0 -17
  215. package/dist/source/tools/read-file.js +0 -86
  216. package/dist/source/tools/read-multiple-files.d.ts +0 -14
  217. package/dist/source/tools/read-multiple-files.js +0 -55
  218. package/dist/source/tools/save-file.d.ts +0 -17
  219. package/dist/source/tools/save-file.js +0 -98
  220. package/dist/source/tools/think.d.ts +0 -11
  221. package/dist/source/tools/think.js +0 -45
  222. package/dist/source/tools/types.d.ts +0 -29
  223. package/dist/source/tools/types.js +0 -14
  224. package/dist/source/tools/web-fetch.d.ts +0 -47
  225. package/dist/source/tools/web-fetch.js +0 -246
  226. package/dist/source/tools/web-search.d.ts +0 -13
  227. package/dist/source/tools/web-search.js +0 -80
  228. package/dist/source/utils/process.d.ts +0 -36
  229. package/dist/source/utils/process.js +0 -75
  230. package/dist/source/version.d.ts +0 -1
  231. package/dist/source/version.js +0 -21
  232. package/dist/terminal-output.d.ts +0 -8
  233. package/dist/terminal-output.js +0 -213
  234. package/dist/tools/memory-read.d.ts +0 -13
  235. package/dist/tools/memory-read.js +0 -135
  236. package/dist/tools/memory-write.d.ts +0 -12
  237. package/dist/tools/memory-write.js +0 -83
  238. package/knip.json +0 -5
  239. package/source/cli.ts +0 -172
  240. package/source/commands/application-log-command.ts +0 -53
  241. package/source/commands/clear-command.ts +0 -14
  242. package/source/commands/compact-command.ts +0 -64
  243. package/source/commands/copy-command.ts +0 -55
  244. package/source/commands/edit-command.ts +0 -63
  245. package/source/commands/edit-prompt-command.ts +0 -31
  246. package/source/commands/exit-command.ts +0 -18
  247. package/source/commands/files-command.ts +0 -85
  248. package/source/commands/generate-rules-command.ts +0 -82
  249. package/source/commands/help-command.ts +0 -27
  250. package/source/commands/init-command.ts +0 -48
  251. package/source/commands/last-log-command.ts +0 -88
  252. package/source/commands/manager.ts +0 -151
  253. package/source/commands/model-command.ts +0 -123
  254. package/source/commands/paste-command.ts +0 -62
  255. package/source/commands/prompt-command.ts +0 -150
  256. package/source/commands/reset-command.ts +0 -22
  257. package/source/commands/rules-command.ts +0 -76
  258. package/source/commands/save-command.ts +0 -20
  259. package/source/commands/types.ts +0 -28
  260. package/source/commands/usage-command.ts +0 -26
  261. package/source/config.ts +0 -223
  262. package/source/conversation-analyzer.ts +0 -115
  263. package/source/dedent.ts +0 -53
  264. package/source/formatting.ts +0 -132
  265. package/source/index.ts +0 -240
  266. package/source/logger.ts +0 -29
  267. package/source/mentions.ts +0 -227
  268. package/source/messages.ts +0 -360
  269. package/source/middleware/audit-message.ts +0 -133
  270. package/source/middleware/index.ts +0 -2
  271. package/source/middleware/rate-limit.ts +0 -24
  272. package/source/models/ai-config.ts +0 -109
  273. package/source/models/anthropic-provider.ts +0 -199
  274. package/source/models/deepseek-provider.ts +0 -53
  275. package/source/models/google-provider.ts +0 -68
  276. package/source/models/manager.ts +0 -84
  277. package/source/models/openai-provider.ts +0 -81
  278. package/source/models/openrouter-provider.ts +0 -288
  279. package/source/models/providers.ts +0 -197
  280. package/source/models/xai-provider.ts +0 -59
  281. package/source/parsing.ts +0 -20
  282. package/source/prompts/manager.ts +0 -90
  283. package/source/prompts.ts +0 -172
  284. package/source/repl-prompt.ts +0 -196
  285. package/source/repl.ts +0 -572
  286. package/source/terminal/formatting.ts +0 -121
  287. package/source/terminal/index.ts +0 -518
  288. package/source/terminal/markdown-utils.ts +0 -89
  289. package/source/terminal/markdown.ts +0 -155
  290. package/source/terminal/types.ts +0 -84
  291. package/source/terminal-output.test.ts +0 -266
  292. package/source/token-tracker.ts +0 -78
  293. package/source/token-utils.ts +0 -17
  294. package/source/tools/agent.ts +0 -107
  295. package/source/tools/bash.ts +0 -367
  296. package/source/tools/code-interpreter.ts +0 -172
  297. package/source/tools/command-validation.ts +0 -81
  298. package/source/tools/delete-file.ts +0 -71
  299. package/source/tools/directory-tree.ts +0 -54
  300. package/source/tools/edit-file.ts +0 -155
  301. package/source/tools/filesystem-utils.ts +0 -265
  302. package/source/tools/git-utils.ts +0 -70
  303. package/source/tools/grep.ts +0 -184
  304. package/source/tools/index.ts +0 -278
  305. package/source/tools/memory-read.ts +0 -174
  306. package/source/tools/memory-write.ts +0 -105
  307. package/source/tools/move-file.ts +0 -59
  308. package/source/tools/read-file.ts +0 -129
  309. package/source/tools/read-multiple-files.ts +0 -80
  310. package/source/tools/save-file.ts +0 -147
  311. package/source/tools/think.ts +0 -51
  312. package/source/tools/types.ts +0 -58
  313. package/source/tools/web-fetch.ts +0 -327
  314. package/source/tools/web-search.ts +0 -101
  315. package/source/utils/process.ts +0 -121
  316. package/source/version.ts +0 -21
  317. package/test/commands/copy-command.test.ts +0 -69
  318. package/test/config.test.ts +0 -200
  319. package/test/terminal/markdown-utils.test.ts +0 -124
  320. package/test/tools/bash-tool.test.ts +0 -58
  321. package/test/tools/code-interpreter.test.ts +0 -91
  322. package/test/tools/command-validation.test.ts +0 -48
  323. package/tsconfig.build.json +0 -9
  324. package/tsconfig.json +0 -30
package/dist/repl.js CHANGED
@@ -8,7 +8,7 @@ import { AiConfig } from "./models/ai-config.js";
8
8
  import { systemPrompt } from "./prompts.js";
9
9
  import { ReplPrompt } from "./repl-prompt.js";
10
10
  import { isMarkdown } from "./terminal/markdown-utils.js";
11
- import { getCurrentBranch, getDiffStat, hasUncommittedChanges, inGitDirectory, } from "./tools/git-utils.js"; // Modified import
11
+ import { getCurrentBranch, getDiffStat, getGitStatus, hasUncommittedChanges, inGitDirectory, } from "./tools/git-utils.js"; // Modified import
12
12
  import { initAgents, initTools } from "./tools/index.js";
13
13
  export class Repl {
14
14
  options;
@@ -39,16 +39,8 @@ export class Repl {
39
39
  prevCb = cb;
40
40
  const langModel = modelManager.getModel("repl");
41
41
  const modelConfig = modelManager.getModelMetadata("repl");
42
- const currentDir = process.cwd().split("/").pop() || process.cwd();
43
- const branch = await getCurrentBranch();
44
- let branchDisplay = "";
45
- if (branch) {
46
- const hasChanges = await hasUncommittedChanges();
47
- const asterisk = hasChanges ? "*" : "";
48
- branchDisplay = ` ${chalk.gray(branch + asterisk)}`;
49
- }
50
42
  terminal.hr();
51
- terminal.writeln(`${chalk.blue(currentDir)}${branchDisplay}`);
43
+ terminal.writeln(await getProjectStatusLine());
52
44
  terminal.writeln(chalk.dim(langModel.modelId));
53
45
  terminal.displayProgressBar(currentContextWindow, modelConfig.contextWindow);
54
46
  if (!promptManager.isPending()) {
@@ -84,7 +76,8 @@ export class Repl {
84
76
  // flag to see if the user prompt has added context
85
77
  const hasAddedContext = promptManager.hasContext();
86
78
  if (hasAddedContext) {
87
- terminal.info("Context will be added to prompt.");
79
+ const contextTokenCount = promptManager.getContextTokenCount();
80
+ terminal.info(`Context will be added to prompt. (${contextTokenCount} tokens)`);
88
81
  terminal.lineBreak();
89
82
  }
90
83
  const userPrompt = promptManager.get();
@@ -152,23 +145,19 @@ export class Repl {
152
145
  }
153
146
  // Create a more visual representation of steps/tool usage
154
147
  this.displayToolUse(result, terminal);
155
- if (await inGitDirectory()) {
156
- // Added check
157
- const stats = await getDiffStat();
158
- terminal.writeln(`${chalk.dim("Files changed:")} ${chalk.yellow(stats.filesChanged)} ` +
159
- `${chalk.green(`+${stats.insertions}`)} ` + // Insertions first (green)
160
- `${chalk.red(`-${stats.deletions}`)}`);
161
- }
162
148
  const total = result.totalUsage ??
163
149
  result.usage;
164
- const outgoingTokens = isNumber(total.inputTokens)
150
+ const inputTokens = isNumber(total.inputTokens)
165
151
  ? total.inputTokens
166
152
  : 0;
167
- const incomingTokens = isNumber(total.outputTokens)
153
+ const outputTokens = isNumber(total.outputTokens)
168
154
  ? total.outputTokens
169
155
  : 0;
170
- const tokenSummary = `Tokens: ↑ ${outgoingTokens} ↓ ${incomingTokens}`;
156
+ const tokenSummary = `Tokens: ↑ ${inputTokens} ↓ ${outputTokens}`;
171
157
  terminal.writeln(chalk.dim(tokenSummary));
158
+ const inputCost = modelConfig.costPerInputToken * inputTokens;
159
+ const outputCost = modelConfig.costPerOutputToken * outputTokens;
160
+ terminal.writeln(chalk.dim(`Cost: $${(inputCost + outputCost).toFixed(2)}`));
172
161
  // Track aggregate usage across all steps when available
173
162
  tokenTracker.trackUsage("repl", total);
174
163
  // Derive current context window from final step usage
@@ -241,8 +230,8 @@ export class Repl {
241
230
  terminal.write(chalk.dim("\n</think>\n\n"));
242
231
  }
243
232
  // if there is accumulatedText, display it
244
- if (accumulatedText) {
245
- terminal.writeln(`${chalk.blue.bold("●")} Response:`);
233
+ if (accumulatedText.trim()) {
234
+ terminal.writeln(`${chalk.blue.bold("● Response:")}`);
246
235
  terminal.display(accumulatedText, true);
247
236
  terminal.lineBreak();
248
237
  }
@@ -255,8 +244,8 @@ export class Repl {
255
244
  terminal.write(chalk.gray("\n</think>\n\n"));
256
245
  }
257
246
  // if there is accumulatedText, display it
258
- if (accumulatedText) {
259
- terminal.writeln(`${chalk.green.bold("●")} Response:`);
247
+ if (accumulatedText.trim()) {
248
+ terminal.writeln(`${chalk.green.bold("● Response:")}`);
260
249
  terminal.display(accumulatedText, true);
261
250
  terminal.lineBreak();
262
251
  }
@@ -325,6 +314,7 @@ export class Repl {
325
314
  }
326
315
  }
327
316
  if (toolsCalled.length > 0) {
317
+ terminal.lineBreak();
328
318
  terminal.writeln(chalk.dim("Tools:"));
329
319
  for (const toolCalled of toolsCalled) {
330
320
  const colorFn = toolColors.get(toolCalled) ?? chalk.white;
@@ -361,7 +351,9 @@ function displayToolMessages(messages, terminal) {
361
351
  case "tool-error":
362
352
  _handleToolErrorMessage(msg.data, terminal);
363
353
  break;
364
- // 'tool-init' is handled before the loop, so no case needed here.
354
+ case "tool-init":
355
+ // 'tool-init' is handled before the loop, so nothing to do here.
356
+ break;
365
357
  default:
366
358
  // Optional: Log an unexpected event type for debugging, or do nothing.
367
359
  logger.debug(`Unhandled tool message event: ${msg.event}`);
@@ -429,3 +421,34 @@ const toolCallRepair = (modelManager, terminal) => {
429
421
  };
430
422
  return fn;
431
423
  };
424
+ async function getProjectStatusLine() {
425
+ //
426
+ const currentDir = process.cwd().split("/").pop() || process.cwd();
427
+ const branch = await getCurrentBranch();
428
+ let gitStatus = "";
429
+ if (branch) {
430
+ const hasChanges = await hasUncommittedChanges();
431
+ const asterisk = hasChanges ? "*" : "";
432
+ gitStatus = ` ${chalk.gray(branch + asterisk)}`;
433
+ }
434
+ if (await inGitDirectory()) {
435
+ // Added check
436
+ const stats = await getDiffStat();
437
+ const fileChanges = await getGitStatus();
438
+ let fileStatus = "";
439
+ if (fileChanges.added)
440
+ fileStatus += ` +${fileChanges.added}`;
441
+ if (fileChanges.modified)
442
+ fileStatus += ` ~${fileChanges.modified}`;
443
+ if (fileChanges.deleted)
444
+ fileStatus += ` -${fileChanges.deleted}`;
445
+ if (fileChanges.untracked)
446
+ fileStatus += ` ?${fileChanges.untracked}`;
447
+ gitStatus +=
448
+ " " +
449
+ `${chalk.dim("[")}${chalk.yellow(fileStatus.trim())} ` +
450
+ `${chalk.green(`+${stats.insertions}`)} ` + // Insertions first (green)
451
+ `${chalk.red(`-${stats.deletions}`)}${chalk.dim("]")}`; // Deletions last (red)
452
+ }
453
+ return `${chalk.blue(currentDir)}${gitStatus}`;
454
+ }
@@ -1,8 +1,3 @@
1
- /**
2
- * Terminal Formatting Utilities
3
- *
4
- * Provides functions for formatting and displaying text in the terminal.
5
- */
6
1
  /**
7
2
  * Clear the terminal screen
8
3
  */
@@ -34,4 +29,20 @@ export declare function getTerminalSize(): {
34
29
  * Word wrap text to the specified width
35
30
  */
36
31
  export declare function wordWrap(text: string, width: number): string;
32
+ export declare const ESC = "\u001B[";
33
+ export declare const OSC = "\u001B]";
34
+ export declare const BEL = "\u0007";
35
+ export declare const SEP = ";";
37
36
  export declare const link: (text: string, url: string) => string;
37
+ export declare const image: (data: string | Buffer, options?: {
38
+ width?: number | string;
39
+ height?: number | string;
40
+ preserveAspectRatio?: boolean;
41
+ }) => string;
42
+ export declare function ansiRegex({ onlyFirst }?: {
43
+ onlyFirst?: boolean | undefined;
44
+ }): RegExp;
45
+ /**
46
+ * Strip ANSI escape sequences from a string
47
+ */
48
+ export declare function stripAnsi(s: string): string;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Provides functions for formatting and displaying text in the terminal.
5
5
  */
6
- import chalk from "chalk";
6
+ import { supportsHyperlinks } from "./supports-hyperlinks.js";
7
7
  /**
8
8
  * Clear the terminal screen
9
9
  */
@@ -99,8 +99,42 @@ function wrapLine(line, width) {
99
99
  }
100
100
  return wrappedLines.join("\n");
101
101
  }
102
- // const ESC = "\u001B[";
103
- const OSC = "\u001B]";
104
- const BEL = "\u0007";
105
- const SEP = ";";
106
- export const link = (text, url) => chalk.underline.blue([OSC, "8", SEP, SEP, url, BEL, text, OSC, "8", SEP, SEP, BEL].join(""));
102
+ export const ESC = "\u001B[";
103
+ export const OSC = "\u001B]";
104
+ export const BEL = "\u0007";
105
+ export const SEP = ";";
106
+ export const link = (text, url) => {
107
+ if (supportsHyperlinks.stdout) {
108
+ return [OSC, "8", SEP, SEP, url, BEL, text, OSC, "8", SEP, SEP, BEL].join("");
109
+ }
110
+ return `[${text}](${url})`;
111
+ };
112
+ export const image = (data, options = {}) => {
113
+ let returnValue = `${OSC}1337;File=inline=1`;
114
+ if (options.width) {
115
+ returnValue += `;width=${options.width}`;
116
+ }
117
+ if (options.height) {
118
+ returnValue += `;height=${options.height}`;
119
+ }
120
+ if (options.preserveAspectRatio === false) {
121
+ returnValue += ";preserveAspectRatio=0";
122
+ }
123
+ return `${returnValue}:${Buffer.from(data).toString("base64")}${BEL}`;
124
+ };
125
+ export function ansiRegex({ onlyFirst = false } = {}) {
126
+ // Valid string terminator sequences are BEL, ESC\, and 0x9c
127
+ const st = "(?:\\u0007|\\u001B\\u005C|\\u009C)";
128
+ const pattern = [
129
+ `[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?${st})`,
130
+ "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
131
+ ].join("|");
132
+ return new RegExp(pattern, onlyFirst ? undefined : "g");
133
+ }
134
+ /**
135
+ * Strip ANSI escape sequences from a string
136
+ */
137
+ export function stripAnsi(s) {
138
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: need to detect ansi control characters
139
+ return s.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
140
+ }
@@ -75,7 +75,7 @@ export declare class Terminal {
75
75
  /**
76
76
  * Display a table of data
77
77
  */
78
- table(data: [string, string | number][], options?: {
78
+ table(data: (string | number)[][], options?: {
79
79
  header?: string[];
80
80
  colWidths?: number[];
81
81
  }): void;
@@ -10,7 +10,7 @@ import ora from "ora";
10
10
  import wrapAnsi from "wrap-ansi";
11
11
  import { logger } from "../logger.js";
12
12
  import { getPackageVersion } from "../version.js";
13
- import { clearTerminal, getTerminalSize, setTerminalTitle, link as terminalLink, } from "./formatting.js";
13
+ import { clearTerminal, getTerminalSize, setTerminalTitle, stripAnsi, link as terminalLink, } from "./formatting.js";
14
14
  import { applyMarkdown } from "./markdown.js";
15
15
  /**
16
16
  * Initialize the terminal interface
@@ -214,9 +214,6 @@ export class Terminal {
214
214
  const cols = this.terminalWidth > 0 ? this.terminalWidth : 80;
215
215
  const width = Math.max(4, cols - 4);
216
216
  // Helper to strip ANSI sequences for accurate visible-width calculation
217
- const stripAnsi = (s) =>
218
- // biome-ignore lint/suspicious/noControlCharactersInRegex: need to detect ansi control characters
219
- s.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
220
217
  const paddedHeader = ` ${header} `;
221
218
  const headerVisibleLen = stripAnsi(paddedHeader).length;
222
219
  const headerStartPos = 1;
@@ -249,27 +246,68 @@ export class Terminal {
249
246
  * Create a clickable link in the terminal if supported
250
247
  */
251
248
  link(text, url) {
252
- return terminalLink(text, url);
249
+ return chalk.underline.blue(terminalLink(text, url));
253
250
  }
254
251
  /**
255
252
  * Display a table of data
256
253
  */
257
254
  table(data, options = {}) {
258
255
  const { header, colWidths } = options;
256
+ // Determine number of columns from data or header
257
+ let colCount = header?.length;
258
+ if (colCount === undefined) {
259
+ colCount = data.length > 0 && data[0] ? data[0].length : 1;
260
+ }
259
261
  // Calculate column widths based on terminal width
260
262
  const padding = 5; // Account for table borders and padding
261
- const availableWidth = Math.max(10, this.terminalWidth - padding);
262
- const colCount = header?.length ?? 1;
263
- const width = Math.max(10, Math.floor(availableWidth / Math.max(1, colCount)));
264
- const computedColWidths = colWidths
265
- ? colWidths.map((percent) => Math.max(10, Math.floor((percent / 100) * availableWidth)))
266
- : new Array(colCount).fill(width);
263
+ const availableWidth = Math.max(20, this.terminalWidth - padding);
264
+ let computedColWidths;
265
+ if (colWidths && colWidths.length === colCount) {
266
+ // Use provided percentages
267
+ computedColWidths = colWidths.map((percent) => Math.max(10, Math.floor((percent / 100) * availableWidth)));
268
+ }
269
+ else {
270
+ // Distribute width evenly with minimum width per column
271
+ const minColWidth = 15;
272
+ const maxColsThatFit = Math.floor(availableWidth / minColWidth);
273
+ const actualColCount = Math.min(colCount, maxColsThatFit);
274
+ if (actualColCount === 1) {
275
+ computedColWidths = [availableWidth];
276
+ }
277
+ else {
278
+ // Calculate base width and distribute remaining pixels
279
+ const baseWidth = Math.floor(availableWidth / actualColCount);
280
+ const remainder = availableWidth % actualColCount;
281
+ computedColWidths = Array(actualColCount).fill(baseWidth);
282
+ // Distribute remainder pixels to first few columns
283
+ for (let i = 0; i < remainder && i < actualColCount; i++) {
284
+ computedColWidths[i] = (computedColWidths[i] || 0) + 1;
285
+ }
286
+ }
287
+ // If we have fewer computed widths than columns, extend the array
288
+ while (computedColWidths.length < colCount) {
289
+ computedColWidths.push(minColWidth);
290
+ }
291
+ }
267
292
  const table = new Table({
268
293
  head: header,
269
294
  colWidths: computedColWidths,
270
- wordWrap: true, // Enable word wrapping for the description column
295
+ wordWrap: true,
296
+ wrapOnWordBoundary: true,
297
+ });
298
+ // Ensure all data rows have the same number of columns
299
+ const normalizedData = data.map((row) => {
300
+ if (row.length < colCount) {
301
+ // Pad with empty strings if row has fewer columns
302
+ return [...row, ...Array(colCount - row.length).fill("")];
303
+ }
304
+ if (row.length > colCount) {
305
+ // Truncate if row has more columns
306
+ return row.slice(0, colCount);
307
+ }
308
+ return row;
271
309
  });
272
- table.push(...data);
310
+ table.push(...normalizedData);
273
311
  this.writeln(table.toString());
274
312
  }
275
313
  /**
@@ -400,7 +438,9 @@ export class Terminal {
400
438
  const percentage = total === 0 ? 1 : current / total;
401
439
  const filledWidth = Math.max(0, Math.min(progressBarMaxWidth, Math.floor(percentage * progressBarMaxWidth)));
402
440
  const emptyWidth = Math.max(0, progressBarMaxWidth - filledWidth);
403
- const a = chalk.yellow("─"); //"█"
441
+ const a = filledWidth / progressBarMaxWidth > 0.5
442
+ ? chalk.red("─")
443
+ : chalk.yellow("─"); //"█"
404
444
  const b = chalk.gray("─"); // "░"
405
445
  const filledBar = a.repeat(filledWidth);
406
446
  const emptyBar = b.repeat(emptyWidth);
@@ -105,7 +105,6 @@ function format(token, listDepth = 0, orderedListNumber = null, parent = null) {
105
105
  return chalk.strikethrough(token.text);
106
106
  }
107
107
  default:
108
- console.log(token.type);
109
108
  return "";
110
109
  }
111
110
  }
@@ -0,0 +1,16 @@
1
+ interface ColorSupport {
2
+ level: number;
3
+ hasBasic: boolean;
4
+ has256: boolean;
5
+ has16m: boolean;
6
+ }
7
+ export declare function createSupportsColor(stream: {
8
+ isTty?: boolean;
9
+ }, options?: {
10
+ streamIsTty?: boolean;
11
+ }): ColorSupport | false;
12
+ export declare const supportsColor: {
13
+ stdout: false | ColorSupport;
14
+ stderr: false | ColorSupport;
15
+ };
16
+ export {};
@@ -0,0 +1,121 @@
1
+ // based on https://raw.githubusercontent.com/chalk/supports-color/refs/heads/main/index.js
2
+ import os from "node:os";
3
+ import process from "node:process";
4
+ import tty from "node:tty";
5
+ const { env } = process;
6
+ function envForceColor() {
7
+ if (!("FORCE_COLOR" in env)) {
8
+ return;
9
+ }
10
+ if (env["FORCE_COLOR"] === "true") {
11
+ return 1;
12
+ }
13
+ if (env["FORCE_COLOR"] === "false") {
14
+ return 0;
15
+ }
16
+ if (env["FORCE_COLOR"] && env["FORCE_COLOR"].length === 0) {
17
+ return 1;
18
+ }
19
+ const level = Math.min(Number.parseInt(env["FORCE_COLOR"] || "0", 10), 3);
20
+ if (![0, 1, 2, 3].includes(level)) {
21
+ return;
22
+ }
23
+ return level;
24
+ }
25
+ function translateLevel(level) {
26
+ if (level === 0) {
27
+ return false;
28
+ }
29
+ return {
30
+ level,
31
+ hasBasic: true,
32
+ has256: level >= 2,
33
+ has16m: level >= 3,
34
+ };
35
+ }
36
+ function _supportsColor(haveStream, options = {}) {
37
+ const noFlagForceColor = envForceColor();
38
+ const forceColor = noFlagForceColor;
39
+ if (forceColor === 0) {
40
+ return 0;
41
+ }
42
+ // Check for Azure DevOps pipelines.
43
+ // Has to be above the `!streamIsTTY` check.
44
+ if ("TF_BUILD" in env && "AGENT_NAME" in env) {
45
+ return 1;
46
+ }
47
+ if (haveStream && !options.streamIsTty && forceColor === undefined) {
48
+ return 0;
49
+ }
50
+ const min = forceColor || 0;
51
+ if (env["TERM"] === "dumb") {
52
+ return min;
53
+ }
54
+ if (process.platform === "win32") {
55
+ // Windows 10 build 10586 is the first Windows release that supports 256 colors.
56
+ // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
57
+ const osRelease = os.release().split(".");
58
+ if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10_586) {
59
+ return Number(osRelease[2]) >= 14_931 ? 3 : 2;
60
+ }
61
+ return 1;
62
+ }
63
+ if ("CI" in env) {
64
+ if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
65
+ return 3;
66
+ }
67
+ if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) ||
68
+ env["CI_NAME"] === "codeship") {
69
+ return 1;
70
+ }
71
+ return min;
72
+ }
73
+ if ("TEAMCITY_VERSION" in env) {
74
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env["TEAMCITY_VERSION"] || "")
75
+ ? 1
76
+ : 0;
77
+ }
78
+ if (env["COLORTERM"] === "truecolor") {
79
+ return 3;
80
+ }
81
+ if (env["TERM"] === "xterm-kitty") {
82
+ return 3;
83
+ }
84
+ if (env["TERM"] === "xterm-ghostty") {
85
+ return 3;
86
+ }
87
+ if ("TERM_PROGRAM" in env) {
88
+ const termProgramVersion = String(env["TERM_PROGRAM_VERSION"] || "0");
89
+ const version = Number.parseInt(termProgramVersion.split(".")[0] ?? "0", 10);
90
+ switch (env["TERM_PROGRAM"]) {
91
+ case "iTerm.app": {
92
+ return version >= 3 ? 3 : 2;
93
+ }
94
+ case "Apple_Terminal": {
95
+ return 2;
96
+ }
97
+ // No default
98
+ }
99
+ }
100
+ if (/-256(color)?$/i.test(env["TERM"] || "")) {
101
+ return 2;
102
+ }
103
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env["TERM"] || "")) {
104
+ return 1;
105
+ }
106
+ if ("COLORTERM" in env) {
107
+ return 1;
108
+ }
109
+ return min;
110
+ }
111
+ export function createSupportsColor(stream, options = {}) {
112
+ const level = _supportsColor(true, {
113
+ streamIsTty: stream?.isTty,
114
+ ...options,
115
+ });
116
+ return translateLevel(level);
117
+ }
118
+ export const supportsColor = {
119
+ stdout: createSupportsColor({ isTty: tty.isatty(1) }),
120
+ stderr: createSupportsColor({ isTty: tty.isatty(2) }),
121
+ };
@@ -0,0 +1,7 @@
1
+ export declare function createSupportsHyperlinks(stream: {
2
+ isTty?: boolean;
3
+ }): boolean;
4
+ export declare const supportsHyperlinks: {
5
+ stdout: boolean;
6
+ stderr: boolean;
7
+ };
@@ -0,0 +1,98 @@
1
+ // based on https://raw.githubusercontent.com/chalk/supports-hyperlinks/refs/heads/main/index.js
2
+ import process from "node:process";
3
+ import { createSupportsColor } from "./supports-color.js";
4
+ function parseVersion(versionString = "") {
5
+ if (/^\d{3,4}$/.test(versionString)) {
6
+ // Env var doesn't always use dots. example: 4601 => 46.1.0
7
+ const match = /(\d{1,2})(\d{2})/.exec(versionString) ?? [];
8
+ return {
9
+ major: 0,
10
+ minor: Number.parseInt(match[1] ?? "0", 10),
11
+ patch: Number.parseInt(match[2] ?? "0", 10),
12
+ };
13
+ }
14
+ const versions = (versionString ?? "")
15
+ .split(".")
16
+ .map((n) => Number.parseInt(n, 10));
17
+ return {
18
+ major: versions[0] ?? 0,
19
+ minor: versions[1] ?? 0,
20
+ patch: versions[2] ?? 0,
21
+ };
22
+ }
23
+ export function createSupportsHyperlinks(stream) {
24
+ const { CI, CURSOR_TRACE_ID, FORCE_HYPERLINK, NETLIFY, TEAMCITY_VERSION, TERM_PROGRAM, TERM_PROGRAM_VERSION, VTE_VERSION, TERM, } = process.env;
25
+ if (FORCE_HYPERLINK) {
26
+ return !(FORCE_HYPERLINK.length > 0 && Number.parseInt(FORCE_HYPERLINK, 10) === 0);
27
+ }
28
+ // Netlify does not run a TTY, it does not need `supportsColor` check
29
+ if (NETLIFY) {
30
+ return true;
31
+ }
32
+ // If they specify no colors, they probably don't want hyperlinks.
33
+ if (!createSupportsColor(stream)) {
34
+ return false;
35
+ }
36
+ if (stream && !stream.isTty) {
37
+ return false;
38
+ }
39
+ // Windows Terminal
40
+ if ("WT_SESSION" in process.env) {
41
+ return true;
42
+ }
43
+ if (process.platform === "win32") {
44
+ return false;
45
+ }
46
+ if (CI) {
47
+ return false;
48
+ }
49
+ if (TEAMCITY_VERSION) {
50
+ return false;
51
+ }
52
+ if (TERM_PROGRAM) {
53
+ const version = parseVersion(TERM_PROGRAM_VERSION);
54
+ switch (TERM_PROGRAM) {
55
+ case "iTerm.app": {
56
+ if (version.major === 3) {
57
+ return version.minor >= 1;
58
+ }
59
+ return version.major > 3;
60
+ }
61
+ case "WezTerm": {
62
+ return version.major >= 20_200_620;
63
+ }
64
+ case "vscode": {
65
+ // Cursor forked VS Code and supports hyperlinks in 0.x.x
66
+ if (CURSOR_TRACE_ID) {
67
+ return true;
68
+ }
69
+ // eslint-disable-next-line no-mixed-operators
70
+ return (version.major > 1 || (version.major === 1 && version.minor >= 72));
71
+ }
72
+ case "ghostty": {
73
+ return true;
74
+ }
75
+ // No default
76
+ }
77
+ }
78
+ if (VTE_VERSION) {
79
+ // 0.50.0 was supposed to support hyperlinks, but throws a segfault
80
+ if (VTE_VERSION === "0.50.0") {
81
+ return false;
82
+ }
83
+ const version = parseVersion(VTE_VERSION);
84
+ return version.major > 0 || version.minor >= 50;
85
+ }
86
+ switch (TERM) {
87
+ case "alacritty": {
88
+ // Support added in v0.11 (2022-10-13)
89
+ return true;
90
+ }
91
+ // No default
92
+ }
93
+ return false;
94
+ }
95
+ export const supportsHyperlinks = {
96
+ stdout: createSupportsHyperlinks({ isTty: process.stdout.isTTY }),
97
+ stderr: createSupportsHyperlinks({ isTty: process.stderr.isTTY }),
98
+ };