@datename/fastcode-termux 0.1.6-beta → 0.1.8-beta.0
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.
- package/LICENSE +4 -0
- package/README.md +50 -17
- package/dist/agent/approval-json.d.ts +43 -0
- package/dist/agent/approval-json.js +69 -0
- package/dist/agent/approval-native.d.ts +3 -0
- package/dist/agent/approval-native.js +165 -0
- package/dist/agent/approval-store.d.ts +16 -0
- package/dist/agent/approval-store.js +21 -0
- package/dist/agent/approval.d.ts +33 -0
- package/dist/agent/approval.js +40 -5
- package/dist/agent/compaction.d.ts +2 -0
- package/dist/agent/compaction.js +60 -17
- package/dist/agent/guards.d.ts +16 -16
- package/dist/agent/guards.js +978 -24
- package/dist/agent/policy/defaults.d.ts +16 -0
- package/dist/agent/policy/defaults.js +17 -0
- package/dist/agent/policy/egress.d.ts +32 -0
- package/dist/agent/policy/egress.js +280 -0
- package/dist/agent/process-tree.d.ts +20 -0
- package/dist/agent/process-tree.js +81 -10
- package/dist/agent/protocol.d.ts +20 -1
- package/dist/agent/protocol.js +165 -38
- package/dist/agent/registry.d.ts +3 -1
- package/dist/agent/registry.js +168 -11
- package/dist/agent/runner.d.ts +21 -0
- package/dist/agent/runner.js +44 -5
- package/dist/agent/shell-ast.d.ts +11 -0
- package/dist/agent/shell-ast.js +204 -0
- package/dist/agent/shell-session.d.ts +14 -0
- package/dist/agent/shell-session.js +251 -40
- package/dist/agent/tool-schemas.js +26 -7
- package/dist/agent/tools/append_file.js +3 -1
- package/dist/agent/tools/apply_patch.js +44 -4
- package/dist/agent/tools/copy_file.js +26 -3
- package/dist/agent/tools/delete_file.js +22 -3
- package/dist/agent/tools/grep.js +35 -4
- package/dist/agent/tools/make_dir.js +3 -1
- package/dist/agent/tools/move_path.d.ts +7 -0
- package/dist/agent/tools/move_path.js +31 -5
- package/dist/agent/tools/read_file.js +63 -3
- package/dist/agent/tools/read_many.js +21 -3
- package/dist/agent/tools/readers.d.ts +53 -1
- package/dist/agent/tools/readers.js +236 -26
- package/dist/agent/tools/replace_in_file.js +38 -5
- package/dist/agent/tools/shell.js +2 -2
- package/dist/agent/tools/types.d.ts +26 -0
- package/dist/agent/tools/validate.d.ts +15 -2
- package/dist/agent/tools/validate.js +66 -3
- package/dist/agent/tools/write_file.js +3 -1
- package/dist/app.d.ts +2 -1
- package/dist/app.js +526 -85
- package/dist/cli-main.js +363 -0
- package/dist/cli.js +397 -337
- package/dist/commands/about.js +1 -1
- package/dist/commands/clear.d.ts +1 -0
- package/dist/commands/clear.js +1 -1
- package/dist/commands/compact.d.ts +12 -0
- package/dist/commands/compact.js +14 -2
- package/dist/commands/context.js +5 -4
- package/dist/commands/index.js +2 -0
- package/dist/commands/jobs.js +3 -2
- package/dist/commands/kill.js +3 -1
- package/dist/commands/login.d.ts +2 -2
- package/dist/commands/login.js +15 -15
- package/dist/commands/model.d.ts +1 -13
- package/dist/commands/model.js +6 -38
- package/dist/commands/provider.d.ts +1 -15
- package/dist/commands/provider.js +4 -22
- package/dist/commands/skills.js +34 -4
- package/dist/commands/threads.d.ts +9 -0
- package/dist/commands/threads.js +26 -0
- package/dist/config/store.d.ts +21 -0
- package/dist/config/store.js +47 -9
- package/dist/config/trust.d.ts +9 -0
- package/dist/config/trust.js +41 -2
- package/dist/headless.d.ts +7 -0
- package/dist/headless.js +213 -0
- package/dist/index.js +19 -3
- package/dist/lib/debug.d.ts +2 -0
- package/dist/lib/debug.js +22 -0
- package/dist/lib/diagnostics.d.ts +19 -0
- package/dist/lib/diagnostics.js +32 -6
- package/dist/lib/highlight-subset.d.ts +21 -0
- package/dist/lib/highlight-subset.js +88 -0
- package/dist/lib/lifecycle.d.ts +5 -0
- package/dist/lib/lifecycle.js +62 -0
- package/dist/lib/markdown.js +22 -4
- package/dist/providers/deepseek.js +1 -1
- package/dist/providers/openaiCompatible.d.ts +24 -2
- package/dist/providers/openaiCompatible.js +20 -6
- package/dist/providers/spec.d.ts +12 -0
- package/dist/providers/spec.js +29 -0
- package/dist/session/log.d.ts +14 -2
- package/dist/session/log.js +56 -27
- package/dist/session/projection.d.ts +34 -1
- package/dist/session/projection.js +315 -46
- package/dist/skills/manager.d.ts +15 -0
- package/dist/skills/manager.js +135 -18
- package/dist/types.d.ts +15 -0
- package/dist/ui/StatusBar.js +1 -1
- package/package.json +3 -2
- package/src/agent/approval-json.ts +103 -0
- package/src/agent/approval-native.ts +168 -0
- package/src/agent/approval-store.ts +38 -0
- package/src/agent/approval.ts +58 -11
- package/src/agent/compaction.ts +62 -17
- package/src/agent/guards.ts +1049 -25
- package/src/agent/policy/defaults.ts +34 -0
- package/src/agent/policy/egress.ts +335 -0
- package/src/agent/process-tree.ts +105 -23
- package/src/agent/protocol.ts +187 -39
- package/src/agent/registry.ts +186 -14
- package/src/agent/runner.ts +50 -7
- package/src/agent/shell-ast.ts +227 -0
- package/src/agent/shell-session.ts +265 -41
- package/src/agent/tool-schemas.ts +26 -7
- package/src/agent/tools/append_file.ts +3 -1
- package/src/agent/tools/apply_patch.ts +45 -4
- package/src/agent/tools/copy_file.ts +27 -3
- package/src/agent/tools/delete_file.ts +22 -3
- package/src/agent/tools/grep.ts +36 -4
- package/src/agent/tools/make_dir.ts +3 -1
- package/src/agent/tools/move_path.ts +38 -5
- package/src/agent/tools/read_file.ts +64 -3
- package/src/agent/tools/read_many.ts +21 -3
- package/src/agent/tools/readers.ts +289 -26
- package/src/agent/tools/replace_in_file.ts +39 -5
- package/src/agent/tools/shell.ts +2 -2
- package/src/agent/tools/types.ts +43 -1
- package/src/agent/tools/validate.ts +73 -4
- package/src/agent/tools/write_file.ts +3 -1
- package/src/app.ts +533 -82
- package/src/{cli.ts → cli-main.ts} +34 -6
- package/src/commands/about.ts +1 -1
- package/src/commands/clear.ts +1 -1
- package/src/commands/compact.ts +14 -2
- package/src/commands/context.ts +5 -4
- package/src/commands/index.ts +2 -0
- package/src/commands/jobs.ts +3 -2
- package/src/commands/kill.ts +3 -1
- package/src/commands/login.ts +15 -15
- package/src/commands/model.ts +6 -38
- package/src/commands/provider.ts +4 -22
- package/src/commands/skills.ts +34 -4
- package/src/commands/threads.ts +28 -0
- package/src/config/store.ts +47 -9
- package/src/config/trust.ts +45 -2
- package/src/headless.ts +225 -0
- package/src/index.ts +19 -3
- package/src/lib/debug.ts +24 -0
- package/src/lib/diagnostics.ts +32 -6
- package/src/lib/highlight-subset.ts +93 -0
- package/src/lib/lifecycle.ts +69 -0
- package/src/lib/markdown.ts +22 -4
- package/src/providers/deepseek.ts +1 -1
- package/src/providers/openaiCompatible.ts +47 -10
- package/src/providers/spec.ts +42 -0
- package/src/session/log.ts +70 -27
- package/src/session/projection.ts +360 -47
- package/src/skills/manager.ts +140 -18
- package/src/types/highlight-shims.d.ts +14 -0
- package/src/types.ts +16 -1
- package/src/ui/StatusBar.ts +1 -1
- package/tsconfig.json +1 -0
- /package/dist/{cli.d.ts → cli-main.d.ts} +0 -0
package/dist/cli-main.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
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 { probeConnectivity } from "./lib/http.js";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
import { latestSessionFile, listSessions, projectionPath, readSession, resolveSessionFile } from "./session/log.js";
|
|
18
|
+
import { readProjection, sessionMetaFromFile } from "./session/projection.js";
|
|
19
|
+
import { forgetToolApproval, listToolApprovals, listTrusted, trustProject, untrustProject } from "./config/trust.js";
|
|
20
|
+
const NL = String.fromCharCode(10);
|
|
21
|
+
const HELP = [
|
|
22
|
+
"",
|
|
23
|
+
" FastCode — multi-LLM TUI coding assistant",
|
|
24
|
+
"",
|
|
25
|
+
" Usage",
|
|
26
|
+
" $ fastcode Launch the TUI",
|
|
27
|
+
" $ fastcode \"explain this repo\" Launch the TUI with an initial prompt",
|
|
28
|
+
"",
|
|
29
|
+
" Model & provider (no TUI needed)",
|
|
30
|
+
" $ fastcode provider List providers",
|
|
31
|
+
" $ fastcode provider <id> Set the default provider",
|
|
32
|
+
" $ fastcode model List models for the current provider",
|
|
33
|
+
" $ fastcode model <id> Set the default model",
|
|
34
|
+
" $ fastcode config Print config file and data paths",
|
|
35
|
+
" $ fastcode config path Print just the config file path",
|
|
36
|
+
" $ fastcode sessions List saved sessions (JSONL)",
|
|
37
|
+
" $ fastcode sessions show <id> Print a saved session",
|
|
38
|
+
" $ fastcode resume [id|latest] Resume a saved session in the TUI",
|
|
39
|
+
"",
|
|
40
|
+
" Trust (first-run safety)",
|
|
41
|
+
" $ fastcode trust [dir] Trust a folder (enables agent tools)",
|
|
42
|
+
" $ fastcode untrust [dir] Revoke trust for a folder",
|
|
43
|
+
" $ fastcode trusted List trusted folders and remembered approvals",
|
|
44
|
+
" $ fastcode trust --forget <key> Revoke one remembered approval",
|
|
45
|
+
"",
|
|
46
|
+
" Skills & maintenance",
|
|
47
|
+
" $ fastcode skills list Print installed skills",
|
|
48
|
+
" $ fastcode skills install <id> Install a skill from the registry",
|
|
49
|
+
" $ fastcode skills uninstall <id> Remove an installed skill",
|
|
50
|
+
" $ fastcode skills search <q> Search the registry",
|
|
51
|
+
" $ fastcode doctor Environment diagnostics (Termux/mobile)",
|
|
52
|
+
" $ fastcode doctor --net Also probe registry connectivity",
|
|
53
|
+
" $ fastcode update Check for a newer release",
|
|
54
|
+
"",
|
|
55
|
+
" Options",
|
|
56
|
+
" --version, -v Show version",
|
|
57
|
+
" --help, -h Show this help",
|
|
58
|
+
"",
|
|
59
|
+
" Files (on Termux, edit them directly with nano):",
|
|
60
|
+
" config.json provider, model, theme, agent mode, enabled skills",
|
|
61
|
+
" auth.json API keys only, kept in a SEPARATE file (chmod 0600)",
|
|
62
|
+
"",
|
|
63
|
+
" API keys may also come from ENV (DEEPSEEK_API_KEY, ANTHROPIC_API_KEY,",
|
|
64
|
+
" OPENAI_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, GROQ_API_KEY,",
|
|
65
|
+
" MISTRAL_API_KEY, XAI_API_KEY, MOONSHOT_API_KEY, ZHIPUAI_API_KEY,",
|
|
66
|
+
" DASHSCOPE_API_KEY) or from /login inside the TUI.",
|
|
67
|
+
" Override the data directory with FASTCODE_HOME.",
|
|
68
|
+
"",
|
|
69
|
+
" Approval UI: --ui tty|native|plain|json (default tty)",
|
|
70
|
+
"",
|
|
71
|
+
].join(NL);
|
|
72
|
+
export async function parseCli() {
|
|
73
|
+
const cli = meow(HELP, {
|
|
74
|
+
importMeta: import.meta,
|
|
75
|
+
flags: {
|
|
76
|
+
version: { type: "boolean", shortFlag: "v" },
|
|
77
|
+
ui: { type: "string", default: "tty" },
|
|
78
|
+
forget: { type: "string" },
|
|
79
|
+
net: { type: "boolean", default: false },
|
|
80
|
+
},
|
|
81
|
+
autoHelp: true,
|
|
82
|
+
autoVersion: true,
|
|
83
|
+
});
|
|
84
|
+
const uiMode = String(cli.flags.ui ?? "tty").toLowerCase();
|
|
85
|
+
if (!["tty", "native", "plain", "json"].includes(uiMode)) {
|
|
86
|
+
console.error("Unknown --ui mode: " + uiMode + " (use tty, native, plain, or json)");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
// First positional dictates the sub-command. Anything else is a prompt.
|
|
90
|
+
const [first, ...rest] = cli.input;
|
|
91
|
+
if (first === "provider") {
|
|
92
|
+
const cfg = loadConfig();
|
|
93
|
+
const paths = getPaths();
|
|
94
|
+
if (rest.length === 0) {
|
|
95
|
+
const lines = ["Providers (current: " + cfg.providerId + ")"];
|
|
96
|
+
for (const p of PROVIDERS) {
|
|
97
|
+
const marker = p.id === cfg.providerId ? "●" : " ";
|
|
98
|
+
const auth = p.authEnv.length === 0 ? "(no key needed)" : "[" + p.authEnv.join(", ") + "]";
|
|
99
|
+
lines.push(" " + marker + " " + p.id.padEnd(12) + " " + p.label + " " + auth);
|
|
100
|
+
}
|
|
101
|
+
lines.push("");
|
|
102
|
+
lines.push("Switch: fastcode provider <id>");
|
|
103
|
+
lines.push("Or edit: " + paths.config);
|
|
104
|
+
console.log(lines.join(NL));
|
|
105
|
+
return { done: true };
|
|
106
|
+
}
|
|
107
|
+
const next = getProvider(rest[0]);
|
|
108
|
+
if (!next) {
|
|
109
|
+
console.error("Unknown provider: " + rest[0] + ". Run: fastcode provider");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
saveConfig({ ...cfg, providerId: next.id, model: next.defaultModel });
|
|
113
|
+
console.log("Provider: " + next.id + " (" + next.label + ")");
|
|
114
|
+
console.log("Model: " + next.defaultModel);
|
|
115
|
+
console.log("Saved to " + paths.config);
|
|
116
|
+
console.log("API keys stay in " + paths.auth + " (untouched)");
|
|
117
|
+
return { done: true };
|
|
118
|
+
}
|
|
119
|
+
if (first === "model") {
|
|
120
|
+
const cfg = loadConfig();
|
|
121
|
+
const paths = getPaths();
|
|
122
|
+
if (rest.length === 0) {
|
|
123
|
+
const provider = getProvider(cfg.providerId);
|
|
124
|
+
console.log("Provider: " + cfg.providerId + " Model: " + cfg.model);
|
|
125
|
+
if (provider) {
|
|
126
|
+
let models = [];
|
|
127
|
+
try {
|
|
128
|
+
const { key } = resolveProviderKey(provider.id, provider.authEnv);
|
|
129
|
+
models = await provider.listModels(key);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
models = [];
|
|
133
|
+
}
|
|
134
|
+
if (models.length > 0) {
|
|
135
|
+
console.log("");
|
|
136
|
+
console.log("Known models for " + provider.label + " (use /model in the TUI for the live list):");
|
|
137
|
+
for (const m of models.slice(0, 40)) {
|
|
138
|
+
const marker = m.id === cfg.model ? "●" : " ";
|
|
139
|
+
console.log(" " + marker + " " + m.id + (m.label ? " — " + m.label : ""));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
console.log("");
|
|
144
|
+
console.log("Switch: fastcode model <id>");
|
|
145
|
+
console.log("Or edit: " + paths.config);
|
|
146
|
+
return { done: true };
|
|
147
|
+
}
|
|
148
|
+
const id = rest.join(" ");
|
|
149
|
+
saveConfig({ ...cfg, model: id });
|
|
150
|
+
console.log("Model: " + id + " (provider " + cfg.providerId + ")");
|
|
151
|
+
console.log("Saved to " + paths.config);
|
|
152
|
+
return { done: true };
|
|
153
|
+
}
|
|
154
|
+
if (first === "config") {
|
|
155
|
+
const paths = getPaths();
|
|
156
|
+
if (rest[0] === "path") {
|
|
157
|
+
console.log(paths.config);
|
|
158
|
+
return { done: true };
|
|
159
|
+
}
|
|
160
|
+
const cfg = loadConfig();
|
|
161
|
+
console.log("Config file: " + paths.config);
|
|
162
|
+
console.log("Keys file: " + paths.auth + " (separate file, chmod 0600)");
|
|
163
|
+
console.log("");
|
|
164
|
+
console.log(JSON.stringify(cfg, null, 2));
|
|
165
|
+
return { done: true };
|
|
166
|
+
}
|
|
167
|
+
if (first === "sessions") {
|
|
168
|
+
const all = listSessions();
|
|
169
|
+
if (all.length === 0) {
|
|
170
|
+
console.log("No sessions yet. They are appended to " + getPaths().sessions);
|
|
171
|
+
return { done: true };
|
|
172
|
+
}
|
|
173
|
+
if (rest[0] === "show") {
|
|
174
|
+
const idOrFile = rest[1] ?? all[0].id;
|
|
175
|
+
const entries = readSession(idOrFile);
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
if (entry.type === "message")
|
|
178
|
+
console.log("[" + entry.role + "] " + entry.content);
|
|
179
|
+
else if (entry.type === "user" || entry.type === "assistant_raw")
|
|
180
|
+
console.log("[" + entry.type + "] " + String(entry.content ?? "").slice(0, 300));
|
|
181
|
+
else
|
|
182
|
+
console.log("[" + entry.type + "]");
|
|
183
|
+
}
|
|
184
|
+
const file = resolveSessionFile(idOrFile);
|
|
185
|
+
const projection = readProjection(file);
|
|
186
|
+
console.log("");
|
|
187
|
+
if (projection) {
|
|
188
|
+
console.log("[projection] " + projectionPath(file));
|
|
189
|
+
console.log(" messages: " + projection.messages.length + " · activeFiles: " + projection.activeFiles.length + " · lastFactSeq: " + projection.lastFactSeq);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.log("[projection] (none)");
|
|
193
|
+
}
|
|
194
|
+
return { done: true };
|
|
195
|
+
}
|
|
196
|
+
console.log("Sessions (newest first):");
|
|
197
|
+
for (const entry of all.slice(0, 20)) {
|
|
198
|
+
console.log(" " + entry.id + " " + (entry.bytes / 1024).toFixed(1) + "KB " + entry.messages + " msgs " + entry.modified);
|
|
199
|
+
}
|
|
200
|
+
console.log("");
|
|
201
|
+
console.log("Resume: fastcode resume [id|latest]");
|
|
202
|
+
console.log("Show: fastcode sessions show <id>");
|
|
203
|
+
return { done: true };
|
|
204
|
+
}
|
|
205
|
+
if (first === "resume") {
|
|
206
|
+
const target = rest[0];
|
|
207
|
+
const file = !target || target === "latest" ? latestSessionFile() : resolveSessionFile(target);
|
|
208
|
+
if (!file || !existsSync(file)) {
|
|
209
|
+
console.error("No saved session to resume. Start one with: fastcode");
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const meta = sessionMetaFromFile(file);
|
|
213
|
+
if (meta.cwd) {
|
|
214
|
+
if (existsSync(meta.cwd)) {
|
|
215
|
+
process.chdir(meta.cwd);
|
|
216
|
+
console.log("Working directory: " + meta.cwd);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
console.log("Session directory no longer exists: " + meta.cwd + " (staying in " + process.cwd() + ")");
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (meta.providerId)
|
|
223
|
+
console.log("Session model: " + meta.providerId + "::" + meta.model);
|
|
224
|
+
console.log("Resuming " + file);
|
|
225
|
+
return { done: false, resume: file };
|
|
226
|
+
}
|
|
227
|
+
if (first === "trust" || first === "untrust" || first === "trusted") {
|
|
228
|
+
if (first === "trusted") {
|
|
229
|
+
const all = listTrusted();
|
|
230
|
+
if (all.length === 0)
|
|
231
|
+
console.log("No trusted folders yet. Run: fastcode trust");
|
|
232
|
+
for (const entry of all)
|
|
233
|
+
console.log(" " + entry.path + " (" + entry.addedAt + ")");
|
|
234
|
+
const approvals = listToolApprovals();
|
|
235
|
+
if (approvals.length > 0) {
|
|
236
|
+
console.log("");
|
|
237
|
+
console.log("Remembered tool approvals:");
|
|
238
|
+
for (const entry of approvals)
|
|
239
|
+
console.log(" " + entry.key + " catalog=" + entry.catalogHash);
|
|
240
|
+
}
|
|
241
|
+
return { done: true };
|
|
242
|
+
}
|
|
243
|
+
if (first === "trust" && typeof cli.flags.forget === "string") {
|
|
244
|
+
const key = String(cli.flags.forget);
|
|
245
|
+
console.log(forgetToolApproval(key) ? "Forgot approval: " + key : "No such approval: " + key);
|
|
246
|
+
return { done: true };
|
|
247
|
+
}
|
|
248
|
+
const dir = rest[0] ?? process.cwd();
|
|
249
|
+
if (first === "trust") {
|
|
250
|
+
console.log("Trusted: " + trustProject(dir));
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
console.log(untrustProject(dir) ? "Removed from the trusted list: " + dir : "Not in the trusted list: " + dir);
|
|
254
|
+
}
|
|
255
|
+
return { done: true };
|
|
256
|
+
}
|
|
257
|
+
if (first === "skills") {
|
|
258
|
+
const sub = rest[0] ?? "list";
|
|
259
|
+
const args = rest.slice(1);
|
|
260
|
+
if (sub === "list" || sub === "ls") {
|
|
261
|
+
const installed = listInstalledSkills();
|
|
262
|
+
if (installed.length === 0) {
|
|
263
|
+
console.log("No skills installed. Try: fastcode skills install <id>");
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
for (const s of installed)
|
|
267
|
+
console.log(s.name + "\t" + s.source + "\t" + s.path);
|
|
268
|
+
}
|
|
269
|
+
return { done: true };
|
|
270
|
+
}
|
|
271
|
+
if (sub === "install" || sub === "add") {
|
|
272
|
+
if (args.length === 0) {
|
|
273
|
+
console.error("Usage: fastcode skills install <id|owner/repo|url>");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
const cfg = loadConfig();
|
|
277
|
+
const force = args.includes("--force");
|
|
278
|
+
const spec = args.filter((a) => a !== "--force").join(" ");
|
|
279
|
+
const skill = await installSkill(spec, { registry: cfg.skillsRegistry, force });
|
|
280
|
+
console.log("Installed " + skill.name + " -> " + skill.path);
|
|
281
|
+
return { done: true };
|
|
282
|
+
}
|
|
283
|
+
if (sub === "uninstall" || sub === "remove" || sub === "rm") {
|
|
284
|
+
if (args.length === 0) {
|
|
285
|
+
console.error("Usage: fastcode skills uninstall <id>");
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const ok = uninstallSkill(args[0]);
|
|
289
|
+
console.log(ok ? "Removed " + args[0] + "." : "No skill named " + args[0] + ".");
|
|
290
|
+
return { done: true };
|
|
291
|
+
}
|
|
292
|
+
if (sub === "search") {
|
|
293
|
+
if (args.length === 0) {
|
|
294
|
+
console.error("Usage: fastcode skills search <query>");
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
const cfg = loadConfig();
|
|
298
|
+
const hits = await searchRegistry(args.join(" "), cfg.skillsRegistry);
|
|
299
|
+
if (hits.length === 0)
|
|
300
|
+
console.log("No matches.");
|
|
301
|
+
for (const h of hits.slice(0, 20)) {
|
|
302
|
+
console.log(h.slug + "\t" + (h.description ?? ""));
|
|
303
|
+
}
|
|
304
|
+
return { done: true };
|
|
305
|
+
}
|
|
306
|
+
console.error("Unknown skills sub-command: " + sub);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
if (first === "doctor") {
|
|
310
|
+
console.log(formatDiagnostics(collectDiagnostics()));
|
|
311
|
+
if (cli.flags.net === true) {
|
|
312
|
+
const url = "https://registry.npmjs.org/";
|
|
313
|
+
const reachable = await probeConnectivity(url);
|
|
314
|
+
console.log(` network ${reachable ? "reachable" : "unreachable"} (${url})`);
|
|
315
|
+
}
|
|
316
|
+
return { done: true };
|
|
317
|
+
}
|
|
318
|
+
if (first === "update") {
|
|
319
|
+
console.log(await updateReport());
|
|
320
|
+
return { done: true };
|
|
321
|
+
}
|
|
322
|
+
// No sub-command → treat all positionals as the initial prompt.
|
|
323
|
+
const prompt = cli.input.length > 0 ? cli.input.join(" ") : undefined;
|
|
324
|
+
return prompt ? { prompt, done: false, ui: uiMode } : { done: false, ui: uiMode };
|
|
325
|
+
}
|
|
326
|
+
function compareVersions(a, b) {
|
|
327
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
328
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
329
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
|
|
330
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
331
|
+
if (d !== 0)
|
|
332
|
+
return d > 0 ? 1 : -1;
|
|
333
|
+
}
|
|
334
|
+
return 0;
|
|
335
|
+
}
|
|
336
|
+
async function updateReport() {
|
|
337
|
+
const meta = readPackageMeta();
|
|
338
|
+
const lines = ["FastCode " + meta.version, ""];
|
|
339
|
+
const registry = process.env["FASTCODE_UPDATE_REGISTRY"] ??
|
|
340
|
+
"https://registry.npmjs.org/" + encodeURIComponent(meta.name) + "/latest";
|
|
341
|
+
try {
|
|
342
|
+
const res = await fetch(registry, { headers: { accept: "application/json" } });
|
|
343
|
+
if (!res.ok)
|
|
344
|
+
throw new Error(res.status + " " + res.statusText);
|
|
345
|
+
const payload = await res.json();
|
|
346
|
+
const latest = payload.version;
|
|
347
|
+
const cmp = compareVersions(latest, meta.version);
|
|
348
|
+
lines.push("Published latest: " + latest);
|
|
349
|
+
lines.push(cmp > 0
|
|
350
|
+
? "Update available: " + meta.version + " -> " + latest
|
|
351
|
+
: cmp < 0
|
|
352
|
+
? "This build (" + meta.version + ") is newer than the published " + latest + "."
|
|
353
|
+
: "You are up to date.");
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
lines.push("Could not reach the registry: " + err.message);
|
|
357
|
+
}
|
|
358
|
+
lines.push("");
|
|
359
|
+
lines.push("Update with one of:");
|
|
360
|
+
lines.push(" npm install -g " + meta.name + "@latest");
|
|
361
|
+
lines.push(" # or from a checkout: git pull && npm install -g .");
|
|
362
|
+
return lines.join(NL);
|
|
363
|
+
}
|