@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
package/src/cli.ts ADDED
@@ -0,0 +1,335 @@
1
+ /**
2
+ * CLI argument parsing.
3
+ *
4
+ * \`fastcode\` launches the TUI. The non-interactive helpers let you configure
5
+ * and script FastCode straight from a Termux shell, without starting Ink:
6
+ * provider/model selection, config paths, skills, doctor and update.
7
+ */
8
+ import meow from "meow";
9
+ import { readPackageMeta } from "./lib/pkg.js";
10
+ import { installSkill, listInstalledSkills, uninstallSkill, searchRegistry } from "./skills/manager.js";
11
+ import { loadConfig, saveConfig, resolveProviderKey } from "./config/store.js";
12
+ import { getPaths } from "./config/paths.js";
13
+ import { PROVIDERS, getProvider } from "./providers/index.js";
14
+ import { collectDiagnostics, formatDiagnostics } from "./lib/diagnostics.js";
15
+ import { existsSync } from "node:fs";
16
+ import { latestSessionFile, listSessions, projectionPath, readSession, resolveSessionFile } from "./session/log.js";
17
+ import { readProjection, sessionMetaFromFile } from "./session/projection.js";
18
+ import { listTrusted, trustProject, untrustProject } from "./config/trust.js";
19
+ const NL = String.fromCharCode(10);
20
+ const HELP = [
21
+ "",
22
+ " FastCode — multi-LLM TUI coding assistant",
23
+ "",
24
+ " Usage",
25
+ " $ fastcode Launch the TUI",
26
+ " $ fastcode \"explain this repo\" Launch the TUI with an initial prompt",
27
+ "",
28
+ " Model & provider (no TUI needed)",
29
+ " $ fastcode provider List providers",
30
+ " $ fastcode provider <id> Set the default provider",
31
+ " $ fastcode model List models for the current provider",
32
+ " $ fastcode model <id> Set the default model",
33
+ " $ fastcode config Print config file and data paths",
34
+ " $ fastcode config path Print just the config file path",
35
+ " $ fastcode sessions List saved sessions (JSONL)",
36
+ " $ fastcode sessions show <id> Print a saved session",
37
+ " $ fastcode resume [id|latest] Resume a saved session in the TUI",
38
+ "",
39
+ " Trust (first-run safety)",
40
+ " $ fastcode trust [dir] Trust a folder (enables agent tools)",
41
+ " $ fastcode untrust [dir] Revoke trust for a folder",
42
+ " $ fastcode trusted List trusted folders",
43
+ "",
44
+ " Skills & maintenance",
45
+ " $ fastcode skills list Print installed skills",
46
+ " $ fastcode skills install <id> Install a skill from the registry",
47
+ " $ fastcode skills uninstall <id> Remove an installed skill",
48
+ " $ fastcode skills search <q> Search the registry",
49
+ " $ fastcode doctor Environment diagnostics (Termux/mobile)",
50
+ " $ fastcode update Check for a newer release",
51
+ "",
52
+ " Options",
53
+ " --version, -v Show version",
54
+ " --help, -h Show this help",
55
+ "",
56
+ " Files (on Termux, edit them directly with nano):",
57
+ " config.json provider, model, theme, agent mode, enabled skills",
58
+ " auth.json API keys only, kept in a SEPARATE file (chmod 0600)",
59
+ "",
60
+ " API keys may also come from ENV (DEEPSEEK_API_KEY, ANTHROPIC_API_KEY,",
61
+ " OPENAI_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, GROQ_API_KEY,",
62
+ " MISTRAL_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, ZHIPUAI_API_KEY,",
63
+ " DASHSCOPE_API_KEY) or from /login inside the TUI.",
64
+ " Override the data directory with FASTCODE_HOME.",
65
+ "",
66
+ ].join(NL);
67
+ export async function parseCli(): Promise<any> {
68
+ const cli = meow(HELP, {
69
+ importMeta: import.meta,
70
+ flags: {
71
+ version: { type: "boolean", shortFlag: "v" },
72
+ },
73
+ autoHelp: true,
74
+ autoVersion: true,
75
+ });
76
+ // First positional dictates the sub-command. Anything else is a prompt.
77
+ const [first, ...rest] = cli.input;
78
+ if (first === "provider") {
79
+ const cfg = loadConfig();
80
+ const paths = getPaths();
81
+ if (rest.length === 0) {
82
+ const lines = ["Providers (current: " + cfg.providerId + ")"];
83
+ for (const p of PROVIDERS) {
84
+ const marker = p.id === cfg.providerId ? "●" : " ";
85
+ const auth = p.authEnv.length === 0 ? "(no key needed)" : "[" + p.authEnv.join(", ") + "]";
86
+ lines.push(" " + marker + " " + p.id.padEnd(12) + " " + p.label + " " + auth);
87
+ }
88
+ lines.push("");
89
+ lines.push("Switch: fastcode provider <id>");
90
+ lines.push("Or edit: " + paths.config);
91
+ console.log(lines.join(NL));
92
+ return { done: true };
93
+ }
94
+ const next = getProvider(rest[0]);
95
+ if (!next) {
96
+ console.error("Unknown provider: " + rest[0] + ". Run: fastcode provider");
97
+ process.exit(1);
98
+ }
99
+ saveConfig({ ...cfg, providerId: next.id, model: next.defaultModel });
100
+ console.log("Provider: " + next.id + " (" + next.label + ")");
101
+ console.log("Model: " + next.defaultModel);
102
+ console.log("Saved to " + paths.config);
103
+ console.log("API keys stay in " + paths.auth + " (untouched)");
104
+ return { done: true };
105
+ }
106
+ if (first === "model") {
107
+ const cfg = loadConfig();
108
+ const paths = getPaths();
109
+ if (rest.length === 0) {
110
+ const provider = getProvider(cfg.providerId);
111
+ console.log("Provider: " + cfg.providerId + " Model: " + cfg.model);
112
+ if (provider) {
113
+ let models = [];
114
+ try {
115
+ const { key } = resolveProviderKey(provider.id, provider.authEnv);
116
+ models = await provider.listModels(key);
117
+ }
118
+ catch {
119
+ models = [];
120
+ }
121
+ if (models.length > 0) {
122
+ console.log("");
123
+ console.log("Known models for " + provider.label + " (use /model in the TUI for the live list):");
124
+ for (const m of models.slice(0, 40)) {
125
+ const marker = m.id === cfg.model ? "●" : " ";
126
+ console.log(" " + marker + " " + m.id + (m.label ? " — " + m.label : ""));
127
+ }
128
+ }
129
+ }
130
+ console.log("");
131
+ console.log("Switch: fastcode model <id>");
132
+ console.log("Or edit: " + paths.config);
133
+ return { done: true };
134
+ }
135
+ const id = rest.join(" ");
136
+ saveConfig({ ...cfg, model: id });
137
+ console.log("Model: " + id + " (provider " + cfg.providerId + ")");
138
+ console.log("Saved to " + paths.config);
139
+ return { done: true };
140
+ }
141
+ if (first === "config") {
142
+ const paths = getPaths();
143
+ if (rest[0] === "path") {
144
+ console.log(paths.config);
145
+ return { done: true };
146
+ }
147
+ const cfg = loadConfig();
148
+ console.log("Config file: " + paths.config);
149
+ console.log("Keys file: " + paths.auth + " (separate file, chmod 0600)");
150
+ console.log("");
151
+ console.log(JSON.stringify(cfg, null, 2));
152
+ return { done: true };
153
+ }
154
+ if (first === "sessions") {
155
+ const all = listSessions();
156
+ if (all.length === 0) {
157
+ console.log("No sessions yet. They are appended to " + getPaths().sessions);
158
+ return { done: true };
159
+ }
160
+ if (rest[0] === "show") {
161
+ const idOrFile = rest[1] ?? all[0].id;
162
+ const entries = readSession(idOrFile);
163
+ for (const entry of entries) {
164
+ if (entry.type === "message")
165
+ console.log("[" + entry.role + "] " + entry.content);
166
+ else if (entry.type === "user" || entry.type === "assistant_raw")
167
+ console.log("[" + entry.type + "] " + String(entry.content ?? "").slice(0, 300));
168
+ else
169
+ console.log("[" + entry.type + "]");
170
+ }
171
+ const file = resolveSessionFile(idOrFile);
172
+ const projection = readProjection(file);
173
+ console.log("");
174
+ if (projection) {
175
+ console.log("[projection] " + projectionPath(file));
176
+ console.log(" messages: " + projection.messages.length + " · activeFiles: " + projection.activeFiles.length + " · lastFactSeq: " + projection.lastFactSeq);
177
+ }
178
+ else {
179
+ console.log("[projection] (none)");
180
+ }
181
+ return { done: true };
182
+ }
183
+ console.log("Sessions (newest first):");
184
+ for (const entry of all.slice(0, 20)) {
185
+ console.log(" " + entry.id + " " + (entry.bytes / 1024).toFixed(1) + "KB " + entry.messages + " msgs " + entry.modified);
186
+ }
187
+ console.log("");
188
+ console.log("Resume: fastcode resume [id|latest]");
189
+ console.log("Show: fastcode sessions show <id>");
190
+ return { done: true };
191
+ }
192
+ if (first === "resume") {
193
+ const target = rest[0];
194
+ const file = !target || target === "latest" ? latestSessionFile() : resolveSessionFile(target);
195
+ if (!file || !existsSync(file)) {
196
+ console.error("No saved session to resume. Start one with: fastcode");
197
+ process.exit(1);
198
+ }
199
+ const meta = sessionMetaFromFile(file);
200
+ if (meta.cwd) {
201
+ if (existsSync(meta.cwd)) {
202
+ process.chdir(meta.cwd);
203
+ console.log("Working directory: " + meta.cwd);
204
+ }
205
+ else {
206
+ console.log("Session directory no longer exists: " + meta.cwd + " (staying in " + process.cwd() + ")");
207
+ }
208
+ }
209
+ if (meta.providerId)
210
+ console.log("Session model: " + meta.providerId + "::" + meta.model);
211
+ console.log("Resuming " + file);
212
+ return { done: false, resume: file };
213
+ }
214
+ if (first === "trust" || first === "untrust" || first === "trusted") {
215
+ if (first === "trusted") {
216
+ const all = listTrusted();
217
+ if (all.length === 0) {
218
+ console.log("No trusted folders yet. Run: fastcode trust");
219
+ return { done: true };
220
+ }
221
+ for (const entry of all)
222
+ console.log(" " + entry.path + " (" + entry.addedAt + ")");
223
+ return { done: true };
224
+ }
225
+ const dir = rest[0] ?? process.cwd();
226
+ if (first === "trust") {
227
+ console.log("Trusted: " + trustProject(dir));
228
+ }
229
+ else {
230
+ console.log(untrustProject(dir) ? "Removed from the trusted list: " + dir : "Not in the trusted list: " + dir);
231
+ }
232
+ return { done: true };
233
+ }
234
+ if (first === "skills") {
235
+ const sub = rest[0] ?? "list";
236
+ const args = rest.slice(1);
237
+ if (sub === "list" || sub === "ls") {
238
+ const installed = listInstalledSkills();
239
+ if (installed.length === 0) {
240
+ console.log("No skills installed. Try: fastcode skills install <id>");
241
+ }
242
+ else {
243
+ for (const s of installed)
244
+ console.log(s.name + "\t" + s.source + "\t" + s.path);
245
+ }
246
+ return { done: true };
247
+ }
248
+ if (sub === "install" || sub === "add") {
249
+ if (args.length === 0) {
250
+ console.error("Usage: fastcode skills install <id|owner/repo|url>");
251
+ process.exit(1);
252
+ }
253
+ const cfg = loadConfig();
254
+ const force = args.includes("--force");
255
+ const spec = args.filter((a) => a !== "--force").join(" ");
256
+ const skill = await installSkill(spec, { registry: cfg.skillsRegistry, force });
257
+ console.log("Installed " + skill.name + " -> " + skill.path);
258
+ return { done: true };
259
+ }
260
+ if (sub === "uninstall" || sub === "remove" || sub === "rm") {
261
+ if (args.length === 0) {
262
+ console.error("Usage: fastcode skills uninstall <id>");
263
+ process.exit(1);
264
+ }
265
+ const ok = uninstallSkill(args[0]);
266
+ console.log(ok ? "Removed " + args[0] + "." : "No skill named " + args[0] + ".");
267
+ return { done: true };
268
+ }
269
+ if (sub === "search") {
270
+ if (args.length === 0) {
271
+ console.error("Usage: fastcode skills search <query>");
272
+ process.exit(1);
273
+ }
274
+ const cfg = loadConfig();
275
+ const hits = await searchRegistry(args.join(" "), cfg.skillsRegistry);
276
+ if (hits.length === 0)
277
+ console.log("No matches.");
278
+ for (const h of hits.slice(0, 20)) {
279
+ console.log(h.slug + "\t" + (h.description ?? ""));
280
+ }
281
+ return { done: true };
282
+ }
283
+ console.error("Unknown skills sub-command: " + sub);
284
+ process.exit(1);
285
+ }
286
+ if (first === "doctor") {
287
+ console.log(formatDiagnostics(collectDiagnostics()));
288
+ return { done: true };
289
+ }
290
+ if (first === "update") {
291
+ console.log(await updateReport());
292
+ return { done: true };
293
+ }
294
+ // No sub-command → treat all positionals as the initial prompt.
295
+ const prompt = cli.input.length > 0 ? cli.input.join(" ") : undefined;
296
+ return prompt ? { prompt, done: false } : { done: false };
297
+ }
298
+ function compareVersions(a, b) {
299
+ const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
300
+ const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
301
+ for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
302
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
303
+ if (d !== 0)
304
+ return d > 0 ? 1 : -1;
305
+ }
306
+ return 0;
307
+ }
308
+ async function updateReport() {
309
+ const meta = readPackageMeta();
310
+ const lines = ["FastCode " + meta.version, ""];
311
+ const registry = process.env["FASTCODE_UPDATE_REGISTRY"] ??
312
+ "https://registry.npmjs.org/" + encodeURIComponent(meta.name) + "/latest";
313
+ try {
314
+ const res = await fetch(registry, { headers: { accept: "application/json" } });
315
+ if (!res.ok)
316
+ throw new Error(res.status + " " + res.statusText);
317
+ const payload: any = await res.json();
318
+ const latest = payload.version;
319
+ const cmp = compareVersions(latest, meta.version);
320
+ lines.push("Published latest: " + latest);
321
+ lines.push(cmp > 0
322
+ ? "Update available: " + meta.version + " -> " + latest
323
+ : cmp < 0
324
+ ? "This build (" + meta.version + ") is newer than the published " + latest + "."
325
+ : "You are up to date.");
326
+ }
327
+ catch (err) {
328
+ lines.push("Could not reach the registry: " + err.message);
329
+ }
330
+ lines.push("");
331
+ lines.push("Update with one of:");
332
+ lines.push(" npm install -g " + meta.name + "@latest");
333
+ lines.push(" # or from a checkout: git pull && npm install -g .");
334
+ return lines.join(NL);
335
+ }
@@ -0,0 +1,16 @@
1
+ import { platformLabel } from "../lib/platform.js";
2
+ export const aboutCommand = {
3
+ name: "about",
4
+ description: "Show version, platform, and links.",
5
+ aliases: ["version"],
6
+ run() {
7
+ const lines = [
8
+ `FastCode — multi-LLM TUI coding assistant`,
9
+ `Platform: ${platformLabel()} • Node ${process.version}`,
10
+ ``,
11
+ `GitHub: https://github.com/dateName/fastcode-termux`,
12
+ `Skills: https://skillshub.wtf`,
13
+ ];
14
+ return { message: lines.join("\n") };
15
+ },
16
+ };
@@ -0,0 +1,18 @@
1
+ export const agentCommand = {
2
+ name: "agent",
3
+ description: "Toggle coding-agent tools. Usage: /agent [on|off|status]",
4
+ run({ args, state, actions }) {
5
+ const sub = (args[0] ?? "status").toLowerCase();
6
+ if (sub === "on") {
7
+ actions.setAgentMode(true);
8
+ return { message: "Agent mode enabled. FastCode can run shell and file tools." };
9
+ }
10
+ if (sub === "off") {
11
+ actions.setAgentMode(false);
12
+ return { message: "Agent mode disabled." };
13
+ }
14
+ return {
15
+ message: `Agent mode: ${state.agentMode ? "on" : "off"}\n\nUse /agent on to let FastCode run local shell and file tools.`,
16
+ };
17
+ },
18
+ };
@@ -0,0 +1,16 @@
1
+ import { pollShellSession } from "../agent/shell-session.js";
2
+
3
+ export const attachCommand = {
4
+ name: "attach",
5
+ description: "Show new output from a background shell session (/attach <id>)",
6
+ async run({ args }) {
7
+ const id = args[0];
8
+ if (!id)
9
+ return { message: "Usage: /attach <session-id> (see /jobs)" };
10
+ const result = await pollShellSession(id, 0);
11
+ if (!result)
12
+ return { message: `Unknown session: ${id}` };
13
+ const status = result.status === "running" ? "running" : `${result.status} (exit ${result.exitCode ?? "?"})`;
14
+ return { message: `[session ${id}] ${status}\n${result.output || "(no new output)"}` };
15
+ },
16
+ };
@@ -0,0 +1,9 @@
1
+ export const bgCommand = {
2
+ name: "bg",
3
+ description: "Explain backgrounding (long shell commands auto-background after ~2s)",
4
+ run() {
5
+ return {
6
+ message: "Long shell commands already return a session id after ~2s; use /jobs to list them, /attach <id> for new output, /kill <id> to stop.",
7
+ };
8
+ },
9
+ };
@@ -0,0 +1,10 @@
1
+ export const cancelCommand = {
2
+ name: "cancel",
3
+ description: "Cancel the active agent run (soft keyboard alternative to Esc)",
4
+ run({ state, actions }) {
5
+ if (!state.busy)
6
+ return { message: "Nothing is running." };
7
+ actions.cancelRun();
8
+ return { message: "Cancelling the active run…" };
9
+ },
10
+ };
@@ -0,0 +1,9 @@
1
+ export const clearCommand = {
2
+ name: "clear",
3
+ description: "Clear the current conversation history.",
4
+ aliases: ["cls"],
5
+ run({ actions }) {
6
+ actions.clearHistory();
7
+ return { message: "Conversation cleared.", silent: false };
8
+ },
9
+ };
@@ -0,0 +1,23 @@
1
+ import { compactMessages, DEFAULT_COMPACTION_CONFIG } from "../agent/compaction.js";
2
+ import { getModelMeta } from "../lib/model-meta.js";
3
+
4
+ export const compactCommand = {
5
+ name: "compact",
6
+ description: "Force local context compaction now",
7
+ run({ state, actions }) {
8
+ const meta = getModelMeta(state.providerId, state.model);
9
+ const result = compactMessages(state.history, { ...DEFAULT_COMPACTION_CONFIG, localThresholdRatio: 0 }, {
10
+ contextWindow: meta.contextWindow,
11
+ providerId: state.providerId,
12
+ model: state.model,
13
+ force: true,
14
+ });
15
+ if (!result.compacted)
16
+ return { message: "Nothing to compact (history is already small)." };
17
+ actions.replaceHistory(result.messages);
18
+ return {
19
+ message: `[context compacted] ${result.beforeTokens} → ${result.afterTokens} tokens (${result.droppedMessages} messages summarized).` +
20
+ (result.summaryFile ? `\nSummary: ${result.summaryFile}` : ""),
21
+ };
22
+ },
23
+ };
@@ -0,0 +1,24 @@
1
+ import { loadConfig } from "../config/store.js";
2
+ import { getPaths } from "../config/paths.js";
3
+ export const configCommand = {
4
+ name: "config",
5
+ description: "Print effective config & paths.",
6
+ run() {
7
+ const cfg = loadConfig();
8
+ const paths = getPaths();
9
+ const lines = [
10
+ "Config (~/.fastcode/config.json):",
11
+ JSON.stringify(cfg, null, 2),
12
+ "",
13
+ "Paths:",
14
+ ` root: ${paths.root}`,
15
+ ` config: ${paths.config}`,
16
+ ` auth: ${paths.auth}`,
17
+ ` skills: ${paths.skills}`,
18
+ ` plugins: ${paths.plugins}`,
19
+ ` cache: ${paths.cache}`,
20
+ ` sessions: ${paths.sessions}`,
21
+ ];
22
+ return { message: lines.join("\n") };
23
+ },
24
+ };
@@ -0,0 +1,25 @@
1
+ import { DEFAULT_COMPACTION_CONFIG, heapExceeded, localThresholdTokens } from "../agent/compaction.js";
2
+ import { estMessagesTokens, getModelMeta } from "../lib/model-meta.js";
3
+
4
+ export const contextCommand = {
5
+ name: "context",
6
+ description: "Show estimated context tokens, compaction threshold, and heap usage",
7
+ run(ctx) {
8
+ const meta = getModelMeta(ctx.state.providerId, ctx.state.model);
9
+ const tokens = estMessagesTokens(ctx.state.history, {
10
+ providerId: ctx.state.providerId,
11
+ model: ctx.state.model,
12
+ });
13
+ const threshold = localThresholdTokens(DEFAULT_COMPACTION_CONFIG, meta.contextWindow);
14
+ const percent = meta.contextWindow > 0 ? (tokens / meta.contextWindow) * 100 : 0;
15
+ const heapMb = process.memoryUsage().heapUsed / 1024 / 1024;
16
+ return {
17
+ message: [
18
+ `Context: ~${tokens.toLocaleString()} / ${meta.contextWindow.toLocaleString()} tokens (${percent.toFixed(1)}%)`,
19
+ `Compaction threshold: ${threshold.toLocaleString()} tokens (${Math.round(DEFAULT_COMPACTION_CONFIG.localThresholdRatio * 100)}%)`,
20
+ `Heap: ${heapMb.toFixed(1)} MB / ${DEFAULT_COMPACTION_CONFIG.heapLimitMb} MB${heapExceeded(DEFAULT_COMPACTION_CONFIG) ? " [forced compaction]" : ""}`,
21
+ `Messages: ${ctx.state.history.length} · chars/token: ~${meta.charsPerToken}`,
22
+ ].join("\n"),
23
+ };
24
+ },
25
+ };
@@ -0,0 +1,13 @@
1
+ export const costCommand = {
2
+ name: "cost",
3
+ description: "Show approximate cost & token usage for this session.",
4
+ run({ state }) {
5
+ const lines = [
6
+ `Provider: ${state.providerId}`,
7
+ `Model: ${state.model}`,
8
+ `Tokens: ${state.totalInputTokens} in / ${state.totalOutputTokens} out`,
9
+ `Cost: ~$${state.totalCostUsd.toFixed(4)}`,
10
+ ];
11
+ return { message: lines.join("\n") };
12
+ },
13
+ };
@@ -0,0 +1,7 @@
1
+ export const cwdCommand = {
2
+ name: "cwd",
3
+ description: "Show the current agent sandbox root.",
4
+ run() {
5
+ return { message: `Agent sandbox root:\n\n\`${process.cwd()}\`` };
6
+ },
7
+ };
@@ -0,0 +1,37 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { readFileSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ export const editCommand = {
7
+ name: "edit",
8
+ description: "Edit the prompt in $EDITOR (works around Ink IME issues on Termux)",
9
+ run({ state, actions }) {
10
+ const file = join(tmpdir(), `fastcode-edit-${Date.now()}.md`);
11
+ writeFileSync(file, state.draft ?? "", "utf8");
12
+ const editor = process.env["EDITOR"] || process.env["VISUAL"] || "nano";
13
+ const wasRaw = Boolean(process.stdin.isTTY && process.stdin.isRaw);
14
+ try {
15
+ if (wasRaw)
16
+ process.stdin.setRawMode(false);
17
+ const result = spawnSync(editor, [file], { stdio: "inherit" });
18
+ if (result.error)
19
+ return { message: `Could not launch ${editor}: ${result.error.message}` };
20
+ const text = readFileSync(file, "utf8").trim();
21
+ if (!text)
22
+ return { message: "Editor closed with no text." };
23
+ actions.setInput(text);
24
+ return { message: "Draft loaded into the prompt; press Enter to send." };
25
+ }
26
+ finally {
27
+ if (wasRaw)
28
+ process.stdin.setRawMode(true);
29
+ try {
30
+ unlinkSync(file);
31
+ }
32
+ catch {
33
+ // temp file may already be gone
34
+ }
35
+ }
36
+ },
37
+ };
@@ -0,0 +1,49 @@
1
+ import { getProvider } from "../providers/index.js";
2
+ import type { ReasoningEffort } from "../types.js";
3
+ const EFFORTS: ReasoningEffort[] = ["auto", "low", "medium", "high", "max"];
4
+ const HINTS: Record<ReasoningEffort, string> = {
5
+ auto: "provider default (send nothing)",
6
+ low: "fast, minimal thinking",
7
+ medium: "balanced",
8
+ high: "deeper reasoning, slower",
9
+ max: "maximum budget where supported",
10
+ };
11
+ export const effortCommand = {
12
+ name: "effort",
13
+ description: "Set thinking effort (auto/low/medium/high/max). Usage: /effort [level]",
14
+ run({ args, state, actions }) {
15
+ const provider = getProvider(state.providerId);
16
+ const supported = provider?.supportsReasoning?.(state.model) ?? false;
17
+ const current = (state.reasoningEffort ?? "auto");
18
+ const apply = (value: ReasoningEffort) => {
19
+ actions.setReasoningEffort(value);
20
+ return describe(value, provider, supported);
21
+ };
22
+ if (args.length > 0) {
23
+ const value = args[0].toLowerCase();
24
+ if (!EFFORTS.includes(value as ReasoningEffort)) {
25
+ return { message: `Unknown effort "${args[0]}". Choose: ${EFFORTS.join(", ")}.` };
26
+ }
27
+ return { message: apply(value as ReasoningEffort) };
28
+ }
29
+ return {
30
+ message: `Thinking effort: ${current}${supported ? "" : " (this provider has no thinking parameter)"}`,
31
+ picker: {
32
+ title: `Thinking effort · ${provider?.label ?? state.providerId}`,
33
+ current,
34
+ items: EFFORTS.map((value) => ({ value, label: value, hint: HINTS[value] })),
35
+ onSelect: (value) => actions.pushSystemMessage(apply(value as ReasoningEffort)),
36
+ },
37
+ };
38
+ },
39
+ };
40
+ function describe(value, provider, supported) {
41
+ if (value === "auto") {
42
+ return "Thinking effort: auto — the provider's default is used (no thinking parameter is sent).";
43
+ }
44
+ if (!supported) {
45
+ return `Thinking effort saved as ${value}, but ${provider?.label ?? "this provider"} does not expose a thinking parameter; ` +
46
+ "requests will omit it. Switch to a reasoning-capable model/provider to use it.";
47
+ }
48
+ return `Thinking effort: ${value}. The next request will carry the provider-specific thinking parameter.`;
49
+ }
@@ -0,0 +1,8 @@
1
+ export const exitCommand = {
2
+ name: "exit",
3
+ description: "Quit FastCode.",
4
+ aliases: ["quit", "q"],
5
+ run() {
6
+ return { message: "Bye!", quit: true };
7
+ },
8
+ };
@@ -0,0 +1,8 @@
1
+ export const expandCommand = {
2
+ name: "expand",
3
+ description: "Toggle full tool output in the transcript (stable UI defaults to collapsed)",
4
+ run({ state, actions }) {
5
+ actions.setExpanded(!state.expanded);
6
+ return { message: state.expanded ? "Tool output: collapsed." : "Tool output: expanded (new output only)." };
7
+ },
8
+ };
@@ -0,0 +1,18 @@
1
+ import { COMMANDS } from "./index.js";
2
+ export const helpCommand = {
3
+ name: "help",
4
+ description: "Show all available slash commands.",
5
+ aliases: ["h", "?"],
6
+ run() {
7
+ const visible = COMMANDS.filter((c) => !c.hidden);
8
+ const widest = Math.max(...visible.map((c) => c.name.length));
9
+ const lines = [
10
+ "Slash commands — type any of these at the prompt:",
11
+ "",
12
+ ...visible.map((c) => ` /${c.name.padEnd(widest)} ${c.description}`),
13
+ "",
14
+ "Press ESC to cancel a streaming response. Press Ctrl+C twice to quit.",
15
+ ];
16
+ return { message: lines.join("\n") };
17
+ },
18
+ };