@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,270 @@
1
+ import { normalize, resolve } from "node:path";
2
+
3
+ /**
4
+ * Dangerous-command guard.
5
+ *
6
+ * Blocks destructive shell patterns before execution. Word-boundary escapes
7
+ * are written as character classes so the intent is identical without
8
+ * boundary syntax.
9
+ *
10
+ * Overrides:
11
+ * FASTCODE_ALLOW_DANGEROUS=1 bypass the deny list (reported in output)
12
+ * FASTCODE_SHELL_ALLOWLIST=a;b only commands matching these regexes run
13
+ * FASTCODE_SHELL_DENY_EXTRA=x;y extra regexes to deny
14
+ * FASTCODE_RESTRICT_TO_WORKSPACE=1 also reject ../ and .. escapes
15
+ */
16
+ export const DEFAULT_DENY_PATTERNS = [
17
+ "(^|[^a-z])rm +-[rf]{1,2}([^a-z]|$)",
18
+ "(^|[^a-z])del +/[fq]([^a-z]|$)",
19
+ "(^|[^a-z])rmdir +/s([^a-z]|$)",
20
+ "(^|[^a-z-])(format|mkfs|diskpart)([^a-z]|$)",
21
+ "(^|[^a-z])dd +if=",
22
+ "> */dev/sd",
23
+ "(^|[^a-z])(shutdown|reboot|poweroff)([^a-z]|$)",
24
+ ];
25
+ export const FORK_BOMB_MARKERS = [":(){", ":|:&"];
26
+ function enabled(name) {
27
+ const value = process.env[name];
28
+ if (value === undefined)
29
+ return false;
30
+ return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
31
+ }
32
+ function splitList(value) {
33
+ return (value ?? "").split(/[;,]/).map((entry) => entry.trim()).filter(Boolean);
34
+ }
35
+ function matches(lower, pattern) {
36
+ try {
37
+ return new RegExp(pattern).test(lower);
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /**
44
+ * Decide whether a shell command may run.
45
+ * Returns { allow: true } or { allow: false, output: explanation }.
46
+ */
47
+ export function evaluateShellCommand(command) {
48
+ const cmd = String(command ?? "").trim();
49
+ const lower = cmd.toLowerCase();
50
+ if (enabled("FASTCODE_ALLOW_DANGEROUS"))
51
+ return { allow: true, output: undefined, bypassed: true };
52
+ const deny = DEFAULT_DENY_PATTERNS.concat(splitList(process.env["FASTCODE_SHELL_DENY_EXTRA"]));
53
+ for (const pattern of deny) {
54
+ if (matches(lower, pattern)) {
55
+ return {
56
+ allow: false,
57
+ pattern,
58
+ output: "Error: blocked by the safety guard (dangerous pattern: " + pattern + "). " +
59
+ "Set FASTCODE_ALLOW_DANGEROUS=1 to override.",
60
+ };
61
+ }
62
+ }
63
+ for (const marker of FORK_BOMB_MARKERS) {
64
+ if (lower.includes(marker))
65
+ return { allow: false, output: "Error: blocked by the safety guard (fork bomb pattern)." };
66
+ }
67
+ const allowlist = splitList(process.env["FASTCODE_SHELL_ALLOWLIST"]);
68
+ if (allowlist.length > 0 && !allowlist.some((pattern) => matches(lower, pattern)))
69
+ return { allow: false, output: "Error: blocked by the safety guard (not in FASTCODE_SHELL_ALLOWLIST)." };
70
+ if (enabled("FASTCODE_RESTRICT_TO_WORKSPACE")) {
71
+ const backslash = String.fromCharCode(92);
72
+ if (cmd.includes("../") || cmd.includes(".." + backslash))
73
+ return { allow: false, output: "Error: blocked by the safety guard (path traversal)." };
74
+ }
75
+ return { allow: true, output: undefined };
76
+ }
77
+ export const READ_ONLY_TOOLS = new Set([
78
+ "pwd", "read_file", "read_many", "list_files", "grep", "stat_path",
79
+ ]);
80
+ const READ_ONLY_COMMANDS = new Set([
81
+ "pwd", "ls", "dir", "cat", "head", "tail", "wc", "stat", "file", "du", "df",
82
+ "grep", "rg", "egrep", "fgrep", "which", "whereis", "where", "whoami", "id",
83
+ "groups", "uname", "date", "uptime", "printenv", "echo", "printf",
84
+ "true", "false", "basename", "dirname", "realpath", "readlink", "tree",
85
+ "more", "less", "jq", "cut", "tr",
86
+ ]);
87
+ const READ_ONLY_PREFIXES = new Set([
88
+ "git status", "git diff", "git log", "git show", "git rev-parse",
89
+ "git ls-files", "git blame", "git grep", "git describe", "git merge-base",
90
+ "git cat-file", "git rev-list", "git shortlog", "git whatchanged",
91
+ "npm ls", "npm list", "npm view", "npm outdated", "npm explain", "npm why",
92
+ "pnpm list", "pnpm ls", "yarn list", "yarn info",
93
+ "cargo tree", "go list", "pip list", "pip show",
94
+ "docker ps", "docker images", "docker logs",
95
+ ]);
96
+ const NON_READ_ONLY_LEADERS = new Set([
97
+ "cd", "sudo", "doas", "env", "xargs", "time", "command", "eval", "exec",
98
+ "source", ".", "sh", "bash", "zsh", "fish", "powershell", "cmd",
99
+ ]);
100
+ function scanCommand(command, onChar) {
101
+ let quote = "";
102
+ let escaped = false;
103
+ for (let i = 0; i < command.length; i += 1) {
104
+ const char = command[i];
105
+ if (escaped) {
106
+ escaped = false;
107
+ onChar(char, i, quote);
108
+ continue;
109
+ }
110
+ if (char === "\\") {
111
+ escaped = true;
112
+ onChar(char, i, quote);
113
+ continue;
114
+ }
115
+ if (quote) {
116
+ if (char === quote)
117
+ quote = "";
118
+ onChar(char, i, quote);
119
+ continue;
120
+ }
121
+ if (char === '"' || char === "'") {
122
+ quote = char;
123
+ onChar(char, i, quote);
124
+ continue;
125
+ }
126
+ onChar(char, i, quote);
127
+ }
128
+ return quote === "";
129
+ }
130
+ function hasWriteRedirection(command) {
131
+ let found = false;
132
+ const balanced = scanCommand(command, (char, index, quote) => {
133
+ if (quote || char !== ">")
134
+ return;
135
+ const previous = command[index - 1];
136
+ if (previous === "=")
137
+ return;
138
+ found = true;
139
+ });
140
+ return !balanced || found;
141
+ }
142
+ function hasCommandSubstitution(command) {
143
+ let found = false;
144
+ const balanced = scanCommand(command, (char, index, quote) => {
145
+ if (quote)
146
+ return;
147
+ if (char === "`" || (char === "$" && command[index + 1] === "("))
148
+ found = true;
149
+ });
150
+ return !balanced || found;
151
+ }
152
+ export function splitCommandSegments(command) {
153
+ const parts = [];
154
+ let current = "";
155
+ let quote = "";
156
+ let escaped = false;
157
+ for (let i = 0; i < command.length; i += 1) {
158
+ const char = command[i];
159
+ if (escaped) {
160
+ current += char;
161
+ escaped = false;
162
+ continue;
163
+ }
164
+ if (char === "\\") {
165
+ current += char;
166
+ escaped = true;
167
+ continue;
168
+ }
169
+ if (quote) {
170
+ current += char;
171
+ if (char === quote)
172
+ quote = "";
173
+ continue;
174
+ }
175
+ if (char === '"' || char === "'") {
176
+ quote = char;
177
+ current += char;
178
+ continue;
179
+ }
180
+ const next = command[i + 1];
181
+ if ((char === "&" && next === "&") || (char === "|" && next === "|")) {
182
+ parts.push(current);
183
+ current = "";
184
+ i += 1;
185
+ continue;
186
+ }
187
+ if (char === ";" || char === "|" || char === "&" || char === "\n") {
188
+ parts.push(current);
189
+ current = "";
190
+ continue;
191
+ }
192
+ current += char;
193
+ }
194
+ parts.push(current);
195
+ return parts.map((part) => part.trim()).filter(Boolean);
196
+ }
197
+ function isReadOnlySegment(segment) {
198
+ const tokens = segment.split(/\s+/).filter(Boolean);
199
+ if (tokens.length === 0)
200
+ return true;
201
+ const first = tokens[0].toLowerCase();
202
+ const second = (tokens[1] ?? "").toLowerCase();
203
+ if (NON_READ_ONLY_LEADERS.has(first))
204
+ return false;
205
+ if (READ_ONLY_PREFIXES.has(first + " " + second))
206
+ return true;
207
+ if (READ_ONLY_COMMANDS.has(first))
208
+ return true;
209
+ return false;
210
+ }
211
+ export function isReadOnlyCommand(command) {
212
+ const cmd = String(command ?? "").trim();
213
+ if (!cmd)
214
+ return false;
215
+ if (hasWriteRedirection(cmd) || hasCommandSubstitution(cmd))
216
+ return false;
217
+ const segments = splitCommandSegments(cmd);
218
+ if (segments.length === 0)
219
+ return false;
220
+ return segments.every(isReadOnlySegment);
221
+ }
222
+ export function isReadOnlyToolCall(call) {
223
+ const tool = typeof call?.tool === "string" ? call.tool : "";
224
+ if (tool === "shell")
225
+ return isReadOnlyCommand(call.command);
226
+ return READ_ONLY_TOOLS.has(tool);
227
+ }
228
+ export function loopExemptTools() {
229
+ const raw = process.env["FASTCODE_LOOP_EXEMPT"] ?? "";
230
+ return new Set(raw.split(",").map((entry) => entry.trim()).filter(Boolean));
231
+ }
232
+ export function isLoopExempt(tool) {
233
+ return loopExemptTools().has(String(tool ?? ""));
234
+ }
235
+ function stableValue(value) {
236
+ if (value === undefined)
237
+ return "null";
238
+ if (value === null || typeof value !== "object")
239
+ return JSON.stringify(value);
240
+ if (Array.isArray(value))
241
+ return "[" + value.map((entry) => stableValue(entry)).join(",") + "]";
242
+ const keys = Object.keys(value).sort().filter((key) => value[key] !== undefined);
243
+ return "{" + keys.map((key) => JSON.stringify(key) + ":" + stableValue(value[key])).join(",") + "}";
244
+ }
245
+ export function stableStringify(value) {
246
+ return stableValue(value);
247
+ }
248
+ function normalizeFingerprintPath(value, cwd) {
249
+ let normalized = normalize(resolve(cwd, value));
250
+ if (normalized.length > 1)
251
+ normalized = normalized.replace(/[\\/]+$/, "");
252
+ return normalized;
253
+ }
254
+ function fingerprintValue(key, value, cwd) {
255
+ if (typeof value === "string" && (key === "path" || key === "from" || key === "to" || key === "cwd"))
256
+ return normalizeFingerprintPath(value, cwd);
257
+ if (key === "paths" && Array.isArray(value))
258
+ return value.map((entry) => typeof entry === "string" ? normalizeFingerprintPath(entry, cwd) : entry);
259
+ return value;
260
+ }
261
+ export function toolCallFingerprint(call, cwd = process.cwd()) {
262
+ const tool = typeof call?.tool === "string" ? call.tool : "unknown";
263
+ const args = {};
264
+ for (const key of Object.keys(call ?? {}).sort()) {
265
+ if (key === "tool" || key === "timeoutMs")
266
+ continue;
267
+ args[key] = fingerprintValue(key, call[key], cwd);
268
+ }
269
+ return tool + ":" + stableStringify(args);
270
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Ignore rules for directory walks and reads.
3
+ *
4
+ * Defaults cover the usual dependency/build folders. Users can add more with
5
+ * `.fastcodeignore` in the project root, using a gitignore-like syntax:
6
+ *
7
+ * node_modules/ # trailing slash = directories only
8
+ * *.log # basename at any depth
9
+ * /secrets.txt # anchored to the project root
10
+ * !important.log # re-include (last matching rule wins)
11
+ */
12
+ import { readFileSync, statSync } from "node:fs";
13
+ import { join, relative, sep } from "node:path";
14
+
15
+ const DEFAULT_NAMES = new Set([
16
+ "node_modules",
17
+ ".git",
18
+ ".hg",
19
+ ".svn",
20
+ "dist",
21
+ "build",
22
+ "out",
23
+ ".next",
24
+ ".nuxt",
25
+ ".output",
26
+ ".turbo",
27
+ ".parcel-cache",
28
+ ".cache",
29
+ "coverage",
30
+ ".nyc_output",
31
+ "__pycache__",
32
+ ".venv",
33
+ "venv",
34
+ "target",
35
+ ".gradle",
36
+ ".idea",
37
+ ]);
38
+
39
+ const BINARY_EXT = /\.(png|jpe?g|gif|webp|ico|bmp|tiff?|pdf|zip|gz|tgz|bz2|xz|7z|rar|exe|dll|so|dylib|bin|wasm|mp3|mp4|mov|avi|mkv|wav|flac|ttf|otf|woff2?|eot|class|jar|apk|ipa|sqlite|db|lock)$/i;
40
+
41
+ export interface IgnoreRule {
42
+ regex: RegExp;
43
+ negate: boolean;
44
+ dirOnly: boolean;
45
+ }
46
+
47
+ export interface IgnoreMatcher {
48
+ ignored(name: string, fullPath: string, isDir: boolean): boolean;
49
+ isBinary(name: string): boolean;
50
+ }
51
+
52
+ export function isBinaryName(name: string): boolean {
53
+ return BINARY_EXT.test(name);
54
+ }
55
+
56
+ export function createIgnore(root: string): IgnoreMatcher {
57
+ const rules = loadRules(root);
58
+ return {
59
+ ignored(name, fullPath, isDir) {
60
+ if (DEFAULT_NAMES.has(name))
61
+ return true;
62
+ if (rules.length === 0)
63
+ return false;
64
+ const rel = relative(root, fullPath).split(sep).join("/");
65
+ let ignored = false;
66
+ for (const rule of rules) {
67
+ if (rule.dirOnly && !isDir)
68
+ continue;
69
+ if (rule.regex.test(rel) || rule.regex.test(name))
70
+ ignored = !rule.negate;
71
+ }
72
+ return ignored;
73
+ },
74
+ isBinary(name) {
75
+ return BINARY_EXT.test(name);
76
+ },
77
+ };
78
+ }
79
+
80
+ function loadRules(root: string): IgnoreRule[] {
81
+ const file = join(root, ".fastcodeignore");
82
+ let text = "";
83
+ try {
84
+ const info = statSync(file);
85
+ if (!info.isFile())
86
+ return [];
87
+ text = readFileSync(file, "utf8");
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ const rules: IgnoreRule[] = [];
93
+ for (const raw of text.split(/\r?\n/)) {
94
+ const line = raw.trim();
95
+ if (!line || line.startsWith("#"))
96
+ continue;
97
+ const negate = line.startsWith("!");
98
+ const body = negate ? line.slice(1).trim() : line;
99
+ if (!body)
100
+ continue;
101
+ const dirOnly = body.endsWith("/");
102
+ const pattern = dirOnly ? body.slice(0, -1) : body;
103
+ const anchored = pattern.startsWith("/");
104
+ const glob = anchored ? pattern.slice(1) : pattern;
105
+ try {
106
+ rules.push({
107
+ regex: globToRegExp(glob, anchored),
108
+ negate,
109
+ dirOnly,
110
+ });
111
+ }
112
+ catch {
113
+ // A bad pattern must not disable the whole ignore file.
114
+ }
115
+ }
116
+ return rules;
117
+ }
118
+
119
+ function globToRegExp(glob: string, anchored: boolean): RegExp {
120
+ let source = "";
121
+ for (let i = 0; i < glob.length; i += 1) {
122
+ const char = glob[i];
123
+ if (char === "*") {
124
+ if (glob[i + 1] === "*") {
125
+ source += ".*";
126
+ i += 1;
127
+ if (glob[i + 1] === "/")
128
+ i += 1;
129
+ }
130
+ else {
131
+ source += "[^/]*";
132
+ }
133
+ }
134
+ else if (char === "?") {
135
+ source += "[^/]";
136
+ }
137
+ else {
138
+ source += escapeRegExp(char);
139
+ }
140
+ }
141
+ // Unanchored patterns match a basename at any depth.
142
+ return new RegExp("^" + (anchored ? "" : "(?:.*/)?") + source + "$");
143
+ }
144
+
145
+ function escapeRegExp(value: string): string {
146
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
147
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Single source of truth for how much tool output reaches the model.
3
+ *
4
+ * Output is bounded while it is produced, not by slicing a finished string
5
+ * afterwards, so a runaway command or a huge directory listing cannot grow
6
+ * without limit. Override the cap with FASTCODE_TOOL_OUTPUT_LIMIT (characters).
7
+ */
8
+ export const DEFAULT_TOOL_OUTPUT_LIMIT = 30000;
9
+ export function toolOutputLimit() {
10
+ const raw = Number(process.env["FASTCODE_TOOL_OUTPUT_LIMIT"]);
11
+ return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : DEFAULT_TOOL_OUTPUT_LIMIT;
12
+ }
13
+ /**
14
+ * Bounded text accumulator.
15
+ *
16
+ * mode "head" keeps the first N characters (listings, search hits);
17
+ * mode "tail" keeps the last N (command logs, where the error is last).
18
+ * trimEnd()/toString() mimic a plain string so callers stay simple.
19
+ */
20
+ export function createOutputBuffer(limit = toolOutputLimit(), mode = "head") {
21
+ const NL = String.fromCharCode(10);
22
+ let text = "";
23
+ let dropped = 0;
24
+ const buffer = {
25
+ get full() {
26
+ return text.length >= limit;
27
+ },
28
+ push(chunk) {
29
+ const value = String(chunk);
30
+ if (mode === "tail") {
31
+ text += value;
32
+ if (text.length > limit) {
33
+ const over = text.length - limit;
34
+ dropped += over;
35
+ text = text.slice(over);
36
+ }
37
+ return;
38
+ }
39
+ if (text.length >= limit) {
40
+ dropped += value.length;
41
+ return;
42
+ }
43
+ const room = limit - text.length;
44
+ if (value.length <= room) {
45
+ text += value;
46
+ }
47
+ else {
48
+ text += value.slice(0, room);
49
+ dropped += value.length - room;
50
+ }
51
+ },
52
+ line(value) {
53
+ if (text.length > 0)
54
+ buffer.push(NL);
55
+ buffer.push(value);
56
+ },
57
+ value() {
58
+ if (dropped <= 0)
59
+ return text;
60
+ const note = "[" + dropped + " more characters omitted]";
61
+ return mode === "tail" ? note + NL + text : text + NL + note;
62
+ },
63
+ trimEnd() {
64
+ return buffer.value().trimEnd();
65
+ },
66
+ toString() {
67
+ return buffer.value();
68
+ },
69
+ };
70
+ return buffer;
71
+ }
@@ -0,0 +1,32 @@
1
+ import { spawn, type ChildProcess } from "node:child_process";
2
+
3
+ export function killProcessTree(child: ChildProcess | undefined, signal: NodeJS.Signals = "SIGTERM"): void {
4
+ if (!child || typeof child.pid !== "number")
5
+ return;
6
+ if (process.platform === "win32") {
7
+ try {
8
+ const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" });
9
+ killer.unref();
10
+ }
11
+ catch {
12
+ try {
13
+ child.kill();
14
+ }
15
+ catch {
16
+ // The process may already be gone.
17
+ }
18
+ }
19
+ return;
20
+ }
21
+ try {
22
+ process.kill(-child.pid, signal);
23
+ }
24
+ catch {
25
+ try {
26
+ child.kill(signal);
27
+ }
28
+ catch {
29
+ // Same as above.
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,59 @@
1
+ import { getModelMeta } from "../lib/model-meta.js";
2
+
3
+ export type ToolProtocol = "legacy" | "native";
4
+ export type ToolCapability = "native" | "pseudo";
5
+
6
+ const NATIVE_PROVIDERS = new Set([
7
+ "openai",
8
+ "openrouter",
9
+ "deepseek",
10
+ "groq",
11
+ "mistral",
12
+ "xai",
13
+ "moonshot",
14
+ "zhipu",
15
+ "qwen",
16
+ "ollama",
17
+ ]);
18
+
19
+ export function toolCapabilityKey(providerId: string, model: string): string {
20
+ return `${providerId}::${model}`;
21
+ }
22
+
23
+ export function resolveToolProtocol(config: any, providerId: string, model: string, env = process.env): ToolProtocol {
24
+ const mode = String(env["FASTCODE_TOOL_PROTOCOL"] ?? "legacy").trim().toLowerCase();
25
+ if (mode === "legacy")
26
+ return "legacy";
27
+ if (mode === "native")
28
+ return "native";
29
+ if (mode !== "auto")
30
+ return "legacy";
31
+ const cached = config?.capabilities?.toolCalling?.[toolCapabilityKey(providerId, model)];
32
+ if (cached === "pseudo")
33
+ return "legacy";
34
+ if (cached === "native")
35
+ return "native";
36
+ if (!NATIVE_PROVIDERS.has(providerId))
37
+ return "legacy";
38
+ return getModelMeta(providerId, model).supportsTools ? "native" : "legacy";
39
+ }
40
+
41
+ export function withToolCapability(config: any, providerId: string, model: string, capability: ToolCapability): any {
42
+ const key = toolCapabilityKey(providerId, model);
43
+ return {
44
+ ...config,
45
+ capabilities: {
46
+ ...(config?.capabilities ?? {}),
47
+ toolCalling: {
48
+ ...(config?.capabilities?.toolCalling ?? {}),
49
+ [key]: capability,
50
+ },
51
+ },
52
+ };
53
+ }
54
+
55
+ export function isNativeUnsupportedError(message: string): boolean {
56
+ return /\b400\b/.test(message)
57
+ && /tool|function/i.test(message)
58
+ && /not supported|unsupported|unrecognized|unknown|invalid|does not support/i.test(message);
59
+ }