topchester-ai 0.43.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/README.md +13 -16
- package/dist/bin.mjs +1 -1
- package/dist/{cli-CVWQPq7z.mjs → cli-BgMj4Ifj.mjs} +298 -27
- package/dist/cli-BgMj4Ifj.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/package.json +2 -2
- package/skills/topchester-config/SKILL.md +19 -18
- package/dist/cli-CVWQPq7z.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -117,16 +117,12 @@ Do not commit API keys. Put keys in environment variables, a user config file, o
|
|
|
117
117
|
|
|
118
118
|
Topchester reads config in this order, with later entries overriding earlier ones:
|
|
119
119
|
|
|
120
|
-
1.
|
|
120
|
+
1. `topchester.jsonc`
|
|
121
121
|
2. `~/.config/topchester/config.jsonc`
|
|
122
|
-
3. `
|
|
123
|
-
4.
|
|
124
|
-
5. `.topchester/config.local.yaml`
|
|
125
|
-
6. `.topchester/config.local.jsonc`
|
|
126
|
-
7. `TOPCHESTER_CONFIG`
|
|
127
|
-
8. `--config <path>`
|
|
122
|
+
3. `TOPCHESTER_CONFIG`
|
|
123
|
+
4. `--config <path>`
|
|
128
124
|
|
|
129
|
-
|
|
125
|
+
On first startup, Topchester creates `~/.config/topchester/config.jsonc` with a commented minimal example. Uncomment it to set your personal default model, or keep shared project policy in `topchester.jsonc`.
|
|
130
126
|
|
|
131
127
|
## How The Knowledge Base Works
|
|
132
128
|
|
|
@@ -166,11 +162,12 @@ The package name is `topchester-ai`; the installed command is `topchester`.
|
|
|
166
162
|
## Read More
|
|
167
163
|
|
|
168
164
|
- [Onboarding](onboarding.md)
|
|
169
|
-
- [
|
|
170
|
-
- [
|
|
171
|
-
- [
|
|
172
|
-
- [
|
|
173
|
-
- [
|
|
174
|
-
- [
|
|
175
|
-
- [
|
|
176
|
-
- [
|
|
165
|
+
- [Docs](docs/README.md)
|
|
166
|
+
- [Quickstart](docs/getting-started/quickstart.md)
|
|
167
|
+
- [CLI Commands](docs/reference/cli.md)
|
|
168
|
+
- [TUI](docs/features/tui.md)
|
|
169
|
+
- [Skills](docs/features/skills.md)
|
|
170
|
+
- [Configuration](docs/configuration/config-files.md)
|
|
171
|
+
- [Models and providers](docs/configuration/models-and-providers.md)
|
|
172
|
+
- [Knowledge base](docs/features/knowledge-base.md)
|
|
173
|
+
- [Sessions](docs/features/sessions.md)
|
package/dist/bin.mjs
CHANGED
|
@@ -7,9 +7,9 @@ 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 } from "node:fs";
|
|
10
|
+
import { accessSync, constants, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
12
|
-
import {
|
|
12
|
+
import { parseDocument } from "yaml";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import pino from "pino";
|
|
@@ -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 {
|
|
@@ -5668,16 +5668,16 @@ function ensureGlobalTopchesterConfigDir() {
|
|
|
5668
5668
|
});
|
|
5669
5669
|
return dir;
|
|
5670
5670
|
}
|
|
5671
|
+
function ensureGlobalTopchesterConfigFile() {
|
|
5672
|
+
const configPath = getGlobalTopchesterConfigPath();
|
|
5673
|
+
if (!existsSync(configPath)) {
|
|
5674
|
+
ensureGlobalTopchesterConfigDir();
|
|
5675
|
+
writeFileSync(configPath, getCommentedStarterConfig(), { mode: 384 });
|
|
5676
|
+
}
|
|
5677
|
+
return configPath;
|
|
5678
|
+
}
|
|
5671
5679
|
function loadTopchesterConfig(options) {
|
|
5672
|
-
const
|
|
5673
|
-
const paths = [
|
|
5674
|
-
join(options.workspaceRoot, "topchester.yaml"),
|
|
5675
|
-
join(options.workspaceRoot, "topchester.jsonc"),
|
|
5676
|
-
join(globalConfigDir, "config.yaml"),
|
|
5677
|
-
join(globalConfigDir, "config.jsonc"),
|
|
5678
|
-
process.env.TOPCHESTER_CONFIG,
|
|
5679
|
-
options.configPath
|
|
5680
|
-
].filter((path) => Boolean(path));
|
|
5680
|
+
const paths = getTopchesterConfigSources(options).map((source) => source.path).filter((path) => Boolean(path));
|
|
5681
5681
|
let merged = {};
|
|
5682
5682
|
for (const path of paths) {
|
|
5683
5683
|
const resolvedPath = isAbsolute(path) ? path : resolve(options.workspaceRoot, path);
|
|
@@ -5687,6 +5687,33 @@ function loadTopchesterConfig(options) {
|
|
|
5687
5687
|
}
|
|
5688
5688
|
return topchesterConfigSchema.parse(merged);
|
|
5689
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
|
+
}
|
|
5690
5717
|
const openRouterProviderDefaults = {
|
|
5691
5718
|
type: "openai-compatible",
|
|
5692
5719
|
baseURL: "https://openrouter.ai/api/v1",
|
|
@@ -5821,11 +5848,100 @@ function ensureStringArrayProperty(parent, key) {
|
|
|
5821
5848
|
}
|
|
5822
5849
|
function readConfigFile(path) {
|
|
5823
5850
|
try {
|
|
5824
|
-
return
|
|
5851
|
+
return parseJsonc(readFileSync(path, "utf8"));
|
|
5825
5852
|
} catch (error) {
|
|
5826
5853
|
throw new Error(`Invalid Topchester config at ${path}: ${formatErrorMessage$1(error)}`);
|
|
5827
5854
|
}
|
|
5828
5855
|
}
|
|
5856
|
+
function parseJsonc(source) {
|
|
5857
|
+
const stripped = stripJsoncSyntax(source);
|
|
5858
|
+
return stripped.trim() ? JSON.parse(stripped) : {};
|
|
5859
|
+
}
|
|
5860
|
+
function getCommentedStarterConfig() {
|
|
5861
|
+
return [
|
|
5862
|
+
"// Uncomment and edit this minimal config to choose a default model.",
|
|
5863
|
+
"// {",
|
|
5864
|
+
"// \"models\": {",
|
|
5865
|
+
"// \"default\": \"openrouter/google/gemini-3.1-flash-lite\",",
|
|
5866
|
+
"// },",
|
|
5867
|
+
"// }",
|
|
5868
|
+
""
|
|
5869
|
+
].join("\n");
|
|
5870
|
+
}
|
|
5871
|
+
function stripJsoncSyntax(source) {
|
|
5872
|
+
let output = "";
|
|
5873
|
+
let inString = false;
|
|
5874
|
+
let escaped = false;
|
|
5875
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
5876
|
+
const char = source[index];
|
|
5877
|
+
const next = source[index + 1];
|
|
5878
|
+
if (inString) {
|
|
5879
|
+
output += char;
|
|
5880
|
+
if (escaped) escaped = false;
|
|
5881
|
+
else if (char === "\\") escaped = true;
|
|
5882
|
+
else if (char === "\"") inString = false;
|
|
5883
|
+
continue;
|
|
5884
|
+
}
|
|
5885
|
+
if (char === "\"") {
|
|
5886
|
+
inString = true;
|
|
5887
|
+
output += char;
|
|
5888
|
+
continue;
|
|
5889
|
+
}
|
|
5890
|
+
if (char === "/" && next === "/") {
|
|
5891
|
+
output += " ";
|
|
5892
|
+
index += 2;
|
|
5893
|
+
while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
|
|
5894
|
+
output += " ";
|
|
5895
|
+
index += 1;
|
|
5896
|
+
}
|
|
5897
|
+
index -= 1;
|
|
5898
|
+
continue;
|
|
5899
|
+
}
|
|
5900
|
+
if (char === "/" && next === "*") {
|
|
5901
|
+
output += " ";
|
|
5902
|
+
index += 2;
|
|
5903
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) {
|
|
5904
|
+
output += source[index] === "\n" || source[index] === "\r" ? source[index] : " ";
|
|
5905
|
+
index += 1;
|
|
5906
|
+
}
|
|
5907
|
+
if (index < source.length) {
|
|
5908
|
+
output += " ";
|
|
5909
|
+
index += 1;
|
|
5910
|
+
}
|
|
5911
|
+
continue;
|
|
5912
|
+
}
|
|
5913
|
+
if (char === "," && isTrailingJsonComma(source, index + 1)) {
|
|
5914
|
+
output += " ";
|
|
5915
|
+
continue;
|
|
5916
|
+
}
|
|
5917
|
+
output += char;
|
|
5918
|
+
}
|
|
5919
|
+
return output;
|
|
5920
|
+
}
|
|
5921
|
+
function isTrailingJsonComma(source, startIndex) {
|
|
5922
|
+
let index = startIndex;
|
|
5923
|
+
while (index < source.length) {
|
|
5924
|
+
const char = source[index];
|
|
5925
|
+
const next = source[index + 1];
|
|
5926
|
+
if (/\s/u.test(char)) {
|
|
5927
|
+
index += 1;
|
|
5928
|
+
continue;
|
|
5929
|
+
}
|
|
5930
|
+
if (char === "/" && next === "/") {
|
|
5931
|
+
index += 2;
|
|
5932
|
+
while (index < source.length && source[index] !== "\n" && source[index] !== "\r") index += 1;
|
|
5933
|
+
continue;
|
|
5934
|
+
}
|
|
5935
|
+
if (char === "/" && next === "*") {
|
|
5936
|
+
index += 2;
|
|
5937
|
+
while (index < source.length && !(source[index] === "*" && source[index + 1] === "/")) index += 1;
|
|
5938
|
+
index += 2;
|
|
5939
|
+
continue;
|
|
5940
|
+
}
|
|
5941
|
+
return char === "}" || char === "]";
|
|
5942
|
+
}
|
|
5943
|
+
return false;
|
|
5944
|
+
}
|
|
5829
5945
|
function parseConfigFile(path, value) {
|
|
5830
5946
|
const raw = rawTopchesterConfigSchema.safeParse(value ?? {});
|
|
5831
5947
|
if (!raw.success) throw new Error(`Invalid Topchester config at ${path}: ${raw.error.issues.map(formatZodIssue).join("; ")}`);
|
|
@@ -6041,7 +6157,7 @@ function normalizeLogLevel(level) {
|
|
|
6041
6157
|
//#endregion
|
|
6042
6158
|
//#region src/app/context.ts
|
|
6043
6159
|
function createAppContext(options) {
|
|
6044
|
-
|
|
6160
|
+
ensureGlobalTopchesterConfigFile();
|
|
6045
6161
|
const config = loadTopchesterConfig(options);
|
|
6046
6162
|
const modelGateway = createModelGatewayFromConfig(config);
|
|
6047
6163
|
const loggerInfo = createTopchesterLogger(options.workspaceRoot);
|
|
@@ -12050,6 +12166,11 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
12050
12166
|
else {
|
|
12051
12167
|
const approval = await this.resolveBashApproval(executableToolCall, toolCall.id, options, session, abortSignal);
|
|
12052
12168
|
for (const event of approval.events) yield event;
|
|
12169
|
+
if (approval.cancelled && approval.stopped) {
|
|
12170
|
+
if (!approval.events.some((event) => event.type === "message" && event.text === approval.reason)) yield agentEvent.systemMessage(approval.reason);
|
|
12171
|
+
yield agentEvent.status("ready");
|
|
12172
|
+
return;
|
|
12173
|
+
}
|
|
12053
12174
|
if (approval.cancelled) toolResult = createToolErrorResult(executableToolCall.tool, approval.reason);
|
|
12054
12175
|
else {
|
|
12055
12176
|
const toolEventQueue = createRuntimeEventQueue();
|
|
@@ -12293,9 +12414,15 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
12293
12414
|
abortSignal
|
|
12294
12415
|
});
|
|
12295
12416
|
const events = this.hookResultToEvents(actionRequiredHook);
|
|
12296
|
-
if (actionRequiredHook.
|
|
12417
|
+
if (actionRequiredHook.stopped) return {
|
|
12418
|
+
cancelled: true,
|
|
12419
|
+
stopped: true,
|
|
12420
|
+
reason: actionRequiredHook.stopped.message,
|
|
12421
|
+
events
|
|
12422
|
+
};
|
|
12423
|
+
if (actionRequiredHook.blocked) return {
|
|
12297
12424
|
cancelled: true,
|
|
12298
|
-
reason:
|
|
12425
|
+
reason: actionRequiredHook.blocked.message,
|
|
12299
12426
|
events
|
|
12300
12427
|
};
|
|
12301
12428
|
const approval = await options.requestBashApproval({
|
|
@@ -13963,6 +14090,142 @@ function defaultSelfUpdateCheckRunner(command, args) {
|
|
|
13963
14090
|
});
|
|
13964
14091
|
}
|
|
13965
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
|
|
13966
14229
|
//#region src/cli.ts
|
|
13967
14230
|
async function runTopchesterCli(argv = process.argv, options = {}) {
|
|
13968
14231
|
const program = createTopchesterProgram();
|
|
@@ -13999,6 +14262,11 @@ function createTopchesterProgram() {
|
|
|
13999
14262
|
console.log("Topchester local dev mode");
|
|
14000
14263
|
printStartupSummary(context);
|
|
14001
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
|
+
});
|
|
14002
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) => {
|
|
14003
14271
|
const context = createContextFromOptions(program);
|
|
14004
14272
|
const globalOptions = program.opts();
|
|
@@ -14105,12 +14373,15 @@ function printStartupSummary(context) {
|
|
|
14105
14373
|
}
|
|
14106
14374
|
}
|
|
14107
14375
|
function createContextFromOptions(program) {
|
|
14376
|
+
return createAppContext(getContextOptionsFromProgram(program));
|
|
14377
|
+
}
|
|
14378
|
+
function getContextOptionsFromProgram(program) {
|
|
14108
14379
|
const options = program.opts();
|
|
14109
|
-
return
|
|
14380
|
+
return {
|
|
14110
14381
|
workspaceRoot: options.workspace,
|
|
14111
14382
|
configPath: options.config && (isAbsolute(options.config) ? options.config : resolve(cwd(), options.config)),
|
|
14112
14383
|
devFlags: options.dev
|
|
14113
|
-
}
|
|
14384
|
+
};
|
|
14114
14385
|
}
|
|
14115
14386
|
async function executeKbSearchCommand(program, queryParts, options) {
|
|
14116
14387
|
const context = createContextFromOptions(program);
|
|
@@ -14163,4 +14434,4 @@ function formatDryRunSyncStatus(status) {
|
|
|
14163
14434
|
//#endregion
|
|
14164
14435
|
export { runTopchesterCli as t };
|
|
14165
14436
|
|
|
14166
|
-
//# sourceMappingURL=cli-
|
|
14437
|
+
//# sourceMappingURL=cli-BgMj4Ifj.mjs.map
|