first commit
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
import { Command } from "commander";
|
||||
import { makeClient } from "./backend/client.js";
|
||||
import { ConfigError, resolveBackendConfig, resolveModel } from "./config/config.js";
|
||||
import { configFilePath, loadStoredConfig, saveStoredConfig, type StoredConfig } from "./config/store.js";
|
||||
import { runInkApp } from "./ui/ink/index.js";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
function addBackendOptions(cmd: Command): Command {
|
||||
return cmd
|
||||
.option("-b, --backend <name>", "backend to use: ollama or lmstudio")
|
||||
.option("--base-url <url>", "override the backend's base URL");
|
||||
}
|
||||
|
||||
program
|
||||
.name("locode")
|
||||
.description("Agentic coding CLI for local models via Ollama and LM Studio")
|
||||
.version("0.1.0");
|
||||
|
||||
addBackendOptions(program)
|
||||
.option("-m, --model <name>", "model name as known to the backend")
|
||||
.option(
|
||||
"--tool-mode <mode>",
|
||||
"native | fallback | auto — auto defaults to native, probing only on /model and /backend",
|
||||
"auto",
|
||||
)
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
const { baseURL } = resolveBackendConfig(opts);
|
||||
// Only an explicit --model flag skips the picker; a saved/env default is offered as a suggestion instead.
|
||||
const model: string | undefined = opts.model;
|
||||
const suggestedModel = model ? undefined : resolveModel(undefined);
|
||||
const toolModeOverride = opts.toolMode === "native" || opts.toolMode === "fallback" ? opts.toolMode : undefined;
|
||||
await runInkApp({ baseURL, model, suggestedModel, cwd: process.cwd(), toolModeOverride });
|
||||
} catch (err) {
|
||||
if (err instanceof ConfigError) {
|
||||
console.error(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error((err as Error).message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
addBackendOptions(program.command("models"))
|
||||
.description("List models available from the configured backend")
|
||||
.action(async (opts) => {
|
||||
try {
|
||||
const { baseURL } = resolveBackendConfig(opts);
|
||||
const client = makeClient({ baseURL, model: "" });
|
||||
const list = await client.models.list();
|
||||
for (const m of list.data) {
|
||||
console.log(m.id);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ConfigError) {
|
||||
console.error(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error(`Failed to list models: ${(err as Error).message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
const configCmd = program.command("config").description("Manage persisted config");
|
||||
|
||||
configCmd
|
||||
.command("get [key]")
|
||||
.description("Print the whole config, or one key (backend, model, baseUrl)")
|
||||
.action((key?: string) => {
|
||||
const stored = loadStoredConfig();
|
||||
if (!key) {
|
||||
console.log(JSON.stringify(stored, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(stored[key as keyof StoredConfig] ?? "(not set)");
|
||||
});
|
||||
|
||||
configCmd
|
||||
.command("set <key> <value>")
|
||||
.description("Persist a config value (backend, model, baseUrl)")
|
||||
.action((key: string, value: string) => {
|
||||
if (key !== "backend" && key !== "model" && key !== "baseUrl") {
|
||||
console.error(`Unknown config key "${key}". Valid keys: backend, model, baseUrl`);
|
||||
process.exit(1);
|
||||
}
|
||||
const stored = loadStoredConfig();
|
||||
stored[key] = value;
|
||||
saveStoredConfig(stored);
|
||||
console.log(`Set ${key} = ${value}`);
|
||||
});
|
||||
|
||||
configCmd
|
||||
.command("path")
|
||||
.description("Print the path to the persisted config file")
|
||||
.action(() => {
|
||||
console.log(configFilePath());
|
||||
});
|
||||
|
||||
program.parseAsync();
|
||||
Reference in New Issue
Block a user