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 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. `~/.config/topchester/config.yaml`
120
+ 1. `topchester.jsonc`
121
121
  2. `~/.config/topchester/config.jsonc`
122
- 3. `topchester.yaml`
123
- 4. `topchester.jsonc`
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
- Prefer `topchester.jsonc` for new project config. YAML paths are kept for compatibility.
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
- - [CLI Commands](docs/cli.md)
170
- - [TUI Guide](docs/tui.md)
171
- - [Skills](docs/skills.md)
172
- - [Configuration](docs/config.md)
173
- - [Model Configuration](docs/MODEL_CONFIG.md)
174
- - [Knowledge System](docs/KNOWLEDGE.md)
175
- - [Architecture](docs/ARCHITECTURE.md)
176
- - [Sessions](docs/SESSIONS.md)
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runTopchesterCli } from "./cli-CVWQPq7z.mjs";
2
+ import { t as runTopchesterCli } from "./cli-BgMj4Ifj.mjs";
3
3
  //#region src/bin.ts
4
4
  await runTopchesterCli();
5
5
  //#endregion
@@ -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 { parse as parse$1, parseDocument } from "yaml";
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$2(options.executable, pathEnv);
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$2(name, pathEnv) {
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$1("rg", pathEnv);
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$1("fd", pathEnv);
1220
- const fdfindCommand = fdCommand ? void 0 : await findExecutable$1("fdfind", pathEnv);
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$1("find", pathEnv);
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$1(name, pathEnv) {
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 globalConfigDir = getGlobalTopchesterConfigDir();
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 parse$1(readFileSync(path, "utf8"));
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
- ensureGlobalTopchesterConfigDir();
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.blocked || actionRequiredHook.stopped) return {
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: (actionRequiredHook.blocked ?? actionRequiredHook.stopped).message,
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 createAppContext({
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-CVWQPq7z.mjs.map
14437
+ //# sourceMappingURL=cli-BgMj4Ifj.mjs.map