topchester-ai 0.18.0 → 0.19.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/cli.mjs +534 -46
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -4452,6 +4452,39 @@ const commandPolicySchema = z.object({
|
|
|
4452
4452
|
allowExact: z.array(commandPatternSchema).optional().default([]),
|
|
4453
4453
|
deny: z.array(commandPatternSchema).optional().default([])
|
|
4454
4454
|
}).strict();
|
|
4455
|
+
const hookEventNames = [
|
|
4456
|
+
"SessionStart",
|
|
4457
|
+
"UserPromptSubmit",
|
|
4458
|
+
"PreToolUse",
|
|
4459
|
+
"PostToolUse",
|
|
4460
|
+
"PreCompact",
|
|
4461
|
+
"Stop"
|
|
4462
|
+
];
|
|
4463
|
+
const hookEventAliasMap = {
|
|
4464
|
+
TaskStart: "SessionStart",
|
|
4465
|
+
TaskComplete: "Stop"
|
|
4466
|
+
};
|
|
4467
|
+
const hookTimeoutMsSchema = z.number().int().positive().max(6e5);
|
|
4468
|
+
const hookMatcherSchema = z.union([z.string().min(1), z.array(z.string().min(1))]).optional();
|
|
4469
|
+
const hookHandlerSchema = z.object({
|
|
4470
|
+
type: z.literal("command").optional(),
|
|
4471
|
+
command: z.string().min(1),
|
|
4472
|
+
timeoutMs: hookTimeoutMsSchema.optional(),
|
|
4473
|
+
matcher: hookMatcherSchema
|
|
4474
|
+
}).strict();
|
|
4475
|
+
const canonicalHooksConfigSchema = z.object({
|
|
4476
|
+
enabled: z.boolean().optional(),
|
|
4477
|
+
SessionStart: z.array(hookHandlerSchema).optional(),
|
|
4478
|
+
UserPromptSubmit: z.array(hookHandlerSchema).optional(),
|
|
4479
|
+
PreToolUse: z.array(hookHandlerSchema).optional(),
|
|
4480
|
+
PostToolUse: z.array(hookHandlerSchema).optional(),
|
|
4481
|
+
PreCompact: z.array(hookHandlerSchema).optional(),
|
|
4482
|
+
Stop: z.array(hookHandlerSchema).optional()
|
|
4483
|
+
}).strict();
|
|
4484
|
+
const rawHooksConfigSchema = canonicalHooksConfigSchema.extend({
|
|
4485
|
+
TaskStart: z.array(hookHandlerSchema).optional(),
|
|
4486
|
+
TaskComplete: z.array(hookHandlerSchema).optional()
|
|
4487
|
+
}).strict();
|
|
4455
4488
|
const topchesterConfigSchema = z.object({
|
|
4456
4489
|
models: z.object({
|
|
4457
4490
|
defaultPurpose: modelPurposeSchema.optional(),
|
|
@@ -4460,12 +4493,14 @@ const topchesterConfigSchema = z.object({
|
|
|
4460
4493
|
providers: providersSchema.optional()
|
|
4461
4494
|
}).strict().optional(),
|
|
4462
4495
|
ignore: z.object({ paths: z.array(ignorePathSchema).optional() }).optional(),
|
|
4463
|
-
tools: z.object({ commands: commandPolicySchema.optional() }).strict().optional()
|
|
4496
|
+
tools: z.object({ commands: commandPolicySchema.optional() }).strict().optional(),
|
|
4497
|
+
hooks: canonicalHooksConfigSchema.optional()
|
|
4464
4498
|
});
|
|
4465
4499
|
const rawTopchesterConfigSchema = z.object({
|
|
4466
4500
|
models: rawModelsSchema.optional(),
|
|
4467
4501
|
ignore: z.object({ paths: z.array(ignorePathSchema).optional() }).optional(),
|
|
4468
|
-
tools: z.object({ commands: commandPolicySchema.optional() }).strict().optional()
|
|
4502
|
+
tools: z.object({ commands: commandPolicySchema.optional() }).strict().optional(),
|
|
4503
|
+
hooks: rawHooksConfigSchema.optional()
|
|
4469
4504
|
});
|
|
4470
4505
|
function getGlobalTopchesterConfigDir() {
|
|
4471
4506
|
return join(homedir(), ".config", "topchester");
|
|
@@ -4644,6 +4679,9 @@ function parseConfigFile(path, value) {
|
|
|
4644
4679
|
return parsed.data;
|
|
4645
4680
|
}
|
|
4646
4681
|
function normalizeConfigInput(value) {
|
|
4682
|
+
return normalizeHooksConfigInput(normalizeModelsConfigInput(value));
|
|
4683
|
+
}
|
|
4684
|
+
function normalizeModelsConfigInput(value) {
|
|
4647
4685
|
if (!isPlainObject(value) || !isPlainObject(value.models)) return value;
|
|
4648
4686
|
const models = { ...value.models };
|
|
4649
4687
|
const providers = isPlainObject(models.providers) ? { ...models.providers } : {};
|
|
@@ -4683,6 +4721,22 @@ function normalizeConfigInput(value) {
|
|
|
4683
4721
|
}
|
|
4684
4722
|
};
|
|
4685
4723
|
}
|
|
4724
|
+
function normalizeHooksConfigInput(value) {
|
|
4725
|
+
if (!isPlainObject(value) || !isPlainObject(value.hooks)) return value;
|
|
4726
|
+
const hooks = { ...value.hooks };
|
|
4727
|
+
for (const [alias, canonical] of Object.entries(hookEventAliasMap)) {
|
|
4728
|
+
const aliasHandlers = hooks[alias];
|
|
4729
|
+
if (aliasHandlers === void 0) continue;
|
|
4730
|
+
const canonicalHandlers = hooks[canonical];
|
|
4731
|
+
if (!Array.isArray(aliasHandlers) || canonicalHandlers !== void 0 && !Array.isArray(canonicalHandlers)) continue;
|
|
4732
|
+
hooks[canonical] = [...canonicalHandlers ?? [], ...aliasHandlers];
|
|
4733
|
+
delete hooks[alias];
|
|
4734
|
+
}
|
|
4735
|
+
return {
|
|
4736
|
+
...value,
|
|
4737
|
+
hooks
|
|
4738
|
+
};
|
|
4739
|
+
}
|
|
4686
4740
|
function normalizeModelRef(ref, defaultProvider) {
|
|
4687
4741
|
if (typeof ref === "string") return parseModelRef(ref, defaultProvider);
|
|
4688
4742
|
if (!isPlainObject(ref) || typeof ref.name !== "string") return;
|
|
@@ -4745,7 +4799,7 @@ function isOpenAIProvider(providerId, baseURL) {
|
|
|
4745
4799
|
function deepMerge(base, override, path = []) {
|
|
4746
4800
|
if (Array.isArray(base) && Array.isArray(override)) {
|
|
4747
4801
|
const joinedPath = path.join(".");
|
|
4748
|
-
return joinedPath === "ignore.paths" || joinedPath === "tools.commands.allow" || joinedPath === "tools.commands.allowExact" || joinedPath === "tools.commands.deny" ? [...base, ...override] : override;
|
|
4802
|
+
return joinedPath === "ignore.paths" || joinedPath === "tools.commands.allow" || joinedPath === "tools.commands.allowExact" || joinedPath === "tools.commands.deny" || path.length === 2 && path[0] === "hooks" && hookEventNames.includes(path[1]) ? [...base, ...override] : override;
|
|
4749
4803
|
}
|
|
4750
4804
|
if (!isPlainObject(base) || !isPlainObject(override)) return override;
|
|
4751
4805
|
const result = { ...base };
|
|
@@ -8629,6 +8683,225 @@ function isAbortError(error) {
|
|
|
8629
8683
|
return error.name === "AbortError" || error.message.toLowerCase().includes("aborted");
|
|
8630
8684
|
}
|
|
8631
8685
|
//#endregion
|
|
8686
|
+
//#region src/agent/hooks.ts
|
|
8687
|
+
const DEFAULT_HOOK_TIMEOUT_MS = 5e3;
|
|
8688
|
+
const MAX_CAPTURED_OUTPUT_CHARS = 64e3;
|
|
8689
|
+
const hookResponseSchema = z.object({
|
|
8690
|
+
action: z.enum([
|
|
8691
|
+
"continue",
|
|
8692
|
+
"block",
|
|
8693
|
+
"stop"
|
|
8694
|
+
]).optional(),
|
|
8695
|
+
decision: z.string().optional(),
|
|
8696
|
+
cancel: z.boolean().optional(),
|
|
8697
|
+
context: z.union([z.string(), z.array(z.string())]).optional(),
|
|
8698
|
+
message: z.string().optional(),
|
|
8699
|
+
feedback: z.string().optional(),
|
|
8700
|
+
reason: z.string().optional()
|
|
8701
|
+
}).passthrough();
|
|
8702
|
+
async function runTopchesterHooks(context, event, payload, options = {}) {
|
|
8703
|
+
const handlers = getConfiguredHookHandlers(context, event, options.toolName);
|
|
8704
|
+
const result = {
|
|
8705
|
+
contexts: [],
|
|
8706
|
+
messages: [],
|
|
8707
|
+
handlerCount: 0
|
|
8708
|
+
};
|
|
8709
|
+
for (const handler of handlers) {
|
|
8710
|
+
result.handlerCount += 1;
|
|
8711
|
+
const handlerResult = await runCommandHandler(context, event, payload, handler, options);
|
|
8712
|
+
result.contexts.push(...handlerResult.contexts);
|
|
8713
|
+
result.messages.push(...handlerResult.messages);
|
|
8714
|
+
if (handlerResult.blocked) {
|
|
8715
|
+
result.blocked = handlerResult.blocked;
|
|
8716
|
+
break;
|
|
8717
|
+
}
|
|
8718
|
+
if (handlerResult.stopped) {
|
|
8719
|
+
result.stopped = handlerResult.stopped;
|
|
8720
|
+
break;
|
|
8721
|
+
}
|
|
8722
|
+
}
|
|
8723
|
+
return result;
|
|
8724
|
+
}
|
|
8725
|
+
function formatHookContextsForPrompt(event, contexts) {
|
|
8726
|
+
const normalized = contexts.map((context) => context.trim()).filter(Boolean);
|
|
8727
|
+
if (normalized.length === 0) return "";
|
|
8728
|
+
return [`Hook context from ${event}:`, ...normalized].join("\n\n");
|
|
8729
|
+
}
|
|
8730
|
+
function getConfiguredHookHandlers(context, event, toolName) {
|
|
8731
|
+
const hooks = context.config.hooks;
|
|
8732
|
+
if (!hooks || hooks.enabled === false) return [];
|
|
8733
|
+
return (hooks[event] ?? []).filter((handler) => hookMatches(handler, event, toolName));
|
|
8734
|
+
}
|
|
8735
|
+
function hookMatches(handler, event, toolName) {
|
|
8736
|
+
const matcher = handler.matcher;
|
|
8737
|
+
if (matcher === void 0) return true;
|
|
8738
|
+
const target = toolName ?? event;
|
|
8739
|
+
return (Array.isArray(matcher) ? matcher : [matcher]).some((entry) => entry === "*" || entry === target);
|
|
8740
|
+
}
|
|
8741
|
+
async function runCommandHandler(context, event, payload, handler, options) {
|
|
8742
|
+
const result = await runHookProcess(handler.command ?? "", payload, {
|
|
8743
|
+
cwd: context.workspaceRoot,
|
|
8744
|
+
timeoutMs: handler.timeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS,
|
|
8745
|
+
abortSignal: options.abortSignal,
|
|
8746
|
+
env: buildHookEnv(event, options.toolName)
|
|
8747
|
+
});
|
|
8748
|
+
logHookProcessResult(context, event, handler, result);
|
|
8749
|
+
if (result.timedOut || result.aborted || result.spawnError || result.exitCode !== 0) return emptyHookRunResult();
|
|
8750
|
+
const stdout = result.stdout.trim();
|
|
8751
|
+
if (!stdout) return emptyHookRunResult();
|
|
8752
|
+
let parsed;
|
|
8753
|
+
try {
|
|
8754
|
+
parsed = JSON.parse(stdout);
|
|
8755
|
+
} catch (error) {
|
|
8756
|
+
logHookWarning(context, {
|
|
8757
|
+
event: "hook_response_parse_failed",
|
|
8758
|
+
hookEventName: event,
|
|
8759
|
+
error: error instanceof Error ? error.message : String(error),
|
|
8760
|
+
stdoutLength: result.stdout.length
|
|
8761
|
+
});
|
|
8762
|
+
return emptyHookRunResult();
|
|
8763
|
+
}
|
|
8764
|
+
const response = hookResponseSchema.safeParse(parsed);
|
|
8765
|
+
if (!response.success) {
|
|
8766
|
+
logHookWarning(context, {
|
|
8767
|
+
event: "hook_response_invalid",
|
|
8768
|
+
hookEventName: event,
|
|
8769
|
+
issues: response.error.issues.map((issue) => issue.message)
|
|
8770
|
+
});
|
|
8771
|
+
return emptyHookRunResult();
|
|
8772
|
+
}
|
|
8773
|
+
return normalizeHookResponse(event, response.data);
|
|
8774
|
+
}
|
|
8775
|
+
function normalizeHookResponse(event, response) {
|
|
8776
|
+
const result = emptyHookRunResult();
|
|
8777
|
+
const action = normalizeHookAction(event, response);
|
|
8778
|
+
const message = firstNonEmpty(response.message, response.feedback, response.reason);
|
|
8779
|
+
const contexts = Array.isArray(response.context) ? response.context : response.context ? [response.context] : [];
|
|
8780
|
+
result.contexts.push(...contexts.filter((context) => context.trim().length > 0));
|
|
8781
|
+
if (message) result.messages.push(message);
|
|
8782
|
+
if (action === "block") result.blocked = { message: message || `Hook ${event} blocked the request.` };
|
|
8783
|
+
else if (action === "stop") result.stopped = { message: message || `Hook ${event} stopped the turn.` };
|
|
8784
|
+
return result;
|
|
8785
|
+
}
|
|
8786
|
+
function normalizeHookAction(event, response) {
|
|
8787
|
+
if (response.cancel) return event === "Stop" ? "stop" : "block";
|
|
8788
|
+
if (response.action) return response.action;
|
|
8789
|
+
const decision = response.decision?.trim().toLowerCase();
|
|
8790
|
+
if (decision === "block" || decision === "deny" || decision === "denied") return "block";
|
|
8791
|
+
if (decision === "stop" || decision === "halt") return "stop";
|
|
8792
|
+
return "continue";
|
|
8793
|
+
}
|
|
8794
|
+
function buildHookEnv(event, toolName) {
|
|
8795
|
+
return {
|
|
8796
|
+
...process.env,
|
|
8797
|
+
TOPCHESTER_HOOK_EVENT: event,
|
|
8798
|
+
TOPCHESTER_HOOK_TOOL: toolName ?? ""
|
|
8799
|
+
};
|
|
8800
|
+
}
|
|
8801
|
+
async function runHookProcess(command, payload, options) {
|
|
8802
|
+
const startedAt = Date.now();
|
|
8803
|
+
const shell = process.env.SHELL || "/bin/sh";
|
|
8804
|
+
return new Promise((resolve) => {
|
|
8805
|
+
let stdout = "";
|
|
8806
|
+
let stderr = "";
|
|
8807
|
+
let settled = false;
|
|
8808
|
+
let timedOut = false;
|
|
8809
|
+
let aborted = false;
|
|
8810
|
+
const child = spawn(shell, ["-lc", command], {
|
|
8811
|
+
cwd: options.cwd,
|
|
8812
|
+
env: options.env,
|
|
8813
|
+
stdio: [
|
|
8814
|
+
"pipe",
|
|
8815
|
+
"pipe",
|
|
8816
|
+
"pipe"
|
|
8817
|
+
]
|
|
8818
|
+
});
|
|
8819
|
+
const finish = (partial) => {
|
|
8820
|
+
if (settled) return;
|
|
8821
|
+
settled = true;
|
|
8822
|
+
clearTimeout(timeout);
|
|
8823
|
+
options.abortSignal?.removeEventListener("abort", abort);
|
|
8824
|
+
resolve({
|
|
8825
|
+
stdout,
|
|
8826
|
+
stderr,
|
|
8827
|
+
exitCode: null,
|
|
8828
|
+
signal: null,
|
|
8829
|
+
timedOut,
|
|
8830
|
+
aborted,
|
|
8831
|
+
durationMs: Date.now() - startedAt,
|
|
8832
|
+
...partial
|
|
8833
|
+
});
|
|
8834
|
+
};
|
|
8835
|
+
const timeout = setTimeout(() => {
|
|
8836
|
+
timedOut = true;
|
|
8837
|
+
child.kill("SIGTERM");
|
|
8838
|
+
}, options.timeoutMs);
|
|
8839
|
+
const abort = () => {
|
|
8840
|
+
aborted = true;
|
|
8841
|
+
child.kill("SIGTERM");
|
|
8842
|
+
};
|
|
8843
|
+
if (options.abortSignal?.aborted) abort();
|
|
8844
|
+
else options.abortSignal?.addEventListener("abort", abort, { once: true });
|
|
8845
|
+
child.stdout?.setEncoding("utf8");
|
|
8846
|
+
child.stderr?.setEncoding("utf8");
|
|
8847
|
+
child.stdout?.on("data", (chunk) => {
|
|
8848
|
+
stdout = appendCapped(stdout, chunk);
|
|
8849
|
+
});
|
|
8850
|
+
child.stderr?.on("data", (chunk) => {
|
|
8851
|
+
stderr = appendCapped(stderr, chunk);
|
|
8852
|
+
});
|
|
8853
|
+
child.on("error", (error) => {
|
|
8854
|
+
finish({ spawnError: error.message });
|
|
8855
|
+
});
|
|
8856
|
+
child.on("close", (exitCode, signal) => {
|
|
8857
|
+
finish({
|
|
8858
|
+
exitCode,
|
|
8859
|
+
signal
|
|
8860
|
+
});
|
|
8861
|
+
});
|
|
8862
|
+
child.stdin?.on("error", () => {});
|
|
8863
|
+
child.stdin?.end(`${JSON.stringify(payload)}\n`);
|
|
8864
|
+
});
|
|
8865
|
+
}
|
|
8866
|
+
function logHookProcessResult(context, event, handler, result) {
|
|
8867
|
+
context.logger.debug({
|
|
8868
|
+
event: "hook_run",
|
|
8869
|
+
hookEventName: event,
|
|
8870
|
+
handlerType: handler.type ?? "command",
|
|
8871
|
+
matcher: handler.matcher,
|
|
8872
|
+
exitCode: result.exitCode,
|
|
8873
|
+
signal: result.signal,
|
|
8874
|
+
timedOut: result.timedOut,
|
|
8875
|
+
aborted: result.aborted,
|
|
8876
|
+
spawnError: result.spawnError,
|
|
8877
|
+
durationMs: result.durationMs,
|
|
8878
|
+
stdoutLength: result.stdout.length,
|
|
8879
|
+
stderrLength: result.stderr.length
|
|
8880
|
+
}, "hook run");
|
|
8881
|
+
}
|
|
8882
|
+
function logHookWarning(context, payload) {
|
|
8883
|
+
const logger = context.logger;
|
|
8884
|
+
if (typeof logger.warn === "function") {
|
|
8885
|
+
logger.warn(payload, "hook warning");
|
|
8886
|
+
return;
|
|
8887
|
+
}
|
|
8888
|
+
context.logger.debug(payload, "hook warning");
|
|
8889
|
+
}
|
|
8890
|
+
function emptyHookRunResult() {
|
|
8891
|
+
return {
|
|
8892
|
+
contexts: [],
|
|
8893
|
+
messages: [],
|
|
8894
|
+
handlerCount: 0
|
|
8895
|
+
};
|
|
8896
|
+
}
|
|
8897
|
+
function appendCapped(current, chunk) {
|
|
8898
|
+
if (current.length >= MAX_CAPTURED_OUTPUT_CHARS) return current;
|
|
8899
|
+
return `${current}${chunk}`.slice(0, MAX_CAPTURED_OUTPUT_CHARS);
|
|
8900
|
+
}
|
|
8901
|
+
function firstNonEmpty(...values) {
|
|
8902
|
+
return values.find((value) => value !== void 0 && value.trim().length > 0);
|
|
8903
|
+
}
|
|
8904
|
+
//#endregion
|
|
8632
8905
|
//#region src/agent/profiles.ts
|
|
8633
8906
|
const READ_ONLY_TOOLS = [
|
|
8634
8907
|
"read_file",
|
|
@@ -9578,6 +9851,7 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9578
9851
|
options;
|
|
9579
9852
|
taskPlan = createTaskPlanController();
|
|
9580
9853
|
approvedRunCommands = /* @__PURE__ */ new Set();
|
|
9854
|
+
startedHookSessionKeys = /* @__PURE__ */ new Set();
|
|
9581
9855
|
/**
|
|
9582
9856
|
* Holds the shared application context for one runtime instance.
|
|
9583
9857
|
* The runtime does not own those dependencies; it coordinates the
|
|
@@ -9610,6 +9884,20 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9610
9884
|
async checkKnowledgeBase() {
|
|
9611
9885
|
return getKnowledgeStatusEvents(await this.getKnowledgeStatusWithNonCleanFileCount());
|
|
9612
9886
|
}
|
|
9887
|
+
async runSessionStartHooks(session, options = {}) {
|
|
9888
|
+
const sessionKey = session?.sessionId ?? `workspace:${this.context.workspaceRoot}`;
|
|
9889
|
+
if (this.startedHookSessionKeys.has(sessionKey)) return [];
|
|
9890
|
+
this.startedHookSessionKeys.add(sessionKey);
|
|
9891
|
+
const result = await this.runHookEvent("SessionStart", this.createBaseHookPayload("SessionStart", session, {
|
|
9892
|
+
isResumed: Boolean(options.isResumed),
|
|
9893
|
+
taskStartAlias: "TaskStart"
|
|
9894
|
+
}), { abortSignal: options.abortSignal });
|
|
9895
|
+
return this.hookResultToEvents(result);
|
|
9896
|
+
}
|
|
9897
|
+
async runPreCompactHooks(session, options = {}) {
|
|
9898
|
+
const result = await this.runHookEvent("PreCompact", this.createBaseHookPayload("PreCompact", session, { reason: options.reason ?? "Compaction is about to start." }), { abortSignal: options.abortSignal });
|
|
9899
|
+
return this.hookResultToEvents(result);
|
|
9900
|
+
}
|
|
9613
9901
|
/**
|
|
9614
9902
|
* Streams one user chat turn through the agent loop. It builds the model
|
|
9615
9903
|
* prompt with relevant KB context, calls the model, executes any requested
|
|
@@ -9622,13 +9910,26 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9622
9910
|
* ordered events.
|
|
9623
9911
|
*/
|
|
9624
9912
|
async *submitMessageStream(conversation, message, abortSignal, options = {}) {
|
|
9625
|
-
|
|
9913
|
+
const session = options.session ?? this.options.session;
|
|
9914
|
+
for (const event of await this.runSessionStartHooks(session, { abortSignal })) yield event;
|
|
9915
|
+
const userPromptHook = await this.runHookEvent("UserPromptSubmit", this.createBaseHookPayload("UserPromptSubmit", session, {
|
|
9916
|
+
prompt: { text: message },
|
|
9917
|
+
prompt_text: message,
|
|
9918
|
+
user_prompt: message
|
|
9919
|
+
}), { abortSignal });
|
|
9920
|
+
for (const event of this.hookResultToEvents(userPromptHook)) yield event;
|
|
9921
|
+
if (userPromptHook.blocked || userPromptHook.stopped) {
|
|
9922
|
+
const interruption = userPromptHook.blocked ?? userPromptHook.stopped;
|
|
9923
|
+
if (userPromptHook.messages.length === 0) yield agentEvent.systemMessage(interruption.message);
|
|
9924
|
+
yield agentEvent.status("ready");
|
|
9925
|
+
return;
|
|
9926
|
+
}
|
|
9927
|
+
let nextPrompt = this.appendHookContextsToPrompt(await this.buildPromptWithKnowledgeContext(buildConversationPrompt(conversation, message), message), "UserPromptSubmit", userPromptHook.contexts);
|
|
9626
9928
|
let totalDurationMs = 0;
|
|
9627
9929
|
const tokenUsageTotals = {};
|
|
9628
9930
|
const profile = this.options.profile ?? PRIMARY_AGENT_PROFILE;
|
|
9629
9931
|
const permissions = createToolPermissionView(profile, { deniedTools: this.options.parentPermissions?.deniedTools });
|
|
9630
9932
|
const tools = getProfileToolDefinitions(permissions);
|
|
9631
|
-
const session = options.session ?? this.options.session;
|
|
9632
9933
|
const subagents = new SubagentManager({
|
|
9633
9934
|
context: this.context,
|
|
9634
9935
|
parentSession: session,
|
|
@@ -9733,7 +10034,9 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9733
10034
|
}
|
|
9734
10035
|
yield agentEvent.taskPlan(this.taskPlan.update({ items: [] }));
|
|
9735
10036
|
}
|
|
9736
|
-
|
|
10037
|
+
const finalMessage = finalText.trim() || "I got an empty response from the model.";
|
|
10038
|
+
yield agentEvent.assistantMessage(finalMessage, formatAgentMessageMeta(result.modelId, totalDurationMs, tokenUsageTotals));
|
|
10039
|
+
for (const event of await this.runStopHookEvents(session, finalMessage, "completed", abortSignal)) yield event;
|
|
9737
10040
|
yield agentEvent.status("ready");
|
|
9738
10041
|
return;
|
|
9739
10042
|
}
|
|
@@ -9750,10 +10053,33 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9750
10053
|
if (modelToolCalls.length > 1 && modelToolCalls.every((call) => call.tool === "task")) {
|
|
9751
10054
|
const taskCalls = modelToolCalls.map((call) => call);
|
|
9752
10055
|
const taskResults = [];
|
|
10056
|
+
const postHookContexts = [];
|
|
9753
10057
|
for (let index = 0; index < taskCalls.length; index += DEFAULT_TASK_CONCURRENCY) {
|
|
9754
10058
|
const batch = taskCalls.slice(index, index + DEFAULT_TASK_CONCURRENCY);
|
|
10059
|
+
const executableBatch = [];
|
|
10060
|
+
for (let batchIndex = 0; batchIndex < batch.length; batchIndex += 1) {
|
|
10061
|
+
const call = batch[batchIndex];
|
|
10062
|
+
const resultIndex = index + batchIndex;
|
|
10063
|
+
const preHook = await this.runPreToolUseHook(call, modelToolCalls[resultIndex]?.id, session, abortSignal);
|
|
10064
|
+
for (const event of this.hookResultToEvents(preHook)) yield event;
|
|
10065
|
+
if (preHook.stopped) {
|
|
10066
|
+
if (preHook.messages.length === 0) yield agentEvent.systemMessage(preHook.stopped.message);
|
|
10067
|
+
yield agentEvent.status("ready");
|
|
10068
|
+
return;
|
|
10069
|
+
}
|
|
10070
|
+
if (preHook.blocked) {
|
|
10071
|
+
taskResults[resultIndex] = createToolErrorResult(call.tool, preHook.blocked.message);
|
|
10072
|
+
continue;
|
|
10073
|
+
}
|
|
10074
|
+
executableBatch.push({
|
|
10075
|
+
call,
|
|
10076
|
+
resultIndex,
|
|
10077
|
+
toolCallId: modelToolCalls[resultIndex]?.id
|
|
10078
|
+
});
|
|
10079
|
+
}
|
|
10080
|
+
if (executableBatch.length === 0) continue;
|
|
9755
10081
|
const taskEventQueue = createRuntimeEventQueue();
|
|
9756
|
-
const batchResultPromise = Promise.all(
|
|
10082
|
+
const batchResultPromise = Promise.all(executableBatch.map((entry) => executeToolCall(this.context.workspaceRoot, entry.call, {
|
|
9757
10083
|
logger: this.context.logger,
|
|
9758
10084
|
config: this.context.config,
|
|
9759
10085
|
taskPlan: this.taskPlan,
|
|
@@ -9761,22 +10087,60 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9761
10087
|
permissions,
|
|
9762
10088
|
subagents,
|
|
9763
10089
|
abortSignal,
|
|
9764
|
-
toolCallId:
|
|
10090
|
+
toolCallId: entry.toolCallId,
|
|
9765
10091
|
eventSink: (event) => taskEventQueue.push(event)
|
|
9766
10092
|
}))).finally(() => {
|
|
9767
10093
|
taskEventQueue.close();
|
|
9768
10094
|
});
|
|
9769
10095
|
for await (const event of taskEventQueue) yield event;
|
|
9770
|
-
|
|
10096
|
+
const batchResults = await batchResultPromise;
|
|
10097
|
+
for (let batchIndex = 0; batchIndex < executableBatch.length; batchIndex += 1) {
|
|
10098
|
+
const entry = executableBatch[batchIndex];
|
|
10099
|
+
taskResults[entry.resultIndex] = batchResults[batchIndex];
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
10102
|
+
for (let index = 0; index < taskCalls.length; index += 1) {
|
|
10103
|
+
const call = taskCalls[index];
|
|
10104
|
+
const toolResult = taskResults[index];
|
|
10105
|
+
yield agentEvent.toolCall(call, formatToolCallMessage(call, toolResult));
|
|
10106
|
+
const postHook = await this.runPostToolUseHook(call, modelToolCalls[index]?.id, toolResult, session, abortSignal);
|
|
10107
|
+
for (const event of this.hookResultToEvents(postHook)) yield event;
|
|
10108
|
+
postHookContexts.push(...postHook.contexts);
|
|
10109
|
+
if (postHook.stopped) {
|
|
10110
|
+
if (postHook.messages.length === 0) yield agentEvent.systemMessage(postHook.stopped.message);
|
|
10111
|
+
yield agentEvent.status("ready");
|
|
10112
|
+
return;
|
|
10113
|
+
}
|
|
9771
10114
|
}
|
|
9772
|
-
for (let index = 0; index < taskCalls.length; index += 1) yield agentEvent.toolCall(taskCalls[index], formatToolCallMessage(taskCalls[index], taskResults[index]));
|
|
9773
10115
|
afterTool = "task";
|
|
9774
|
-
nextPrompt = `${nextPrompt}\n\n${taskResults.map((toolResult) => formatToolResultForPrompt(toolResult)).join("\n\n")}\n\n${formatContinuationInstruction(result.toolProtocol, taskResults.at(-1), isToolAllowed(permissions, "plan_todo"))}
|
|
10116
|
+
nextPrompt = this.appendHookContextsToPrompt(`${nextPrompt}\n\n${taskResults.map((toolResult) => formatToolResultForPrompt(toolResult)).join("\n\n")}\n\n${formatContinuationInstruction(result.toolProtocol, taskResults.at(-1), isToolAllowed(permissions, "plan_todo"))}`, "PostToolUse", postHookContexts);
|
|
9775
10117
|
continue;
|
|
9776
10118
|
}
|
|
9777
10119
|
if (modelToolCalls.length > 1 && modelToolCalls.every((call) => isParallelSafeToolName(call.tool))) {
|
|
9778
10120
|
const parallelCalls = modelToolCalls.map((call) => call);
|
|
9779
|
-
const parallelResults =
|
|
10121
|
+
const parallelResults = [];
|
|
10122
|
+
const executableCalls = [];
|
|
10123
|
+
const postHookContexts = [];
|
|
10124
|
+
for (let index = 0; index < parallelCalls.length; index += 1) {
|
|
10125
|
+
const call = parallelCalls[index];
|
|
10126
|
+
const preHook = await this.runPreToolUseHook(call, modelToolCalls[index]?.id, session, abortSignal);
|
|
10127
|
+
for (const event of this.hookResultToEvents(preHook)) yield event;
|
|
10128
|
+
if (preHook.stopped) {
|
|
10129
|
+
if (preHook.messages.length === 0) yield agentEvent.systemMessage(preHook.stopped.message);
|
|
10130
|
+
yield agentEvent.status("ready");
|
|
10131
|
+
return;
|
|
10132
|
+
}
|
|
10133
|
+
if (preHook.blocked) {
|
|
10134
|
+
parallelResults[index] = createToolErrorResult(call.tool, preHook.blocked.message);
|
|
10135
|
+
continue;
|
|
10136
|
+
}
|
|
10137
|
+
executableCalls.push({
|
|
10138
|
+
call,
|
|
10139
|
+
resultIndex: index,
|
|
10140
|
+
toolCallId: modelToolCalls[index]?.id
|
|
10141
|
+
});
|
|
10142
|
+
}
|
|
10143
|
+
const executedResults = await Promise.all(executableCalls.map((entry) => executeToolCall(this.context.workspaceRoot, entry.call, {
|
|
9780
10144
|
logger: this.context.logger,
|
|
9781
10145
|
config: this.context.config,
|
|
9782
10146
|
taskPlan: this.taskPlan,
|
|
@@ -9784,48 +10148,85 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9784
10148
|
permissions,
|
|
9785
10149
|
subagents,
|
|
9786
10150
|
abortSignal,
|
|
9787
|
-
toolCallId:
|
|
10151
|
+
toolCallId: entry.toolCallId
|
|
9788
10152
|
})));
|
|
9789
|
-
for (let index = 0; index <
|
|
10153
|
+
for (let index = 0; index < executableCalls.length; index += 1) {
|
|
10154
|
+
const entry = executableCalls[index];
|
|
10155
|
+
parallelResults[entry.resultIndex] = executedResults[index];
|
|
10156
|
+
}
|
|
10157
|
+
for (let index = 0; index < parallelCalls.length; index += 1) {
|
|
10158
|
+
const call = parallelCalls[index];
|
|
10159
|
+
const toolResult = parallelResults[index];
|
|
10160
|
+
yield agentEvent.toolCall(call, formatToolCallMessage(call, toolResult));
|
|
10161
|
+
const postHook = await this.runPostToolUseHook(call, modelToolCalls[index]?.id, toolResult, session, abortSignal);
|
|
10162
|
+
for (const event of this.hookResultToEvents(postHook)) yield event;
|
|
10163
|
+
postHookContexts.push(...postHook.contexts);
|
|
10164
|
+
if (postHook.stopped) {
|
|
10165
|
+
if (postHook.messages.length === 0) yield agentEvent.systemMessage(postHook.stopped.message);
|
|
10166
|
+
yield agentEvent.status("ready");
|
|
10167
|
+
return;
|
|
10168
|
+
}
|
|
10169
|
+
}
|
|
9790
10170
|
afterTool = parallelCalls.at(-1)?.tool;
|
|
9791
|
-
nextPrompt = `${nextPrompt}\n\n${parallelResults.map((toolResult) => formatToolResultForPrompt(toolResult)).join("\n\n")}\n\n${formatContinuationInstruction(result.toolProtocol, parallelResults.at(-1), isToolAllowed(permissions, "plan_todo"))}
|
|
10171
|
+
nextPrompt = this.appendHookContextsToPrompt(`${nextPrompt}\n\n${parallelResults.map((toolResult) => formatToolResultForPrompt(toolResult)).join("\n\n")}\n\n${formatContinuationInstruction(result.toolProtocol, parallelResults.at(-1), isToolAllowed(permissions, "plan_todo"))}`, "PostToolUse", postHookContexts);
|
|
9792
10172
|
continue;
|
|
9793
10173
|
}
|
|
9794
10174
|
const executableToolCall = toolCall;
|
|
9795
10175
|
const suppressiblePlanTodoAnswer = getSuppressiblePlanTodoAnswer(executableToolCall, result.text, this.taskPlan.get());
|
|
9796
10176
|
if (suppressiblePlanTodoAnswer !== void 0) {
|
|
9797
|
-
|
|
10177
|
+
const finalMessage = suppressiblePlanTodoAnswer || "I got an empty response from the model.";
|
|
10178
|
+
yield agentEvent.assistantMessage(finalMessage, formatAgentMessageMeta(result.modelId, totalDurationMs, tokenUsageTotals));
|
|
10179
|
+
for (const event of await this.runStopHookEvents(session, finalMessage, "completed", abortSignal)) yield event;
|
|
9798
10180
|
yield agentEvent.status("ready");
|
|
9799
10181
|
return;
|
|
9800
10182
|
}
|
|
9801
|
-
const
|
|
10183
|
+
const preHook = await this.runPreToolUseHook(executableToolCall, toolCall.id, session, abortSignal);
|
|
10184
|
+
for (const event of this.hookResultToEvents(preHook)) yield event;
|
|
9802
10185
|
let toolResult;
|
|
9803
|
-
if (
|
|
10186
|
+
if (preHook.stopped) {
|
|
10187
|
+
if (preHook.messages.length === 0) yield agentEvent.systemMessage(preHook.stopped.message);
|
|
10188
|
+
yield agentEvent.status("ready");
|
|
10189
|
+
return;
|
|
10190
|
+
}
|
|
10191
|
+
if (preHook.blocked) toolResult = createToolErrorResult(executableToolCall.tool, preHook.blocked.message);
|
|
9804
10192
|
else {
|
|
9805
|
-
const
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
10193
|
+
const approval = await this.resolveRunCommandApproval(executableToolCall, options);
|
|
10194
|
+
if (approval.cancelled) toolResult = createToolErrorResult(executableToolCall.tool, approval.reason);
|
|
10195
|
+
else {
|
|
10196
|
+
const toolEventQueue = createRuntimeEventQueue();
|
|
10197
|
+
const toolResultPromise = executeToolCall(this.context.workspaceRoot, executableToolCall, {
|
|
10198
|
+
logger: this.context.logger,
|
|
10199
|
+
config: this.context.config,
|
|
10200
|
+
runCommandApprovals: { allowExactCommands: approval.approvedCommands },
|
|
10201
|
+
taskPlan: this.taskPlan,
|
|
10202
|
+
profile,
|
|
10203
|
+
permissions,
|
|
10204
|
+
subagents,
|
|
10205
|
+
abortSignal,
|
|
10206
|
+
toolCallId: toolCall.id,
|
|
10207
|
+
eventSink: (event) => toolEventQueue.push(event)
|
|
10208
|
+
}).finally(() => {
|
|
10209
|
+
toolEventQueue.close();
|
|
10210
|
+
});
|
|
10211
|
+
for await (const event of toolEventQueue) yield event;
|
|
10212
|
+
toolResult = await toolResultPromise;
|
|
10213
|
+
}
|
|
9822
10214
|
}
|
|
9823
10215
|
yield agentEvent.toolCall(executableToolCall, formatToolCallMessage(executableToolCall, toolResult));
|
|
9824
10216
|
if (!isToolErrorResult(toolResult) && toolResult.tool === "plan_todo") yield agentEvent.taskPlan(toolResult.plan);
|
|
10217
|
+
const postHook = await this.runPostToolUseHook(executableToolCall, toolCall.id, toolResult, session, abortSignal);
|
|
10218
|
+
for (const event of this.hookResultToEvents(postHook)) yield event;
|
|
10219
|
+
if (postHook.stopped) {
|
|
10220
|
+
if (postHook.messages.length === 0) yield agentEvent.systemMessage(postHook.stopped.message);
|
|
10221
|
+
yield agentEvent.status("ready");
|
|
10222
|
+
return;
|
|
10223
|
+
}
|
|
9825
10224
|
afterTool = executableToolCall.tool;
|
|
9826
|
-
nextPrompt = `${nextPrompt}\n\n${formatToolResultForPrompt(toolResult)}\n\n${formatContinuationInstruction(result.toolProtocol, toolResult, isToolAllowed(permissions, "plan_todo"))}
|
|
10225
|
+
nextPrompt = this.appendHookContextsToPrompt(`${nextPrompt}\n\n${formatToolResultForPrompt(toolResult)}\n\n${formatContinuationInstruction(result.toolProtocol, toolResult, isToolAllowed(permissions, "plan_todo"))}`, "PostToolUse", postHook.contexts);
|
|
9827
10226
|
}
|
|
9828
|
-
|
|
10227
|
+
const finalMessage = "I stopped because the tool loop ended unexpectedly.";
|
|
10228
|
+
yield agentEvent.assistantMessage(finalMessage, formatAgentMessageMeta(lastModelId, totalDurationMs, tokenUsageTotals));
|
|
10229
|
+
for (const event of await this.runStopHookEvents(session, finalMessage, "failed", abortSignal)) yield event;
|
|
9829
10230
|
yield agentEvent.status("ready");
|
|
9830
10231
|
}
|
|
9831
10232
|
/**
|
|
@@ -9840,6 +10241,68 @@ var TopchesterAgentRuntime = class TopchesterAgentRuntime {
|
|
|
9840
10241
|
}
|
|
9841
10242
|
return events;
|
|
9842
10243
|
}
|
|
10244
|
+
async runPreToolUseHook(call, toolCallId, session, abortSignal) {
|
|
10245
|
+
return this.runHookEvent("PreToolUse", this.createToolHookPayload("PreToolUse", call, toolCallId, session), {
|
|
10246
|
+
toolName: call.tool,
|
|
10247
|
+
abortSignal
|
|
10248
|
+
});
|
|
10249
|
+
}
|
|
10250
|
+
async runPostToolUseHook(call, toolCallId, result, session, abortSignal) {
|
|
10251
|
+
return this.runHookEvent("PostToolUse", this.createToolHookPayload("PostToolUse", call, toolCallId, session, { result }), {
|
|
10252
|
+
toolName: call.tool,
|
|
10253
|
+
abortSignal
|
|
10254
|
+
});
|
|
10255
|
+
}
|
|
10256
|
+
async runStopHookEvents(session, finalMessage, status, abortSignal) {
|
|
10257
|
+
const result = await this.runHookEvent("Stop", this.createBaseHookPayload("Stop", session, {
|
|
10258
|
+
taskCompleteAlias: "TaskComplete",
|
|
10259
|
+
finalMessage,
|
|
10260
|
+
status
|
|
10261
|
+
}), { abortSignal });
|
|
10262
|
+
return this.hookResultToEvents(result);
|
|
10263
|
+
}
|
|
10264
|
+
async runHookEvent(event, payload, options = {}) {
|
|
10265
|
+
return runTopchesterHooks(this.context, event, payload, options);
|
|
10266
|
+
}
|
|
10267
|
+
createBaseHookPayload(event, session, extra = {}) {
|
|
10268
|
+
return {
|
|
10269
|
+
hook_event_name: event,
|
|
10270
|
+
event,
|
|
10271
|
+
cwd: this.context.workspaceRoot,
|
|
10272
|
+
workspaceRoot: this.context.workspaceRoot,
|
|
10273
|
+
source: "topchester",
|
|
10274
|
+
...session ? {
|
|
10275
|
+
session_id: session.sessionId,
|
|
10276
|
+
sessionId: session.sessionId,
|
|
10277
|
+
session: {
|
|
10278
|
+
sessionId: session.sessionId,
|
|
10279
|
+
rootSessionId: session.metadata.rootSessionId,
|
|
10280
|
+
parentSessionId: session.metadata.parentSessionId,
|
|
10281
|
+
source: session.metadata.source
|
|
10282
|
+
}
|
|
10283
|
+
} : {},
|
|
10284
|
+
...extra
|
|
10285
|
+
};
|
|
10286
|
+
}
|
|
10287
|
+
createToolHookPayload(event, call, toolCallId, session, extra = {}) {
|
|
10288
|
+
return this.createBaseHookPayload(event, session, {
|
|
10289
|
+
tool_name: call.tool,
|
|
10290
|
+
tool_input: call.args,
|
|
10291
|
+
tool: {
|
|
10292
|
+
name: call.tool,
|
|
10293
|
+
input: call.args,
|
|
10294
|
+
...toolCallId ? { callId: toolCallId } : {}
|
|
10295
|
+
},
|
|
10296
|
+
...extra
|
|
10297
|
+
});
|
|
10298
|
+
}
|
|
10299
|
+
hookResultToEvents(result) {
|
|
10300
|
+
return result.messages.map((message) => agentEvent.systemMessage(message));
|
|
10301
|
+
}
|
|
10302
|
+
appendHookContextsToPrompt(prompt, event, contexts) {
|
|
10303
|
+
const hookContext = formatHookContextsForPrompt(event, contexts);
|
|
10304
|
+
return hookContext ? `${prompt}\n\n${hookContext}` : prompt;
|
|
10305
|
+
}
|
|
9843
10306
|
async resolveRunCommandApproval(call, options) {
|
|
9844
10307
|
const approvedCommands = [...this.approvedRunCommands];
|
|
9845
10308
|
if (call.tool !== "run_command") return {
|
|
@@ -10368,6 +10831,7 @@ var TopchesterTuiShell = class {
|
|
|
10368
10831
|
const isResumed = this.options.session !== void 0;
|
|
10369
10832
|
const messages = this.options.initialMessages ?? getStartupThreadMessages(this.context);
|
|
10370
10833
|
if (!isResumed) await persistMessagesWithWarning(session, messages, messages);
|
|
10834
|
+
await this.appendStartupRuntimeEvents(session, messages, await this.runtime.runSessionStartHooks?.(session, { isResumed }) ?? []);
|
|
10371
10835
|
const folderName = getFolderName(this.context.workspaceRoot);
|
|
10372
10836
|
const modelLabel = getModelLabel(this.context);
|
|
10373
10837
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
@@ -10837,10 +11301,24 @@ var TopchesterTuiShell = class {
|
|
|
10837
11301
|
this.session = session;
|
|
10838
11302
|
this.sessionStartedAt = Date.now();
|
|
10839
11303
|
await persistMessagesWithWarning(session, messages, messages);
|
|
11304
|
+
await this.appendStartupRuntimeEvents(session, messages, await this.runtime.runSessionStartHooks?.(session, { isResumed: false }) ?? []);
|
|
10840
11305
|
app.resetForNewSession(messages);
|
|
10841
11306
|
tui.requestRender();
|
|
10842
11307
|
await this.checkAgent(app, tui);
|
|
10843
11308
|
}
|
|
11309
|
+
async appendStartupRuntimeEvents(session, messages, events) {
|
|
11310
|
+
for (const event of events) {
|
|
11311
|
+
messages.push(...renderRuntimeEvent(event));
|
|
11312
|
+
const payload = runtimeEventToSessionPayload(event);
|
|
11313
|
+
if (!payload) continue;
|
|
11314
|
+
try {
|
|
11315
|
+
await session.append(payload);
|
|
11316
|
+
} catch (error) {
|
|
11317
|
+
messages.push(systemMessage(`Session save failed: ${formatPlainError(error)}`));
|
|
11318
|
+
return;
|
|
11319
|
+
}
|
|
11320
|
+
}
|
|
11321
|
+
}
|
|
10844
11322
|
async applyRuntimeEvents(app, events, renderRequester) {
|
|
10845
11323
|
for (const event of events) {
|
|
10846
11324
|
if (event.type === "status") app.setStatus(event.status);
|
|
@@ -10929,6 +11407,16 @@ async function executeRunCommand(context, options) {
|
|
|
10929
11407
|
});
|
|
10930
11408
|
try {
|
|
10931
11409
|
if (!options.resume) await persistStartupMessages(session, runContext);
|
|
11410
|
+
await applyRuntimeEvents({
|
|
11411
|
+
events: await runtime.runSessionStartHooks(session, {
|
|
11412
|
+
isResumed: Boolean(options.resume),
|
|
11413
|
+
abortSignal: abortController.signal
|
|
11414
|
+
}),
|
|
11415
|
+
session,
|
|
11416
|
+
jsonEvents,
|
|
11417
|
+
runId,
|
|
11418
|
+
plain: !options.json
|
|
11419
|
+
});
|
|
10932
11420
|
await applyRuntimeEvents({
|
|
10933
11421
|
events: await runtime.checkKnowledgeBase(),
|
|
10934
11422
|
session,
|
|
@@ -11169,15 +11657,6 @@ program.command("dev").description("start local development mode").action(() =>
|
|
|
11169
11657
|
console.log("Topchester local dev mode");
|
|
11170
11658
|
printStartupSummary(context);
|
|
11171
11659
|
});
|
|
11172
|
-
program.command("update").alias("upgrade").description("update Topchester with the package manager that installed it").argument("[target]", "version or npm dist tag to install", "latest").action(async (target) => {
|
|
11173
|
-
try {
|
|
11174
|
-
const command = await runSelfUpdate({ target });
|
|
11175
|
-
console.log(formatSelfUpdateSuccess(command).join("\n"));
|
|
11176
|
-
} catch (error) {
|
|
11177
|
-
console.error(formatStartupError(error));
|
|
11178
|
-
process.exitCode = 1;
|
|
11179
|
-
}
|
|
11180
|
-
});
|
|
11181
11660
|
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) => {
|
|
11182
11661
|
const context = createContextFromOptions();
|
|
11183
11662
|
const globalOptions = program.opts();
|
|
@@ -11237,6 +11716,15 @@ kbCommand.command("status").description("show project files that are not current
|
|
|
11237
11716
|
const result = await ui.spinner("Checking KB file status...", async () => filterNonCleanKnowledgeCompileResult(await dryRunKnowledgeCompile(context.workspaceRoot, { config: context.config })));
|
|
11238
11717
|
console.log(formatKnowledgeCompileStatusResult(result, { formatSyncStatus: formatDryRunSyncStatus }).join("\n"));
|
|
11239
11718
|
});
|
|
11719
|
+
program.command("update").alias("upgrade").description("update Topchester with the package manager that installed it").argument("[target]", "version or npm dist tag to install", "latest").action(async (target) => {
|
|
11720
|
+
try {
|
|
11721
|
+
const command = await runSelfUpdate({ target });
|
|
11722
|
+
console.log(formatSelfUpdateSuccess(command).join("\n"));
|
|
11723
|
+
} catch (error) {
|
|
11724
|
+
console.error(formatStartupError(error));
|
|
11725
|
+
process.exitCode = 1;
|
|
11726
|
+
}
|
|
11727
|
+
});
|
|
11240
11728
|
await program.parseAsync();
|
|
11241
11729
|
function printStartupSummary(context) {
|
|
11242
11730
|
const assignments = context.config.models?.assignments ?? {};
|