@datename/fastcode-termux 0.0.0-stage → 0.1.6-beta

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 (347) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +418 -2
  3. package/bin/fastcode.mjs +59 -0
  4. package/dist/agent/approval.d.ts +50 -0
  5. package/dist/agent/approval.js +7 -0
  6. package/dist/agent/compaction.d.ts +39 -0
  7. package/dist/agent/compaction.js +227 -0
  8. package/dist/agent/context.d.ts +29 -0
  9. package/dist/agent/context.js +62 -0
  10. package/dist/agent/guards.d.ts +43 -0
  11. package/dist/agent/guards.js +269 -0
  12. package/dist/agent/ignore.d.ts +11 -0
  13. package/dist/agent/ignore.js +129 -0
  14. package/dist/agent/limits.d.ts +24 -0
  15. package/dist/agent/limits.js +71 -0
  16. package/dist/agent/process-tree.d.ts +2 -0
  17. package/dist/agent/process-tree.js +31 -0
  18. package/dist/agent/protocol-mode.d.ts +6 -0
  19. package/dist/agent/protocol-mode.js +51 -0
  20. package/dist/agent/protocol.d.ts +4 -0
  21. package/dist/agent/protocol.js +129 -0
  22. package/dist/agent/registry.d.ts +17 -0
  23. package/dist/agent/registry.js +90 -0
  24. package/dist/agent/runner.d.ts +106 -0
  25. package/dist/agent/runner.js +352 -0
  26. package/dist/agent/shell-session.d.ts +37 -0
  27. package/dist/agent/shell-session.js +400 -0
  28. package/dist/agent/stream.d.ts +29 -0
  29. package/dist/agent/stream.js +103 -0
  30. package/dist/agent/tool-schemas.d.ts +2 -0
  31. package/dist/agent/tool-schemas.js +63 -0
  32. package/dist/agent/tools/append_file.d.ts +2 -0
  33. package/dist/agent/tools/append_file.js +26 -0
  34. package/dist/agent/tools/apply_patch.d.ts +10 -0
  35. package/dist/agent/tools/apply_patch.js +52 -0
  36. package/dist/agent/tools/copy_file.d.ts +2 -0
  37. package/dist/agent/tools/copy_file.js +30 -0
  38. package/dist/agent/tools/delete_file.d.ts +2 -0
  39. package/dist/agent/tools/delete_file.js +24 -0
  40. package/dist/agent/tools/grep.d.ts +2 -0
  41. package/dist/agent/tools/grep.js +23 -0
  42. package/dist/agent/tools/index.d.ts +2 -0
  43. package/dist/agent/tools/index.js +32 -0
  44. package/dist/agent/tools/list_files.d.ts +2 -0
  45. package/dist/agent/tools/list_files.js +17 -0
  46. package/dist/agent/tools/make_dir.d.ts +2 -0
  47. package/dist/agent/tools/make_dir.js +24 -0
  48. package/dist/agent/tools/move_path.d.ts +2 -0
  49. package/dist/agent/tools/move_path.js +47 -0
  50. package/dist/agent/tools/paths.d.ts +7 -0
  51. package/dist/agent/tools/paths.js +84 -0
  52. package/dist/agent/tools/pwd.d.ts +2 -0
  53. package/dist/agent/tools/pwd.js +10 -0
  54. package/dist/agent/tools/read_file.d.ts +2 -0
  55. package/dist/agent/tools/read_file.js +21 -0
  56. package/dist/agent/tools/read_many.d.ts +2 -0
  57. package/dist/agent/tools/read_many.js +25 -0
  58. package/dist/agent/tools/readers.d.ts +6 -0
  59. package/dist/agent/tools/readers.js +216 -0
  60. package/dist/agent/tools/replace_in_file.d.ts +2 -0
  61. package/dist/agent/tools/replace_in_file.js +30 -0
  62. package/dist/agent/tools/shell.d.ts +2 -0
  63. package/dist/agent/tools/shell.js +48 -0
  64. package/dist/agent/tools/stat_path.d.ts +2 -0
  65. package/dist/agent/tools/stat_path.js +31 -0
  66. package/dist/agent/tools/types.d.ts +39 -0
  67. package/dist/agent/tools/types.js +1 -0
  68. package/dist/agent/tools/validate.d.ts +6 -0
  69. package/dist/agent/tools/validate.js +29 -0
  70. package/dist/agent/tools/write_file.d.ts +2 -0
  71. package/dist/agent/tools/write_file.js +26 -0
  72. package/dist/agent/tools.d.ts +3 -0
  73. package/dist/agent/tools.js +2 -0
  74. package/dist/app.d.ts +4 -0
  75. package/dist/app.js +708 -0
  76. package/dist/cli.d.ts +1 -0
  77. package/dist/cli.js +569 -0
  78. package/dist/commands/about.d.ts +8 -0
  79. package/dist/commands/about.js +16 -0
  80. package/dist/commands/agent.d.ts +11 -0
  81. package/dist/commands/agent.js +18 -0
  82. package/dist/commands/attach.d.ts +9 -0
  83. package/dist/commands/attach.js +15 -0
  84. package/dist/commands/bg.d.ts +7 -0
  85. package/dist/commands/bg.js +9 -0
  86. package/dist/commands/cancel.d.ts +10 -0
  87. package/dist/commands/cancel.js +10 -0
  88. package/dist/commands/clear.d.ts +11 -0
  89. package/dist/commands/clear.js +9 -0
  90. package/dist/commands/compact.d.ts +10 -0
  91. package/dist/commands/compact.js +22 -0
  92. package/dist/commands/config.d.ts +7 -0
  93. package/dist/commands/config.js +24 -0
  94. package/dist/commands/context.d.ts +7 -0
  95. package/dist/commands/context.js +24 -0
  96. package/dist/commands/cost.d.ts +9 -0
  97. package/dist/commands/cost.js +13 -0
  98. package/dist/commands/cwd.d.ts +7 -0
  99. package/dist/commands/cwd.js +7 -0
  100. package/dist/commands/edit.d.ts +10 -0
  101. package/dist/commands/edit.js +36 -0
  102. package/dist/commands/effort.d.ts +25 -0
  103. package/dist/commands/effort.js +48 -0
  104. package/dist/commands/exit.d.ts +9 -0
  105. package/dist/commands/exit.js +8 -0
  106. package/dist/commands/expand.d.ts +10 -0
  107. package/dist/commands/expand.js +8 -0
  108. package/dist/commands/help.d.ts +6 -0
  109. package/dist/commands/help.js +18 -0
  110. package/dist/commands/index.d.ts +9 -0
  111. package/dist/commands/index.js +80 -0
  112. package/dist/commands/jobs.d.ts +7 -0
  113. package/dist/commands/jobs.js +16 -0
  114. package/dist/commands/kill.d.ts +7 -0
  115. package/dist/commands/kill.js +11 -0
  116. package/dist/commands/login.d.ts +18 -0
  117. package/dist/commands/login.js +63 -0
  118. package/dist/commands/logout.d.ts +9 -0
  119. package/dist/commands/logout.js +45 -0
  120. package/dist/commands/model.d.ts +24 -0
  121. package/dist/commands/model.js +50 -0
  122. package/dist/commands/plugin.d.ts +11 -0
  123. package/dist/commands/plugin.js +74 -0
  124. package/dist/commands/provider.d.ts +24 -0
  125. package/dist/commands/provider.js +39 -0
  126. package/dist/commands/reset.d.ts +9 -0
  127. package/dist/commands/reset.js +13 -0
  128. package/dist/commands/skills.d.ts +11 -0
  129. package/dist/commands/skills.js +106 -0
  130. package/dist/commands/steer.d.ts +11 -0
  131. package/dist/commands/steer.js +13 -0
  132. package/dist/commands/system.d.ts +11 -0
  133. package/dist/commands/system.js +20 -0
  134. package/dist/commands/theme.d.ts +11 -0
  135. package/dist/commands/theme.js +22 -0
  136. package/dist/commands/tools.d.ts +7 -0
  137. package/dist/commands/tools.js +29 -0
  138. package/dist/commands/whoami.d.ts +9 -0
  139. package/dist/commands/whoami.js +52 -0
  140. package/dist/config/paths.d.ts +4 -0
  141. package/dist/config/paths.js +59 -0
  142. package/dist/config/store.d.ts +89 -0
  143. package/dist/config/store.js +260 -0
  144. package/dist/config/themes.d.ts +73 -0
  145. package/dist/config/themes.js +77 -0
  146. package/dist/config/trust.d.ts +22 -0
  147. package/dist/config/trust.js +153 -0
  148. package/dist/index.d.ts +1 -0
  149. package/dist/index.js +31 -0
  150. package/dist/lib/diagnostics.d.ts +66 -0
  151. package/dist/lib/diagnostics.js +195 -0
  152. package/dist/lib/http.d.ts +23 -0
  153. package/dist/lib/http.js +128 -0
  154. package/dist/lib/markdown.d.ts +1 -0
  155. package/dist/lib/markdown.js +32 -0
  156. package/dist/lib/model-meta.d.ts +19 -0
  157. package/dist/lib/model-meta.js +113 -0
  158. package/dist/lib/native-tools.d.ts +12 -0
  159. package/dist/lib/native-tools.js +21 -0
  160. package/dist/lib/net.d.ts +33 -0
  161. package/dist/lib/net.js +168 -0
  162. package/dist/lib/pkg.d.ts +5 -0
  163. package/dist/lib/pkg.js +27 -0
  164. package/dist/lib/platform.d.ts +7 -0
  165. package/dist/lib/platform.js +57 -0
  166. package/dist/lib/startup.d.ts +6 -0
  167. package/dist/lib/startup.js +25 -0
  168. package/dist/lib/uiMode.d.ts +2 -0
  169. package/dist/lib/uiMode.js +7 -0
  170. package/dist/patch/apply.d.ts +15 -0
  171. package/dist/patch/apply.js +172 -0
  172. package/dist/patch/parse.d.ts +29 -0
  173. package/dist/patch/parse.js +112 -0
  174. package/dist/patch/plan.d.ts +49 -0
  175. package/dist/patch/plan.js +273 -0
  176. package/dist/plugins/manager.d.ts +23 -0
  177. package/dist/plugins/manager.js +101 -0
  178. package/dist/providers/anthropic.d.ts +22 -0
  179. package/dist/providers/anthropic.js +119 -0
  180. package/dist/providers/custom.d.ts +33 -0
  181. package/dist/providers/custom.js +25 -0
  182. package/dist/providers/deepseek.d.ts +34 -0
  183. package/dist/providers/deepseek.js +16 -0
  184. package/dist/providers/google.d.ts +33 -0
  185. package/dist/providers/google.js +19 -0
  186. package/dist/providers/groq.d.ts +34 -0
  187. package/dist/providers/groq.js +21 -0
  188. package/dist/providers/index.d.ts +10 -0
  189. package/dist/providers/index.js +67 -0
  190. package/dist/providers/mistral.d.ts +34 -0
  191. package/dist/providers/mistral.js +20 -0
  192. package/dist/providers/moonshot.d.ts +34 -0
  193. package/dist/providers/moonshot.js +23 -0
  194. package/dist/providers/ollama.d.ts +33 -0
  195. package/dist/providers/ollama.js +22 -0
  196. package/dist/providers/openai.d.ts +34 -0
  197. package/dist/providers/openai.js +26 -0
  198. package/dist/providers/openaiCompatible.d.ts +26 -0
  199. package/dist/providers/openaiCompatible.js +158 -0
  200. package/dist/providers/openrouter.d.ts +34 -0
  201. package/dist/providers/openrouter.js +27 -0
  202. package/dist/providers/qwen.d.ts +34 -0
  203. package/dist/providers/qwen.js +24 -0
  204. package/dist/providers/xai.d.ts +34 -0
  205. package/dist/providers/xai.js +19 -0
  206. package/dist/providers/zhipu.d.ts +34 -0
  207. package/dist/providers/zhipu.js +24 -0
  208. package/dist/session/log.d.ts +52 -0
  209. package/dist/session/log.js +331 -0
  210. package/dist/session/projection.d.ts +43 -0
  211. package/dist/session/projection.js +257 -0
  212. package/dist/skills/manager.d.ts +18 -0
  213. package/dist/skills/manager.js +201 -0
  214. package/dist/skills/parser.d.ts +18 -0
  215. package/dist/skills/parser.js +65 -0
  216. package/dist/types.d.ts +205 -0
  217. package/dist/types.js +1 -0
  218. package/dist/ui/Banner.d.ts +5 -0
  219. package/dist/ui/Banner.js +65 -0
  220. package/dist/ui/Message.d.ts +1 -0
  221. package/dist/ui/Message.js +24 -0
  222. package/dist/ui/Picker.d.ts +8 -0
  223. package/dist/ui/Picker.js +24 -0
  224. package/dist/ui/Prompt.d.ts +9 -0
  225. package/dist/ui/Prompt.js +38 -0
  226. package/dist/ui/Spinner.d.ts +5 -0
  227. package/dist/ui/Spinner.js +39 -0
  228. package/dist/ui/StatusBar.d.ts +11 -0
  229. package/dist/ui/StatusBar.js +20 -0
  230. package/dist/ui/TrustGate.d.ts +5 -0
  231. package/dist/ui/TrustGate.js +26 -0
  232. package/package.json +96 -4
  233. package/src/agent/approval.ts +47 -0
  234. package/src/agent/compaction.ts +276 -0
  235. package/src/agent/context.ts +97 -0
  236. package/src/agent/guards.ts +270 -0
  237. package/src/agent/ignore.ts +147 -0
  238. package/src/agent/limits.ts +71 -0
  239. package/src/agent/process-tree.ts +32 -0
  240. package/src/agent/protocol-mode.ts +59 -0
  241. package/src/agent/protocol.ts +129 -0
  242. package/src/agent/registry.ts +98 -0
  243. package/src/agent/runner.ts +443 -0
  244. package/src/agent/shell-session.ts +449 -0
  245. package/src/agent/stream.ts +143 -0
  246. package/src/agent/tool-schemas.ts +67 -0
  247. package/src/agent/tools/append_file.ts +28 -0
  248. package/src/agent/tools/apply_patch.ts +54 -0
  249. package/src/agent/tools/copy_file.ts +32 -0
  250. package/src/agent/tools/delete_file.ts +26 -0
  251. package/src/agent/tools/grep.ts +25 -0
  252. package/src/agent/tools/index.ts +34 -0
  253. package/src/agent/tools/list_files.ts +19 -0
  254. package/src/agent/tools/make_dir.ts +26 -0
  255. package/src/agent/tools/move_path.ts +50 -0
  256. package/src/agent/tools/paths.ts +92 -0
  257. package/src/agent/tools/pwd.ts +12 -0
  258. package/src/agent/tools/read_file.ts +23 -0
  259. package/src/agent/tools/read_many.ts +27 -0
  260. package/src/agent/tools/readers.ts +228 -0
  261. package/src/agent/tools/replace_in_file.ts +32 -0
  262. package/src/agent/tools/shell.ts +53 -0
  263. package/src/agent/tools/stat_path.ts +33 -0
  264. package/src/agent/tools/types.ts +42 -0
  265. package/src/agent/tools/validate.ts +35 -0
  266. package/src/agent/tools/write_file.ts +28 -0
  267. package/src/agent/tools.ts +3 -0
  268. package/src/app.ts +708 -0
  269. package/src/cli.ts +335 -0
  270. package/src/commands/about.ts +16 -0
  271. package/src/commands/agent.ts +18 -0
  272. package/src/commands/attach.ts +16 -0
  273. package/src/commands/bg.ts +9 -0
  274. package/src/commands/cancel.ts +10 -0
  275. package/src/commands/clear.ts +9 -0
  276. package/src/commands/compact.ts +23 -0
  277. package/src/commands/config.ts +24 -0
  278. package/src/commands/context.ts +25 -0
  279. package/src/commands/cost.ts +13 -0
  280. package/src/commands/cwd.ts +7 -0
  281. package/src/commands/edit.ts +37 -0
  282. package/src/commands/effort.ts +49 -0
  283. package/src/commands/exit.ts +8 -0
  284. package/src/commands/expand.ts +8 -0
  285. package/src/commands/help.ts +18 -0
  286. package/src/commands/index.ts +80 -0
  287. package/src/commands/jobs.ts +17 -0
  288. package/src/commands/kill.ts +12 -0
  289. package/src/commands/login.ts +63 -0
  290. package/src/commands/logout.ts +45 -0
  291. package/src/commands/model.ts +50 -0
  292. package/src/commands/plugin.ts +74 -0
  293. package/src/commands/provider.ts +39 -0
  294. package/src/commands/reset.ts +13 -0
  295. package/src/commands/skills.ts +106 -0
  296. package/src/commands/steer.ts +13 -0
  297. package/src/commands/system.ts +20 -0
  298. package/src/commands/theme.ts +22 -0
  299. package/src/commands/tools.ts +29 -0
  300. package/src/commands/whoami.ts +52 -0
  301. package/src/config/paths.ts +59 -0
  302. package/src/config/store.ts +293 -0
  303. package/src/config/themes.ts +77 -0
  304. package/src/config/trust.ts +183 -0
  305. package/src/index.ts +31 -0
  306. package/src/lib/diagnostics.ts +195 -0
  307. package/src/lib/http.ts +132 -0
  308. package/src/lib/markdown.ts +32 -0
  309. package/src/lib/model-meta.ts +138 -0
  310. package/src/lib/native-tools.ts +32 -0
  311. package/src/lib/net.ts +204 -0
  312. package/src/lib/pkg.ts +35 -0
  313. package/src/lib/platform.ts +57 -0
  314. package/src/lib/startup.ts +25 -0
  315. package/src/lib/uiMode.ts +7 -0
  316. package/src/patch/apply.ts +194 -0
  317. package/src/patch/parse.ts +131 -0
  318. package/src/patch/plan.ts +317 -0
  319. package/src/plugins/manager.ts +101 -0
  320. package/src/providers/anthropic.ts +119 -0
  321. package/src/providers/custom.ts +25 -0
  322. package/src/providers/deepseek.ts +16 -0
  323. package/src/providers/google.ts +19 -0
  324. package/src/providers/groq.ts +21 -0
  325. package/src/providers/index.ts +68 -0
  326. package/src/providers/mistral.ts +20 -0
  327. package/src/providers/moonshot.ts +23 -0
  328. package/src/providers/ollama.ts +22 -0
  329. package/src/providers/openai.ts +26 -0
  330. package/src/providers/openaiCompatible.ts +158 -0
  331. package/src/providers/openrouter.ts +27 -0
  332. package/src/providers/qwen.ts +24 -0
  333. package/src/providers/xai.ts +19 -0
  334. package/src/providers/zhipu.ts +24 -0
  335. package/src/session/log.ts +374 -0
  336. package/src/session/projection.ts +308 -0
  337. package/src/skills/manager.ts +201 -0
  338. package/src/skills/parser.ts +65 -0
  339. package/src/types.ts +205 -0
  340. package/src/ui/Banner.ts +68 -0
  341. package/src/ui/Message.ts +24 -0
  342. package/src/ui/Picker.ts +24 -0
  343. package/src/ui/Prompt.ts +38 -0
  344. package/src/ui/Spinner.ts +39 -0
  345. package/src/ui/StatusBar.ts +20 -0
  346. package/src/ui/TrustGate.ts +26 -0
  347. package/tsconfig.json +22 -0
