topchester-ai 0.44.0 → 0.45.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/dist/bin.mjs +1 -1
- package/dist/{cli-Cz5puJBe.mjs → cli-BgMj4Ifj.mjs} +185 -20
- package/dist/cli-BgMj4Ifj.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/package.json +1 -1
- package/dist/cli-Cz5puJBe.mjs.map +0 -1
package/dist/bin.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { generateText, stepCountIs, streamText, tool } from "ai";
|
|
|
7
7
|
import { ZodError, z } from "zod";
|
|
8
8
|
import { access, mkdir, open, readFile, readdir, realpath, rename, rm, stat, truncate, writeFile } from "node:fs/promises";
|
|
9
9
|
import { execFile, spawn } from "node:child_process";
|
|
10
|
-
import { constants, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { accessSync, constants, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { parseDocument } from "yaml";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
@@ -34,7 +34,7 @@ const DEFAULT_MAX_OUTPUT_LINES = 1e3;
|
|
|
34
34
|
async function runProcess(options) {
|
|
35
35
|
const startedAt = Date.now();
|
|
36
36
|
const pathEnv = options.pathEnv ?? process.env.PATH ?? "";
|
|
37
|
-
const executablePath = options.executable.includes("/") || isAbsolute(options.executable) ? await resolveExecutablePath(options.executable) : await findExecutable$
|
|
37
|
+
const executablePath = options.executable.includes("/") || isAbsolute(options.executable) ? await resolveExecutablePath(options.executable) : await findExecutable$3(options.executable, pathEnv);
|
|
38
38
|
if (!executablePath) return {
|
|
39
39
|
stdout: "",
|
|
40
40
|
stderr: `${options.missingExecutableLabel ?? "command"} could not run because '${options.executable}' is not available on PATH.\n`,
|
|
@@ -47,7 +47,7 @@ async function runProcess(options) {
|
|
|
47
47
|
};
|
|
48
48
|
return runSpawnedProcess(executablePath, options, startedAt, pathEnv);
|
|
49
49
|
}
|
|
50
|
-
async function findExecutable$
|
|
50
|
+
async function findExecutable$3(name, pathEnv) {
|
|
51
51
|
for (const pathEntry of pathEnv.split(delimiter).filter(Boolean)) {
|
|
52
52
|
const executablePath = join(pathEntry, name);
|
|
53
53
|
try {
|
|
@@ -1200,7 +1200,7 @@ async function collectWorkspaceFilesWithNode(workspaceRoot, startPath) {
|
|
|
1200
1200
|
return files;
|
|
1201
1201
|
}
|
|
1202
1202
|
async function createRipgrepCollector(pathEnv, relativeStartPath) {
|
|
1203
|
-
const command = await findExecutable$
|
|
1203
|
+
const command = await findExecutable$2("rg", pathEnv);
|
|
1204
1204
|
if (!command) return;
|
|
1205
1205
|
return {
|
|
1206
1206
|
name: "rg",
|
|
@@ -1216,8 +1216,8 @@ async function createRipgrepCollector(pathEnv, relativeStartPath) {
|
|
|
1216
1216
|
};
|
|
1217
1217
|
}
|
|
1218
1218
|
async function createFdCollector(pathEnv, relativeStartPath) {
|
|
1219
|
-
const fdCommand = await findExecutable$
|
|
1220
|
-
const fdfindCommand = fdCommand ? void 0 : await findExecutable$
|
|
1219
|
+
const fdCommand = await findExecutable$2("fd", pathEnv);
|
|
1220
|
+
const fdfindCommand = fdCommand ? void 0 : await findExecutable$2("fdfind", pathEnv);
|
|
1221
1221
|
const command = fdCommand ?? fdfindCommand;
|
|
1222
1222
|
if (!command) return;
|
|
1223
1223
|
return {
|
|
@@ -1237,7 +1237,7 @@ async function createFdCollector(pathEnv, relativeStartPath) {
|
|
|
1237
1237
|
};
|
|
1238
1238
|
}
|
|
1239
1239
|
async function createFindCollector(pathEnv, relativeStartPath) {
|
|
1240
|
-
const command = await findExecutable$
|
|
1240
|
+
const command = await findExecutable$2("find", pathEnv);
|
|
1241
1241
|
if (!command) return;
|
|
1242
1242
|
return {
|
|
1243
1243
|
name: "find",
|
|
@@ -1332,7 +1332,7 @@ function resolveWorkspaceScopedPath$3(workspaceRoot, path) {
|
|
|
1332
1332
|
relativePath: relativePath || "."
|
|
1333
1333
|
};
|
|
1334
1334
|
}
|
|
1335
|
-
async function findExecutable$
|
|
1335
|
+
async function findExecutable$2(name, pathEnv) {
|
|
1336
1336
|
for (const pathEntry of pathEnv.split(delimiter).filter(Boolean)) {
|
|
1337
1337
|
const executablePath = join(pathEntry, name);
|
|
1338
1338
|
try {
|
|
@@ -2188,14 +2188,14 @@ function resolveWorkspaceScopedPath$2(workspaceRoot, path) {
|
|
|
2188
2188
|
}
|
|
2189
2189
|
async function findSearchExecutable(pathEnv = process.env.PATH ?? "") {
|
|
2190
2190
|
for (const name of ["rg", "grep"]) {
|
|
2191
|
-
const executablePath = await findExecutable(name, pathEnv);
|
|
2191
|
+
const executablePath = await findExecutable$1(name, pathEnv);
|
|
2192
2192
|
if (executablePath) return {
|
|
2193
2193
|
name,
|
|
2194
2194
|
path: executablePath
|
|
2195
2195
|
};
|
|
2196
2196
|
}
|
|
2197
2197
|
}
|
|
2198
|
-
async function findExecutable(name, pathEnv) {
|
|
2198
|
+
async function findExecutable$1(name, pathEnv) {
|
|
2199
2199
|
for (const pathEntry of pathEnv.split(delimiter).filter(Boolean)) {
|
|
2200
2200
|
const executablePath = join(pathEntry, name);
|
|
2201
2201
|
try {
|
|
@@ -5677,13 +5677,7 @@ function ensureGlobalTopchesterConfigFile() {
|
|
|
5677
5677
|
return configPath;
|
|
5678
5678
|
}
|
|
5679
5679
|
function loadTopchesterConfig(options) {
|
|
5680
|
-
const
|
|
5681
|
-
const paths = [
|
|
5682
|
-
join(options.workspaceRoot, "topchester.jsonc"),
|
|
5683
|
-
join(globalConfigDir, "config.jsonc"),
|
|
5684
|
-
process.env.TOPCHESTER_CONFIG,
|
|
5685
|
-
options.configPath
|
|
5686
|
-
].filter((path) => Boolean(path));
|
|
5680
|
+
const paths = getTopchesterConfigSources(options).map((source) => source.path).filter((path) => Boolean(path));
|
|
5687
5681
|
let merged = {};
|
|
5688
5682
|
for (const path of paths) {
|
|
5689
5683
|
const resolvedPath = isAbsolute(path) ? path : resolve(options.workspaceRoot, path);
|
|
@@ -5693,6 +5687,33 @@ function loadTopchesterConfig(options) {
|
|
|
5693
5687
|
}
|
|
5694
5688
|
return topchesterConfigSchema.parse(merged);
|
|
5695
5689
|
}
|
|
5690
|
+
function getTopchesterConfigSources(options) {
|
|
5691
|
+
return [
|
|
5692
|
+
{
|
|
5693
|
+
label: "workspace",
|
|
5694
|
+
path: join(options.workspaceRoot, "topchester.jsonc")
|
|
5695
|
+
},
|
|
5696
|
+
{
|
|
5697
|
+
label: "user",
|
|
5698
|
+
path: getGlobalTopchesterConfigPath()
|
|
5699
|
+
},
|
|
5700
|
+
{
|
|
5701
|
+
label: "env",
|
|
5702
|
+
path: process.env.TOPCHESTER_CONFIG || void 0
|
|
5703
|
+
},
|
|
5704
|
+
{
|
|
5705
|
+
label: "cli",
|
|
5706
|
+
path: options.configPath
|
|
5707
|
+
}
|
|
5708
|
+
].map((source) => {
|
|
5709
|
+
const resolvedPath = source.path === void 0 ? void 0 : isAbsolute(source.path) ? source.path : resolve(options.workspaceRoot, source.path);
|
|
5710
|
+
return {
|
|
5711
|
+
label: source.label,
|
|
5712
|
+
...resolvedPath === void 0 ? {} : { path: resolvedPath },
|
|
5713
|
+
exists: resolvedPath === void 0 ? false : existsSync(resolvedPath)
|
|
5714
|
+
};
|
|
5715
|
+
});
|
|
5716
|
+
}
|
|
5696
5717
|
const openRouterProviderDefaults = {
|
|
5697
5718
|
type: "openai-compatible",
|
|
5698
5719
|
baseURL: "https://openrouter.ai/api/v1",
|
|
@@ -14069,6 +14090,142 @@ function defaultSelfUpdateCheckRunner(command, args) {
|
|
|
14069
14090
|
});
|
|
14070
14091
|
}
|
|
14071
14092
|
//#endregion
|
|
14093
|
+
//#region src/cli/info.ts
|
|
14094
|
+
async function collectTopchesterInfo(options) {
|
|
14095
|
+
const sources = getTopchesterConfigSources(options);
|
|
14096
|
+
const lines = [
|
|
14097
|
+
color("Topchester info", "cyan"),
|
|
14098
|
+
"",
|
|
14099
|
+
section("summary"),
|
|
14100
|
+
row("version", getTopchesterVersion()),
|
|
14101
|
+
row("workspace", formatInfoPath(options.workspaceRoot)),
|
|
14102
|
+
"",
|
|
14103
|
+
section("config"),
|
|
14104
|
+
...sources.map((source) => {
|
|
14105
|
+
if (!source.path) return row(formatConfigSourceLabel(source.label), status("unset", "muted"));
|
|
14106
|
+
return row(formatConfigSourceLabel(source.label), `${formatInfoPath(source.path)} ${statusBadge(source.exists)}`);
|
|
14107
|
+
})
|
|
14108
|
+
];
|
|
14109
|
+
let config;
|
|
14110
|
+
try {
|
|
14111
|
+
config = loadTopchesterConfig(options);
|
|
14112
|
+
} catch (error) {
|
|
14113
|
+
lines.push(row("status", status("invalid", "bad")), row("error", ui.error(formatInfoError(error))));
|
|
14114
|
+
return {
|
|
14115
|
+
ok: false,
|
|
14116
|
+
lines
|
|
14117
|
+
};
|
|
14118
|
+
}
|
|
14119
|
+
lines.push(row("status", status("valid", "good")), "", ...formatModelHints(config), "", ...formatProviderHints(config));
|
|
14120
|
+
lines.push("", ...formatMcpHints(config), "", ...formatHooksHints(config), "", ...formatPathHints(options));
|
|
14121
|
+
if (options.devFlags && options.devFlags.length > 0) lines.push("", section("dev"), row("flags", options.devFlags.join(", ")));
|
|
14122
|
+
return {
|
|
14123
|
+
ok: true,
|
|
14124
|
+
lines
|
|
14125
|
+
};
|
|
14126
|
+
}
|
|
14127
|
+
function formatModelHints(config) {
|
|
14128
|
+
const assignments = config.models?.assignments ?? {};
|
|
14129
|
+
const purposes = Object.keys(assignments).sort();
|
|
14130
|
+
if (purposes.length === 0) return [
|
|
14131
|
+
section("models"),
|
|
14132
|
+
row("configured", status("none", "muted")),
|
|
14133
|
+
row("hint", "run /connect openrouter, then /model")
|
|
14134
|
+
];
|
|
14135
|
+
return [
|
|
14136
|
+
section("models"),
|
|
14137
|
+
row("default purpose", config.models?.defaultPurpose ?? "agent.primary"),
|
|
14138
|
+
...purposes.map((purpose) => row(purpose, ui.model(formatModelRef(assignments[purpose]))))
|
|
14139
|
+
];
|
|
14140
|
+
}
|
|
14141
|
+
function formatProviderHints(config) {
|
|
14142
|
+
const providers = config.models?.providers ?? {};
|
|
14143
|
+
const namedProviders = Object.entries(providers).filter(([providerId]) => providerId !== "default");
|
|
14144
|
+
if (namedProviders.length === 0) return [section("providers"), row("configured", status("none", "muted"))];
|
|
14145
|
+
const lines = [section("providers")];
|
|
14146
|
+
if (typeof providers.default === "string") lines.push(row("default", providers.default));
|
|
14147
|
+
for (const [providerId, provider] of namedProviders) {
|
|
14148
|
+
if (typeof provider === "string") continue;
|
|
14149
|
+
const auth = provider.apiKeyEnv ? `env:${provider.apiKeyEnv} ${statusBadge(Boolean(process.env[provider.apiKeyEnv]), "set")}` : provider.apiKey ? status("inline", "good") : status("none", "muted");
|
|
14150
|
+
lines.push(row(providerId, `${provider.type} ${provider.baseURL} auth=${auth}`));
|
|
14151
|
+
}
|
|
14152
|
+
return lines;
|
|
14153
|
+
}
|
|
14154
|
+
function formatMcpHints(config) {
|
|
14155
|
+
const servers = Object.entries(config.mcp ?? {});
|
|
14156
|
+
if (servers.length === 0) return [section("mcp"), row("servers", status("none", "muted"))];
|
|
14157
|
+
return [section("mcp"), ...servers.map(([serverName, server]) => {
|
|
14158
|
+
const tools = server.enabledTools && server.enabledTools.length > 0 ? server.enabledTools.join(",") : "all under cap";
|
|
14159
|
+
const commandFound = Boolean(findExecutable(server.command));
|
|
14160
|
+
return row(serverName, `${server.enabled === false ? status("disabled", "muted") : status("enabled", "good")} command=${server.command} ${statusBadge(commandFound, "found")} tools=${tools}`);
|
|
14161
|
+
})];
|
|
14162
|
+
}
|
|
14163
|
+
function formatHooksHints(config) {
|
|
14164
|
+
const hooks = config.hooks ?? {};
|
|
14165
|
+
const hookEntries = Object.entries(hooks).filter((entry) => Array.isArray(entry[1]) && entry[1].length > 0);
|
|
14166
|
+
const commandCount = hookEntries.reduce((count, [, commands]) => count + commands.length, 0);
|
|
14167
|
+
if (commandCount === 0) return [section("hooks"), row("commands", status("none", "muted"))];
|
|
14168
|
+
return [
|
|
14169
|
+
section("hooks"),
|
|
14170
|
+
row("events", String(hookEntries.length)),
|
|
14171
|
+
row("commands", String(commandCount))
|
|
14172
|
+
];
|
|
14173
|
+
}
|
|
14174
|
+
function formatPathHints(options) {
|
|
14175
|
+
const knowledgePath = join(options.workspaceRoot, "topchester-kb");
|
|
14176
|
+
return [
|
|
14177
|
+
section("paths"),
|
|
14178
|
+
row("sessions", formatInfoPath(getTopchesterSessionsPath(options.workspaceRoot))),
|
|
14179
|
+
row("log file", formatInfoPath(getTopchesterLogFilePath(options.workspaceRoot))),
|
|
14180
|
+
row("knowledge", `${formatInfoPath(knowledgePath)} ${statusBadge(existsSync(knowledgePath))}`)
|
|
14181
|
+
];
|
|
14182
|
+
}
|
|
14183
|
+
function section(title) {
|
|
14184
|
+
return color(`${title}:`, "cyan");
|
|
14185
|
+
}
|
|
14186
|
+
function row(label, value) {
|
|
14187
|
+
return ` ${ui.label(label)}: ${value}`;
|
|
14188
|
+
}
|
|
14189
|
+
function statusBadge(ok, okText = "ok") {
|
|
14190
|
+
return `[${status(ok ? okText : "missing", ok ? "good" : "warn")}]`;
|
|
14191
|
+
}
|
|
14192
|
+
function status(text, tone) {
|
|
14193
|
+
switch (tone) {
|
|
14194
|
+
case "good": return ui.ok(text);
|
|
14195
|
+
case "warn": return ui.warn(text);
|
|
14196
|
+
case "bad": return ui.error(text);
|
|
14197
|
+
case "muted": return ui.muted(text);
|
|
14198
|
+
}
|
|
14199
|
+
}
|
|
14200
|
+
function formatConfigSourceLabel(label) {
|
|
14201
|
+
return label === "env" ? "env TOPCHESTER_CONFIG" : label === "cli" ? "cli --config" : label;
|
|
14202
|
+
}
|
|
14203
|
+
function findExecutable(command) {
|
|
14204
|
+
if (command.includes("/") || isAbsolute(command)) return canExecute(command) ? command : void 0;
|
|
14205
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
14206
|
+
if (!dir) continue;
|
|
14207
|
+
const candidate = resolve(dir, command);
|
|
14208
|
+
if (canExecute(candidate)) return candidate;
|
|
14209
|
+
}
|
|
14210
|
+
}
|
|
14211
|
+
function canExecute(path) {
|
|
14212
|
+
try {
|
|
14213
|
+
accessSync(path, constants.X_OK);
|
|
14214
|
+
return true;
|
|
14215
|
+
} catch {
|
|
14216
|
+
return false;
|
|
14217
|
+
}
|
|
14218
|
+
}
|
|
14219
|
+
function formatInfoPath(path) {
|
|
14220
|
+
const homeRelative = relative(homedir(), path);
|
|
14221
|
+
if (!homeRelative) return "~";
|
|
14222
|
+
if (!homeRelative.startsWith("..") && !isAbsolute(homeRelative)) return `~/${homeRelative}`;
|
|
14223
|
+
return path;
|
|
14224
|
+
}
|
|
14225
|
+
function formatInfoError(error) {
|
|
14226
|
+
return error instanceof Error ? error.message : String(error);
|
|
14227
|
+
}
|
|
14228
|
+
//#endregion
|
|
14072
14229
|
//#region src/cli.ts
|
|
14073
14230
|
async function runTopchesterCli(argv = process.argv, options = {}) {
|
|
14074
14231
|
const program = createTopchesterProgram();
|
|
@@ -14105,6 +14262,11 @@ function createTopchesterProgram() {
|
|
|
14105
14262
|
console.log("Topchester local dev mode");
|
|
14106
14263
|
printStartupSummary(context);
|
|
14107
14264
|
});
|
|
14265
|
+
program.command("info").description("show config and local runtime hints").action(async () => {
|
|
14266
|
+
const result = await collectTopchesterInfo(getContextOptionsFromProgram(program));
|
|
14267
|
+
console.log(result.lines.join("\n"));
|
|
14268
|
+
if (!result.ok) process.exitCode = 1;
|
|
14269
|
+
});
|
|
14108
14270
|
program.command("run").description("run one prompt or slash command without opening the TUI").argument("<prompt...>", "prompt text or slash command").option("--model <model>", "override the agent.primary model for this run").option("--timeout <ms>", "timeout for the run in milliseconds", parsePositiveInteger).option("--json", "write JSONL run events to stdout").option("--output-json <path>", "write JSONL run events to a file").action(async (promptParts, options) => {
|
|
14109
14271
|
const context = createContextFromOptions(program);
|
|
14110
14272
|
const globalOptions = program.opts();
|
|
@@ -14211,12 +14373,15 @@ function printStartupSummary(context) {
|
|
|
14211
14373
|
}
|
|
14212
14374
|
}
|
|
14213
14375
|
function createContextFromOptions(program) {
|
|
14376
|
+
return createAppContext(getContextOptionsFromProgram(program));
|
|
14377
|
+
}
|
|
14378
|
+
function getContextOptionsFromProgram(program) {
|
|
14214
14379
|
const options = program.opts();
|
|
14215
|
-
return
|
|
14380
|
+
return {
|
|
14216
14381
|
workspaceRoot: options.workspace,
|
|
14217
14382
|
configPath: options.config && (isAbsolute(options.config) ? options.config : resolve(cwd(), options.config)),
|
|
14218
14383
|
devFlags: options.dev
|
|
14219
|
-
}
|
|
14384
|
+
};
|
|
14220
14385
|
}
|
|
14221
14386
|
async function executeKbSearchCommand(program, queryParts, options) {
|
|
14222
14387
|
const context = createContextFromOptions(program);
|
|
@@ -14269,4 +14434,4 @@ function formatDryRunSyncStatus(status) {
|
|
|
14269
14434
|
//#endregion
|
|
14270
14435
|
export { runTopchesterCli as t };
|
|
14271
14436
|
|
|
14272
|
-
//# sourceMappingURL=cli-
|
|
14437
|
+
//# sourceMappingURL=cli-BgMj4Ifj.mjs.map
|