@@ -0,0 +1,5 @@
1
+ export declare const Spinner: ({ theme, label, stable }: {
2
+ theme: any;
3
+ label: any;
4
+ stable?: boolean;
5
+ }) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,39 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Custom thinking spinner.
4
+ *
5
+ * We don't use ink-spinner's built-in spinners directly because we want to
6
+ * (1) cycle through a varied set of action verbs ("Thinking…", "Pondering…")
7
+ * and (2) keep the dots animation in sync with the verb rotation.
8
+ */
9
+ import { useEffect, useState } from "react";
10
+ import { Text } from "ink";
11
+ const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
12
+ const VERBS = [
13
+ "Thinking",
14
+ "Pondering",
15
+ "Reasoning",
16
+ "Cooking",
17
+ "Crafting",
18
+ "Brewing",
19
+ "Sketching",
20
+ "Charging",
21
+ ];
22
+ export const Spinner = ({ theme, label, stable = false }) => {
23
+ const [tick, setTick] = useState(0);
24
+ // stable UI only lowers the redraw rate; it must still animate. A frozen
25
+ // frame made long waits look like a hang.
26
+ const configured = Number(process.env["FASTCODE_SPINNER_MS"]);
27
+ const interval = Number.isFinite(configured) && configured >= 60
28
+ ? configured
29
+ : stable
30
+ ? 120
31
+ : 80;
32
+ useEffect(() => {
33
+ const id = setInterval(() => setTick((t) => t + 1), interval);
34
+ return () => clearInterval(id);
35
+ }, [interval]);
36
+ const frame = FRAMES[tick % FRAMES.length];
37
+ const verb = label ?? VERBS[Math.floor(tick / 24) % VERBS.length];
38
+ return (_jsxs(Text, { color: theme.accent, children: [frame, " ", _jsxs(Text, { color: theme.muted, children: [verb, "\u2026"] })] }));
39
+ };
@@ -0,0 +1,11 @@
1
+ export declare const StatusBar: ({ theme, providerId, model, inputTokens, outputTokens, costUsd, enabledSkillCount, reasoningEffort, hint, }: {
2
+ theme: any;
3
+ providerId: any;
4
+ model: any;
5
+ inputTokens: any;
6
+ outputTokens: any;
7
+ costUsd: any;
8
+ enabledSkillCount: any;
9
+ reasoningEffort: any;
10
+ hint: any;
11
+ }) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useStdout } from "ink";
3
+ import { platformLabel } from "../lib/platform.js";
4
+ export const StatusBar = ({ theme, providerId, model, inputTokens, outputTokens, costUsd, enabledSkillCount, reasoningEffort, hint, }) => {
5
+ const { stdout } = useStdout();
6
+ const columns = stdout?.columns ?? process.stdout.columns ?? 80;
7
+ const compact = columns < 88;
8
+ const cost = costUsd > 0 ? `$${costUsd.toFixed(4)}` : "$0.0000";
9
+ const providerModel = `${providerId}/${model}`;
10
+ const label = compact ? truncate(providerModel, Math.max(18, columns - 28)) : providerModel;
11
+ const effort = reasoningEffort && reasoningEffort !== "auto" ? `effort:${reasoningEffort}` : "";
12
+ return (_jsxs(Box, { flexDirection: compact ? "column" : "row", justifyContent: "space-between", borderStyle: "round", borderColor: theme.border, paddingX: 1, children: [_jsxs(Text, { color: theme.muted, children: [_jsx(Text, { color: theme.accent, children: "\u25CF" }), " ", label, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), inputTokens, "\u2191 ", outputTokens, "\u2193 ", cost, effort ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " \u00B7 " }), effort] })) : null, !compact ? (_jsxs(_Fragment, { children: [_jsx(Text, { color: theme.muted, children: " \u00B7 " }), "skills: ", enabledSkillCount, _jsx(Text, { color: theme.muted, children: " \u00B7 " }), platformLabel()] })) : null] }), _jsx(Text, { color: theme.muted, children: compact
13
+ ? `${effort ? effort + " \u00B7 " : ""}${platformLabel()} \u00B7 skills:${enabledSkillCount} \u00B7 ${hint ?? "/help \u00B7 esc \u00B7 ctrl+c"}`
14
+ : hint ?? "/help \u00B7 esc cancel \u00B7 ctrl+c quit" })] }));
15
+ };
16
+ function truncate(value, max) {
17
+ if (value.length <= max)
18
+ return value;
19
+ return `${value.slice(0, Math.max(1, max - 1))}\u2026`;
20
+ }
@@ -0,0 +1,5 @@
1
+ export declare const TrustGate: ({ theme, cwd, onDecision }: {
2
+ theme: any;
3
+ cwd: any;
4
+ onDecision: any;
5
+ }) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * First-run confirmation for a folder the agent is not trusted with yet.
4
+ *
5
+ * Agent tools can read, write, delete and execute in the workspace, so the user
6
+ * decides before anything loads.
7
+ */
8
+ import { Box, Text, useInput } from "ink";
9
+ export const TrustGate = ({ theme, cwd, onDecision }) => {
10
+ useInput((input) => {
11
+ const value = String(input ?? "").toLowerCase();
12
+ if (value === "y")
13
+ onDecision(true);
14
+ else if (value === "n" || value === "q")
15
+ onDecision(false);
16
+ });
17
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, marginTop: 1, children: [
18
+ _jsx(Text, { color: theme.accent, bold: true, children: "Trust this folder?" }),
19
+ _jsx(Text, { color: theme.muted, children: cwd }),
20
+ _jsx(Text, { children: "" }),
21
+ _jsx(Text, { children: "FastCode agent tools can read, write and delete files here, and run shell commands." }),
22
+ _jsx(Text, { children: "Continue only if you trust this project." }),
23
+ _jsx(Text, { children: "" }),
24
+ _jsx(Text, { children: "[y] trust and continue [n] decline (chat only) [q] quit" }),
25
+ ] }));
26
+ };
package/package.json CHANGED
@@ -1,6 +1,98 @@
1
1
  {
2
2
  "name": "@datename/fastcode-termux",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.6-beta",
4
+ "description": "FastCode — a blazing-fast TUI coding assistant. Multi-LLM, skills.sh skills, slash commands. Works on Termux, Linux, Windows, and macOS.",
5
+ "type": "module",
6
+ "bin": {
7
+ "fastcode": "bin/fastcode.mjs",
8
+ "fc": "bin/fastcode.mjs"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "files": [
12
+ "bin",
13
+ "dist",
14
+ "src",
15
+ "tsconfig.json",
16
+ "assets",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "engines": {
21
+ "node": ">=18.17"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.json && node scripts/build.mjs",
25
+ "build:bundle": "node scripts/build.mjs",
26
+ "dev": "tsx src/index.ts",
27
+ "start": "node bin/fastcode.mjs",
28
+ "check": "npm test && npm run typecheck && npm run lint && npm run build",
29
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit",
31
+ "test": "node ../scripts/smoke-test.mjs",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "keywords": [
35
+ "tui",
36
+ "cli",
37
+ "ai",
38
+ "coding-agent",
39
+ "claude",
40
+ "claude-code",
41
+ "openclaude",
42
+ "fastcode",
43
+ "skills",
44
+ "termux",
45
+ "openai",
46
+ "anthropic",
47
+ "gemini",
48
+ "openrouter",
49
+ "ollama",
50
+ "deepseek",
51
+ "groq",
52
+ "mistral",
53
+ "agent",
54
+ "ink",
55
+ "react"
56
+ ],
57
+ "author": "SimpleLittleDev",
58
+ "license": "MIT",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/dateName/fastcode-termux.git"
62
+ },
63
+ "homepage": "https://github.com/dateName/fastcode-termux#readme",
64
+ "bugs": {
65
+ "url": "https://github.com/dateName/fastcode-termux/issues"
66
+ },
67
+ "dependencies": {
68
+ "boxen": "^7.1.1",
69
+ "chalk": "^5.3.0",
70
+ "cli-highlight": "^2.1.11",
71
+ "figures": "^6.1.0",
72
+ "gradient-string": "^2.0.2",
73
+ "ink": "^5.0.1",
74
+ "ink-big-text": "^2.0.0",
75
+ "ink-gradient": "^3.0.0",
76
+ "ink-link": "^4.1.0",
77
+ "ink-markdown": "^1.0.1",
78
+ "ink-select-input": "^6.0.0",
79
+ "ink-spinner": "^5.0.0",
80
+ "ink-text-input": "^6.0.0",
81
+ "marked": "^12.0.2",
82
+ "marked-terminal": "^7.1.0",
83
+ "meow": "^13.2.0",
84
+ "react": "^18.3.1",
85
+ "tar": "^7.4.3"
86
+ },
87
+ "devDependencies": {
88
+ "@types/marked-terminal": "^6.1.1",
89
+ "@types/node": "^22.10.0",
90
+ "@types/react": "^18.3.12",
91
+ "@typescript-eslint/eslint-plugin": "^8.18.0",
92
+ "@typescript-eslint/parser": "^8.18.0",
93
+ "esbuild": "^0.28.2",
94
+ "eslint": "^9.16.0",
95
+ "tsx": "^4.19.2",
96
+ "typescript": "^5.6.3"
97
+ }
98
+ }
@@ -0,0 +1,47 @@
1
+ export type ApprovalScope = "once" | "session" | "always";
2
+
3
+ export type ApprovalReason =
4
+ | "trust"
5
+ | "sensitive_path"
6
+ | "ambiguous_match"
7
+ | "destructive"
8
+ | "large_diff";
9
+
10
+ export interface ChoiceItem {
11
+ label: string;
12
+ value: string;
13
+ }
14
+
15
+ export type ApprovalPreview =
16
+ | { kind: "diff"; text: string }
17
+ | { kind: "choices"; items: ChoiceItem[] };
18
+
19
+ export type ApprovalOption =
20
+ | { kind: "allow"; scope: ApprovalScope; label: string }
21
+ | { kind: "deny"; label: string }
22
+ | { kind: "choose"; label: string; value: string }
23
+ | { kind: "feedback"; label: string; prompt: string };
24
+
25
+ export interface ApprovalRequest {
26
+ id: string;
27
+ tool: string;
28
+ args: unknown;
29
+ reason: ApprovalReason;
30
+ options: ApprovalOption[];
31
+ preview?: ApprovalPreview;
32
+ }
33
+
34
+ export type ApprovalDecision =
35
+ | { kind: "allow"; scope: ApprovalScope }
36
+ | { kind: "deny"; reason?: string; feedback?: string }
37
+ | { kind: "pending"; request: ApprovalRequest };
38
+
39
+ export type ApprovalResolver = (request: ApprovalRequest) => ApprovalDecision | Promise<ApprovalDecision>;
40
+
41
+ export const DEFAULT_ALLOW_OPTIONS: ApprovalOption[] = [
42
+ { kind: "allow", scope: "once", label: "Allow once" },
43
+ { kind: "allow", scope: "session", label: "Allow for this session" },
44
+ { kind: "allow", scope: "always", label: "Always allow" },
45
+ { kind: "deny", label: "Deny" },
46
+ { kind: "feedback", label: "Deny with feedback", prompt: "Why should this be denied?" },
47
+ ];
@@ -0,0 +1,276 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { estMessagesTokens, estTokens } from "../lib/model-meta.js";
4
+ import { getPaths } from "../config/paths.js";
5
+ import type { ChatMessage } from "../types.js";
6
+ import { parseToolCalls, stripToolCalls } from "./protocol.js";
7
+
8
+ export interface CompactionConfig {
9
+ enabled: boolean;
10
+ localThresholdRatio: number;
11
+ keepTurns: number;
12
+ serverCompactionAware: boolean;
13
+ serverCompactThreshold?: number;
14
+ clampAboveServerByTokens: number;
15
+ maxSummaryTokens: number;
16
+ heapLimitMb: number;
17
+ }
18
+
19
+ export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
20
+ enabled: true,
21
+ localThresholdRatio: 0.7,
22
+ keepTurns: 6,
23
+ serverCompactionAware: false,
24
+ clampAboveServerByTokens: 8192,
25
+ maxSummaryTokens: 1500,
26
+ heapLimitMb: 220,
27
+ };
28
+
29
+ export interface CompactionOptions {
30
+ contextWindow: number;
31
+ providerId?: string;
32
+ model?: string;
33
+ force?: boolean;
34
+ serverOpaqueState?: string;
35
+ }
36
+
37
+ export interface CompactionResult {
38
+ messages: ChatMessage[];
39
+ compacted: boolean;
40
+ reason?: "threshold" | "heap";
41
+ source: "local" | "server" | "none";
42
+ beforeTokens: number;
43
+ afterTokens: number;
44
+ beforeChars: number;
45
+ afterChars: number;
46
+ droppedMessages: number;
47
+ keptMessages: number;
48
+ summary?: string;
49
+ summaryFile?: string;
50
+ }
51
+
52
+ export function compactionConfigFromEnv(): CompactionConfig {
53
+ const config = { ...DEFAULT_COMPACTION_CONFIG };
54
+ const disabled = (process.env["FASTCODE_COMPACTION"] ?? "").trim().toLowerCase();
55
+ if (["0", "false", "no", "off"].includes(disabled))
56
+ config.enabled = false;
57
+ const heap = Number(process.env["FASTCODE_HEAP_LIMIT_MB"]);
58
+ if (Number.isFinite(heap) && heap > 0)
59
+ config.heapLimitMb = heap;
60
+ const server = (process.env["FASTCODE_SERVER_COMPACTION"] ?? "").trim().toLowerCase();
61
+ if (["1", "true", "yes", "on"].includes(server))
62
+ config.serverCompactionAware = true;
63
+ return config;
64
+ }
65
+
66
+ export function localThresholdTokens(config: CompactionConfig, contextWindow: number): number {
67
+ const local = Math.floor(Math.max(1, contextWindow) * config.localThresholdRatio);
68
+ if (config.serverCompactionAware && typeof config.serverCompactThreshold === "number" && config.serverCompactThreshold > 0) {
69
+ return Math.max(local, config.serverCompactThreshold + config.clampAboveServerByTokens);
70
+ }
71
+ return local;
72
+ }
73
+
74
+ export function heapExceeded(config: CompactionConfig): boolean {
75
+ const used = process.memoryUsage().heapUsed;
76
+ return used > Math.max(0, config.heapLimitMb) * 1024 * 1024;
77
+ }
78
+
79
+ export function shouldCompact(messages: ChatMessage[], config: CompactionConfig, options: CompactionOptions): boolean {
80
+ if (!config.enabled)
81
+ return false;
82
+ if (options.force || heapExceeded(config))
83
+ return true;
84
+ return messageTokens(messages, options) > localThresholdTokens(config, options.contextWindow);
85
+ }
86
+
87
+ export function compactMessages(messages: ChatMessage[], config: CompactionConfig = DEFAULT_COMPACTION_CONFIG, options: CompactionOptions): CompactionResult {
88
+ const beforeTokens = messageTokens(messages, options);
89
+ const beforeChars = messageChars(messages);
90
+ const heap = heapExceeded(config);
91
+ const noop: CompactionResult = {
92
+ messages,
93
+ compacted: false,
94
+ source: "none",
95
+ beforeTokens,
96
+ afterTokens: beforeTokens,
97
+ beforeChars,
98
+ afterChars: beforeChars,
99
+ droppedMessages: 0,
100
+ keptMessages: messages.length,
101
+ };
102
+ if (!config.enabled)
103
+ return noop;
104
+ const threshold = localThresholdTokens(config, options.contextWindow);
105
+ if (!options.force && !heap && beforeTokens <= threshold)
106
+ return noop;
107
+ const reason: "threshold" | "heap" = heap && beforeTokens <= threshold ? "heap" : "threshold";
108
+ if (config.serverCompactionAware && options.serverOpaqueState) {
109
+ return {
110
+ ...noop,
111
+ compacted: true,
112
+ reason,
113
+ source: "server",
114
+ };
115
+ }
116
+ const headCount = messages[0]?.role === "user" ? 1 : 0;
117
+ const keep = Math.max(2, Math.floor(config.keepTurns * 2));
118
+ const tailStart = Math.max(headCount, messages.length - keep);
119
+ if (tailStart <= headCount)
120
+ return noop;
121
+ const middle = messages.slice(headCount, tailStart);
122
+ const summary = buildSummary(middle, config, options);
123
+ const summaryFile = writeSummaryFile(summary);
124
+ const marker: ChatMessage = {
125
+ role: "system",
126
+ content: `[context compacted: ${middle.length} earlier messages were summarized to stay within the context window.` +
127
+ `${summaryFile ? ` Summary file: ${summaryFile}.` : ""} Recent turns are kept verbatim.` +
128
+ " If an earlier detail is missing, re-read that file with a tool.]\n\n" +
129
+ summary,
130
+ };
131
+ const next = [...messages.slice(0, headCount), marker, ...messages.slice(tailStart)];
132
+ return {
133
+ messages: next,
134
+ compacted: true,
135
+ reason,
136
+ source: "local",
137
+ beforeTokens,
138
+ afterTokens: messageTokens(next, options),
139
+ beforeChars,
140
+ afterChars: messageChars(next),
141
+ droppedMessages: middle.length,
142
+ keptMessages: next.length - headCount - 1,
143
+ summary,
144
+ summaryFile,
145
+ };
146
+ }
147
+
148
+ function messageTokens(messages: ChatMessage[], options: CompactionOptions): number {
149
+ return estMessagesTokens(messages, { providerId: options.providerId, model: options.model });
150
+ }
151
+
152
+ function messageChars(messages: ChatMessage[]): number {
153
+ return messages.reduce((sum, message) => sum + (message.content?.length ?? 0), 0);
154
+ }
155
+
156
+ export function buildSummary(messages: ChatMessage[], config: CompactionConfig, options: Pick<CompactionOptions, "providerId" | "model">): string {
157
+ const requests: string[] = [];
158
+ const actions: string[] = [];
159
+ const outcomes: string[] = [];
160
+ const notes: string[] = [];
161
+ const files = new Set<string>();
162
+ for (const message of messages) {
163
+ if (!message.content)
164
+ continue;
165
+ if (message.role === "user") {
166
+ if (message.content.startsWith("FastCode executed the requested local tools.")) {
167
+ for (const line of message.content.split(/\r?\n/)) {
168
+ if (/error|failed|not found|denied|timed out/i.test(line))
169
+ outcomes.push(clip(line, 240));
170
+ }
171
+ }
172
+ else {
173
+ requests.push(clip(message.content, 300));
174
+ }
175
+ collectFiles(message.content, files);
176
+ continue;
177
+ }
178
+ if (message.role === "assistant") {
179
+ const calls = parseToolCalls(message.content);
180
+ for (const call of calls)
181
+ actions.push(describeCall(call));
182
+ const visible = stripToolCalls(message.content);
183
+ if (visible)
184
+ notes.push(clip(visible, 300));
185
+ collectFiles(message.content, files);
186
+ continue;
187
+ }
188
+ const lines = message.content.split(/\r?\n/);
189
+ for (const line of lines) {
190
+ if (/error|failed|not found|denied|timed out/i.test(line))
191
+ outcomes.push(clip(line, 240));
192
+ }
193
+ collectFiles(message.content, files);
194
+ }
195
+ const sections = [
196
+ "# Session summary",
197
+ "",
198
+ "## Requests",
199
+ ...limitLines(requests, 8).map((line) => `- ${line}`),
200
+ "",
201
+ "## Actions",
202
+ ...limitLines(actions, 20).map((line) => `- ${line}`),
203
+ "",
204
+ "## Outcomes",
205
+ ...limitLines(outcomes, 10).map((line) => `- ${line}`),
206
+ "",
207
+ "## Files touched",
208
+ ...(files.size > 0 ? Array.from(files).slice(0, 40).map((file) => `- ${file}`) : ["- (none recorded)"]),
209
+ "",
210
+ "## Notes",
211
+ ...limitLines(notes, 10).map((line) => `- ${line}`),
212
+ ];
213
+ const budget = Math.max(200, Math.floor(config.maxSummaryTokens));
214
+ const kept: string[] = [];
215
+ let used = 0;
216
+ for (const line of sections) {
217
+ const cost = estTokens(line + "\n", options);
218
+ if (used + cost > budget) {
219
+ kept.push("[... summary truncated ...]");
220
+ break;
221
+ }
222
+ kept.push(line);
223
+ used += cost;
224
+ }
225
+ return kept.join("\n").trimEnd() + "\n";
226
+ }
227
+
228
+ function describeCall(call: any): string {
229
+ const tool = String(call?.tool ?? "unknown");
230
+ if (tool === "shell" && typeof call.command === "string")
231
+ return `shell: ${clip(call.command, 200)}`;
232
+ if (typeof call.path === "string")
233
+ return `${tool}: ${call.path}`;
234
+ if (typeof call.from === "string" || typeof call.to === "string")
235
+ return `${tool}: ${call.from ?? "?"} -> ${call.to ?? "?"}`;
236
+ if (typeof call.pattern === "string")
237
+ return `${tool}: /${clip(call.pattern, 120)}/`;
238
+ return tool;
239
+ }
240
+
241
+ function collectFiles(text: string, files: Set<string>): void {
242
+ const matches = text.match(/[A-Za-z0-9_@./\\-]+\.[A-Za-z0-9]{1,8}\b/g);
243
+ if (!matches)
244
+ return;
245
+ for (const match of matches) {
246
+ if (match.length <= 3 || match.length > 240)
247
+ continue;
248
+ if (/^\d+(\.\d+)+$/.test(match))
249
+ continue;
250
+ files.add(match);
251
+ if (files.size >= 200)
252
+ return;
253
+ }
254
+ }
255
+
256
+ function limitLines(lines: string[], max: number): string[] {
257
+ return lines.slice(0, max);
258
+ }
259
+
260
+ function clip(text: string, max: number): string {
261
+ const oneLine = String(text).replace(/\s+/g, " ").trim();
262
+ return oneLine.length > max ? oneLine.slice(0, max) + "…" : oneLine;
263
+ }
264
+
265
+ function writeSummaryFile(summary: string): string | undefined {
266
+ try {
267
+ const dir = getPaths().sessions;
268
+ mkdirSync(dir, { recursive: true });
269
+ const file = join(dir, `summary-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.md`);
270
+ writeFileSync(file, summary, "utf8");
271
+ return file;
272
+ }
273
+ catch {
274
+ return undefined;
275
+ }
276
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Context compaction for long agent sessions.
3
+ *
4
+ * The chat loop keeps appending tool results to the conversation, so a long
5
+ * investigation can grow past the model's window and fail with a provider 400.
6
+ * Instead of silently truncating the tail (which loses the current task), we
7
+ * keep the first user request and the most recent turns, and shrink the middle
8
+ * into short summaries with an explicit marker.
9
+ *
10
+ * Token counts are approximated at 4 characters per token. That is deliberately
11
+ * crude: it must work for every provider without a tokenizer dependency.
12
+ */
13
+ import type { ChatMessage } from "../types.js";
14
+
15
+ export interface CompactOptions {
16
+ /** Character budget for the whole history (default 400k ≈ 100k tokens). */
17
+ maxChars?: number;
18
+ /** Number of trailing messages to keep verbatim. */
19
+ keepRecent?: number;
20
+ /** Maximum characters kept from one old tool result. */
21
+ toolSummaryChars?: number;
22
+ }
23
+
24
+ export interface CompactResult {
25
+ messages: ChatMessage[];
26
+ compacted: boolean;
27
+ beforeChars: number;
28
+ afterChars: number;
29
+ }
30
+
31
+ const TOOL_MARKER = "FastCode executed the requested local tools.";
32
+
33
+ export function historyChars(messages: ChatMessage[]): number {
34
+ return messages.reduce((sum, message) => sum + (message.content?.length ?? 0), 0);
35
+ }
36
+
37
+ export function compactHistory(messages: ChatMessage[], options: CompactOptions = {}): CompactResult {
38
+ const beforeChars = historyChars(messages);
39
+ const maxChars = positive(options.maxChars, 400_000);
40
+ const keepRecent = Math.max(2, Math.floor(positive(options.keepRecent, 6)));
41
+ const toolSummaryChars = Math.max(200, Math.floor(positive(options.toolSummaryChars, 2_000)));
42
+ if (beforeChars <= maxChars || messages.length <= keepRecent + 1) {
43
+ return { messages, compacted: false, beforeChars, afterChars: beforeChars };
44
+ }
45
+ // Keep the original request (usually the first message) plus the newest
46
+ // turns. Everything between is summarized, never dropped without a marker.
47
+ const head = messages[0]?.role === "user" ? [messages[0]] : [];
48
+ const tailStart = Math.max(head.length, messages.length - keepRecent);
49
+ const middle = messages.slice(head.length, tailStart);
50
+ const summarized = middle.map((message) => ({
51
+ role: message.role,
52
+ content: summarizeMessage(message.content, toolSummaryChars),
53
+ }));
54
+ const marker: ChatMessage = {
55
+ role: "system",
56
+ content: `[context compacted: ${middle.length} earlier message(s) were summarized to stay within the context window. ` +
57
+ `Recent tool results are kept in full. If an earlier detail is missing, re-read that file with a tool.]`,
58
+ };
59
+ const next = [...head, marker, ...summarized, ...messages.slice(tailStart)];
60
+ return {
61
+ messages: next,
62
+ compacted: true,
63
+ beforeChars,
64
+ afterChars: historyChars(next),
65
+ };
66
+ }
67
+
68
+ function summarizeMessage(content: string, limit: number): string {
69
+ if (!content)
70
+ return content;
71
+ if (content.includes(TOOL_MARKER))
72
+ return summarizeToolMessage(content, limit);
73
+ if (content.length <= limit)
74
+ return content;
75
+ const head = content.slice(0, Math.floor(limit * 0.6));
76
+ const tail = content.slice(-Math.floor(limit * 0.3));
77
+ return `${head}\n[… ${content.length - head.length - tail.length} chars summarized …]\n${tail}`;
78
+ }
79
+
80
+ /**
81
+ * Tool results have the shape:
82
+ * FastCode executed ...\n\nTool result 1 (ok)\ncall: {...}\noutput:\n```...```
83
+ * Keep the call and the start/end of each output so file paths and errors
84
+ * survive compaction.
85
+ */
86
+ function summarizeToolMessage(content: string, limit: number): string {
87
+ if (content.length <= limit)
88
+ return content;
89
+ const keep = Math.max(120, Math.floor((limit - 240) / 2));
90
+ const head = content.slice(0, keep);
91
+ const tail = content.slice(-keep);
92
+ return `${head}\n[… ${content.length - keep * 2} chars of tool output summarized; re-run the tool if you need the middle …]\n${tail}`;
93
+ }
94
+
95
+ function positive(value: number | undefined, fallback: number): number {
96
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
97
+ }