@kimbho/kimbho-cli 0.1.1 → 0.1.3
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 +9 -0
- package/dist/index.cjs +1204 -221
- package/dist/index.cjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1147,9 +1147,9 @@ var require_command = __commonJS({
|
|
|
1147
1147
|
"../../node_modules/commander/lib/command.js"(exports2) {
|
|
1148
1148
|
var EventEmitter = require("node:events").EventEmitter;
|
|
1149
1149
|
var childProcess = require("node:child_process");
|
|
1150
|
-
var
|
|
1150
|
+
var path6 = require("node:path");
|
|
1151
1151
|
var fs = require("node:fs");
|
|
1152
|
-
var
|
|
1152
|
+
var process12 = require("node:process");
|
|
1153
1153
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
1154
1154
|
var { CommanderError: CommanderError2 } = require_error();
|
|
1155
1155
|
var { Help: Help2, stripColor } = require_help();
|
|
@@ -1196,13 +1196,13 @@ var require_command = __commonJS({
|
|
|
1196
1196
|
this._showSuggestionAfterError = true;
|
|
1197
1197
|
this._savedState = null;
|
|
1198
1198
|
this._outputConfiguration = {
|
|
1199
|
-
writeOut: (str) =>
|
|
1200
|
-
writeErr: (str) =>
|
|
1199
|
+
writeOut: (str) => process12.stdout.write(str),
|
|
1200
|
+
writeErr: (str) => process12.stderr.write(str),
|
|
1201
1201
|
outputError: (str, write) => write(str),
|
|
1202
|
-
getOutHelpWidth: () =>
|
|
1203
|
-
getErrHelpWidth: () =>
|
|
1204
|
-
getOutHasColors: () => useColor() ?? (
|
|
1205
|
-
getErrHasColors: () => useColor() ?? (
|
|
1202
|
+
getOutHelpWidth: () => process12.stdout.isTTY ? process12.stdout.columns : void 0,
|
|
1203
|
+
getErrHelpWidth: () => process12.stderr.isTTY ? process12.stderr.columns : void 0,
|
|
1204
|
+
getOutHasColors: () => useColor() ?? (process12.stdout.isTTY && process12.stdout.hasColors?.()),
|
|
1205
|
+
getErrHasColors: () => useColor() ?? (process12.stderr.isTTY && process12.stderr.hasColors?.()),
|
|
1206
1206
|
stripColor: (str) => stripColor(str)
|
|
1207
1207
|
};
|
|
1208
1208
|
this._hidden = false;
|
|
@@ -1585,7 +1585,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1585
1585
|
if (this._exitCallback) {
|
|
1586
1586
|
this._exitCallback(new CommanderError2(exitCode, code, message));
|
|
1587
1587
|
}
|
|
1588
|
-
|
|
1588
|
+
process12.exit(exitCode);
|
|
1589
1589
|
}
|
|
1590
1590
|
/**
|
|
1591
1591
|
* Register callback `fn` for the command.
|
|
@@ -1983,16 +1983,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1983
1983
|
}
|
|
1984
1984
|
parseOptions = parseOptions || {};
|
|
1985
1985
|
if (argv === void 0 && parseOptions.from === void 0) {
|
|
1986
|
-
if (
|
|
1986
|
+
if (process12.versions?.electron) {
|
|
1987
1987
|
parseOptions.from = "electron";
|
|
1988
1988
|
}
|
|
1989
|
-
const execArgv =
|
|
1989
|
+
const execArgv = process12.execArgv ?? [];
|
|
1990
1990
|
if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
|
|
1991
1991
|
parseOptions.from = "eval";
|
|
1992
1992
|
}
|
|
1993
1993
|
}
|
|
1994
1994
|
if (argv === void 0) {
|
|
1995
|
-
argv =
|
|
1995
|
+
argv = process12.argv;
|
|
1996
1996
|
}
|
|
1997
1997
|
this.rawArgs = argv.slice();
|
|
1998
1998
|
let userArgs;
|
|
@@ -2003,7 +2003,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2003
2003
|
userArgs = argv.slice(2);
|
|
2004
2004
|
break;
|
|
2005
2005
|
case "electron":
|
|
2006
|
-
if (
|
|
2006
|
+
if (process12.defaultApp) {
|
|
2007
2007
|
this._scriptPath = argv[1];
|
|
2008
2008
|
userArgs = argv.slice(2);
|
|
2009
2009
|
} else {
|
|
@@ -2147,9 +2147,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2147
2147
|
let launchWithNode = false;
|
|
2148
2148
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
2149
2149
|
function findFile(baseDir, baseName) {
|
|
2150
|
-
const localBin =
|
|
2150
|
+
const localBin = path6.resolve(baseDir, baseName);
|
|
2151
2151
|
if (fs.existsSync(localBin)) return localBin;
|
|
2152
|
-
if (sourceExt.includes(
|
|
2152
|
+
if (sourceExt.includes(path6.extname(baseName))) return void 0;
|
|
2153
2153
|
const foundExt = sourceExt.find(
|
|
2154
2154
|
(ext) => fs.existsSync(`${localBin}${ext}`)
|
|
2155
2155
|
);
|
|
@@ -2167,17 +2167,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2167
2167
|
} catch {
|
|
2168
2168
|
resolvedScriptPath = this._scriptPath;
|
|
2169
2169
|
}
|
|
2170
|
-
executableDir =
|
|
2171
|
-
|
|
2170
|
+
executableDir = path6.resolve(
|
|
2171
|
+
path6.dirname(resolvedScriptPath),
|
|
2172
2172
|
executableDir
|
|
2173
2173
|
);
|
|
2174
2174
|
}
|
|
2175
2175
|
if (executableDir) {
|
|
2176
2176
|
let localFile = findFile(executableDir, executableFile);
|
|
2177
2177
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
2178
|
-
const legacyName =
|
|
2178
|
+
const legacyName = path6.basename(
|
|
2179
2179
|
this._scriptPath,
|
|
2180
|
-
|
|
2180
|
+
path6.extname(this._scriptPath)
|
|
2181
2181
|
);
|
|
2182
2182
|
if (legacyName !== this._name) {
|
|
2183
2183
|
localFile = findFile(
|
|
@@ -2188,13 +2188,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2188
2188
|
}
|
|
2189
2189
|
executableFile = localFile || executableFile;
|
|
2190
2190
|
}
|
|
2191
|
-
launchWithNode = sourceExt.includes(
|
|
2191
|
+
launchWithNode = sourceExt.includes(path6.extname(executableFile));
|
|
2192
2192
|
let proc;
|
|
2193
|
-
if (
|
|
2193
|
+
if (process12.platform !== "win32") {
|
|
2194
2194
|
if (launchWithNode) {
|
|
2195
2195
|
args.unshift(executableFile);
|
|
2196
|
-
args = incrementNodeInspectorPort(
|
|
2197
|
-
proc = childProcess.spawn(
|
|
2196
|
+
args = incrementNodeInspectorPort(process12.execArgv).concat(args);
|
|
2197
|
+
proc = childProcess.spawn(process12.argv[0], args, { stdio: "inherit" });
|
|
2198
2198
|
} else {
|
|
2199
2199
|
proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
|
|
2200
2200
|
}
|
|
@@ -2205,13 +2205,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2205
2205
|
subcommand._name
|
|
2206
2206
|
);
|
|
2207
2207
|
args.unshift(executableFile);
|
|
2208
|
-
args = incrementNodeInspectorPort(
|
|
2209
|
-
proc = childProcess.spawn(
|
|
2208
|
+
args = incrementNodeInspectorPort(process12.execArgv).concat(args);
|
|
2209
|
+
proc = childProcess.spawn(process12.execPath, args, { stdio: "inherit" });
|
|
2210
2210
|
}
|
|
2211
2211
|
if (!proc.killed) {
|
|
2212
2212
|
const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
|
|
2213
2213
|
signals.forEach((signal) => {
|
|
2214
|
-
|
|
2214
|
+
process12.on(signal, () => {
|
|
2215
2215
|
if (proc.killed === false && proc.exitCode === null) {
|
|
2216
2216
|
proc.kill(signal);
|
|
2217
2217
|
}
|
|
@@ -2222,7 +2222,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2222
2222
|
proc.on("close", (code) => {
|
|
2223
2223
|
code = code ?? 1;
|
|
2224
2224
|
if (!exitCallback) {
|
|
2225
|
-
|
|
2225
|
+
process12.exit(code);
|
|
2226
2226
|
} else {
|
|
2227
2227
|
exitCallback(
|
|
2228
2228
|
new CommanderError2(
|
|
@@ -2244,7 +2244,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2244
2244
|
throw new Error(`'${executableFile}' not executable`);
|
|
2245
2245
|
}
|
|
2246
2246
|
if (!exitCallback) {
|
|
2247
|
-
|
|
2247
|
+
process12.exit(1);
|
|
2248
2248
|
} else {
|
|
2249
2249
|
const wrappedError = new CommanderError2(
|
|
2250
2250
|
1,
|
|
@@ -2739,13 +2739,13 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2739
2739
|
*/
|
|
2740
2740
|
_parseOptionsEnv() {
|
|
2741
2741
|
this.options.forEach((option) => {
|
|
2742
|
-
if (option.envVar && option.envVar in
|
|
2742
|
+
if (option.envVar && option.envVar in process12.env) {
|
|
2743
2743
|
const optionKey = option.attributeName();
|
|
2744
2744
|
if (this.getOptionValue(optionKey) === void 0 || ["default", "config", "env"].includes(
|
|
2745
2745
|
this.getOptionValueSource(optionKey)
|
|
2746
2746
|
)) {
|
|
2747
2747
|
if (option.required || option.optional) {
|
|
2748
|
-
this.emit(`optionEnv:${option.name()}`,
|
|
2748
|
+
this.emit(`optionEnv:${option.name()}`, process12.env[option.envVar]);
|
|
2749
2749
|
} else {
|
|
2750
2750
|
this.emit(`optionEnv:${option.name()}`);
|
|
2751
2751
|
}
|
|
@@ -3035,7 +3035,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3035
3035
|
* @return {Command}
|
|
3036
3036
|
*/
|
|
3037
3037
|
nameFromFilename(filename) {
|
|
3038
|
-
this._name =
|
|
3038
|
+
this._name = path6.basename(filename, path6.extname(filename));
|
|
3039
3039
|
return this;
|
|
3040
3040
|
}
|
|
3041
3041
|
/**
|
|
@@ -3049,9 +3049,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3049
3049
|
* @param {string} [path]
|
|
3050
3050
|
* @return {(string|null|Command)}
|
|
3051
3051
|
*/
|
|
3052
|
-
executableDir(
|
|
3053
|
-
if (
|
|
3054
|
-
this._executableDir =
|
|
3052
|
+
executableDir(path7) {
|
|
3053
|
+
if (path7 === void 0) return this._executableDir;
|
|
3054
|
+
this._executableDir = path7;
|
|
3055
3055
|
return this;
|
|
3056
3056
|
}
|
|
3057
3057
|
/**
|
|
@@ -3200,7 +3200,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3200
3200
|
*/
|
|
3201
3201
|
help(contextOptions) {
|
|
3202
3202
|
this.outputHelp(contextOptions);
|
|
3203
|
-
let exitCode = Number(
|
|
3203
|
+
let exitCode = Number(process12.exitCode ?? 0);
|
|
3204
3204
|
if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
|
|
3205
3205
|
exitCode = 1;
|
|
3206
3206
|
}
|
|
@@ -3290,9 +3290,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3290
3290
|
});
|
|
3291
3291
|
}
|
|
3292
3292
|
function useColor() {
|
|
3293
|
-
if (
|
|
3293
|
+
if (process12.env.NO_COLOR || process12.env.FORCE_COLOR === "0" || process12.env.FORCE_COLOR === "false")
|
|
3294
3294
|
return false;
|
|
3295
|
-
if (
|
|
3295
|
+
if (process12.env.FORCE_COLOR || process12.env.CLICOLOR_FORCE !== void 0)
|
|
3296
3296
|
return true;
|
|
3297
3297
|
return void 0;
|
|
3298
3298
|
}
|
|
@@ -3323,6 +3323,9 @@ var require_commander = __commonJS({
|
|
|
3323
3323
|
}
|
|
3324
3324
|
});
|
|
3325
3325
|
|
|
3326
|
+
// src/index.ts
|
|
3327
|
+
var import_node_child_process3 = require("node:child_process");
|
|
3328
|
+
|
|
3326
3329
|
// ../../node_modules/commander/esm.mjs
|
|
3327
3330
|
var import_index = __toESM(require_commander(), 1);
|
|
3328
3331
|
var {
|
|
@@ -3343,7 +3346,7 @@ var {
|
|
|
3343
3346
|
// package.json
|
|
3344
3347
|
var package_default = {
|
|
3345
3348
|
name: "@kimbho/kimbho-cli",
|
|
3346
|
-
version: "0.1.
|
|
3349
|
+
version: "0.1.3",
|
|
3347
3350
|
description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
|
|
3348
3351
|
type: "module",
|
|
3349
3352
|
engines: {
|
|
@@ -3559,6 +3562,10 @@ function listAgentProfiles() {
|
|
|
3559
3562
|
return Object.values(AGENT_CATALOG).sort((left, right) => left.role.localeCompare(right.role));
|
|
3560
3563
|
}
|
|
3561
3564
|
|
|
3565
|
+
// ../agent-runtime/dist/orchestrator.js
|
|
3566
|
+
var import_promises4 = require("node:fs/promises");
|
|
3567
|
+
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
3568
|
+
|
|
3562
3569
|
// ../../node_modules/zod/v3/external.js
|
|
3563
3570
|
var external_exports = {};
|
|
3564
3571
|
__export(external_exports, {
|
|
@@ -4037,8 +4044,8 @@ function getErrorMap() {
|
|
|
4037
4044
|
|
|
4038
4045
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
4039
4046
|
var makeIssue = (params) => {
|
|
4040
|
-
const { data, path:
|
|
4041
|
-
const fullPath = [...
|
|
4047
|
+
const { data, path: path6, errorMaps, issueData } = params;
|
|
4048
|
+
const fullPath = [...path6, ...issueData.path || []];
|
|
4042
4049
|
const fullIssue = {
|
|
4043
4050
|
...issueData,
|
|
4044
4051
|
path: fullPath
|
|
@@ -4154,11 +4161,11 @@ var errorUtil;
|
|
|
4154
4161
|
|
|
4155
4162
|
// ../../node_modules/zod/v3/types.js
|
|
4156
4163
|
var ParseInputLazyPath = class {
|
|
4157
|
-
constructor(parent, value,
|
|
4164
|
+
constructor(parent, value, path6, key) {
|
|
4158
4165
|
this._cachedPath = [];
|
|
4159
4166
|
this.parent = parent;
|
|
4160
4167
|
this.data = value;
|
|
4161
|
-
this._path =
|
|
4168
|
+
this._path = path6;
|
|
4162
4169
|
this._key = key;
|
|
4163
4170
|
}
|
|
4164
4171
|
get path() {
|
|
@@ -7808,6 +7815,16 @@ var KimbhoConfigSchema = external_exports.object({
|
|
|
7808
7815
|
}
|
|
7809
7816
|
});
|
|
7810
7817
|
|
|
7818
|
+
// ../core/dist/contracts/execution.js
|
|
7819
|
+
var ToolResultSchema = external_exports.object({
|
|
7820
|
+
toolId: external_exports.string().min(1),
|
|
7821
|
+
success: external_exports.boolean(),
|
|
7822
|
+
summary: external_exports.string().min(1),
|
|
7823
|
+
stdout: external_exports.string().optional(),
|
|
7824
|
+
stderr: external_exports.string().optional(),
|
|
7825
|
+
artifacts: external_exports.array(external_exports.string()).default([])
|
|
7826
|
+
});
|
|
7827
|
+
|
|
7811
7828
|
// ../core/dist/contracts/session.js
|
|
7812
7829
|
var SessionStatusSchema = external_exports.enum([
|
|
7813
7830
|
"planned",
|
|
@@ -7816,6 +7833,22 @@ var SessionStatusSchema = external_exports.enum([
|
|
|
7816
7833
|
"blocked",
|
|
7817
7834
|
"completed"
|
|
7818
7835
|
]);
|
|
7836
|
+
var SessionEventTypeSchema = external_exports.enum([
|
|
7837
|
+
"task-started",
|
|
7838
|
+
"task-completed",
|
|
7839
|
+
"task-blocked",
|
|
7840
|
+
"note"
|
|
7841
|
+
]);
|
|
7842
|
+
var SessionEventSchema = external_exports.object({
|
|
7843
|
+
id: external_exports.string().min(1),
|
|
7844
|
+
timestamp: external_exports.string().datetime(),
|
|
7845
|
+
type: SessionEventTypeSchema,
|
|
7846
|
+
taskId: external_exports.string().min(1).optional(),
|
|
7847
|
+
agentRole: AgentRoleSchema.optional(),
|
|
7848
|
+
message: external_exports.string().min(1),
|
|
7849
|
+
toolResults: external_exports.array(ToolResultSchema).default([]),
|
|
7850
|
+
artifacts: external_exports.array(external_exports.string()).default([])
|
|
7851
|
+
});
|
|
7819
7852
|
var SessionSnapshotSchema = external_exports.object({
|
|
7820
7853
|
id: external_exports.string().min(1),
|
|
7821
7854
|
goal: external_exports.string().min(1),
|
|
@@ -7827,8 +7860,10 @@ var SessionSnapshotSchema = external_exports.object({
|
|
|
7827
7860
|
plan: KimbhoPlanSchema,
|
|
7828
7861
|
readyTaskIds: external_exports.array(external_exports.string()).default([]),
|
|
7829
7862
|
blockedTaskIds: external_exports.array(external_exports.string()).default([]),
|
|
7863
|
+
completedTaskIds: external_exports.array(external_exports.string()).default([]),
|
|
7830
7864
|
assignedAgents: external_exports.array(AgentRoleSchema).default([]),
|
|
7831
|
-
notes: external_exports.array(external_exports.string()).default([])
|
|
7865
|
+
notes: external_exports.array(external_exports.string()).default([]),
|
|
7866
|
+
events: external_exports.array(SessionEventSchema).default([])
|
|
7832
7867
|
});
|
|
7833
7868
|
|
|
7834
7869
|
// ../core/dist/config/config.js
|
|
@@ -8116,14 +8151,6 @@ var ToolDescriptorSchema = external_exports.object({
|
|
|
8116
8151
|
producesArtifacts: external_exports.boolean().default(false),
|
|
8117
8152
|
allowedRoles: external_exports.array(AgentRoleSchema).default([])
|
|
8118
8153
|
});
|
|
8119
|
-
var ToolResultSchema = external_exports.object({
|
|
8120
|
-
toolId: external_exports.string().min(1),
|
|
8121
|
-
success: external_exports.boolean(),
|
|
8122
|
-
summary: external_exports.string().min(1),
|
|
8123
|
-
stdout: external_exports.string().optional(),
|
|
8124
|
-
stderr: external_exports.string().optional(),
|
|
8125
|
-
artifacts: external_exports.array(external_exports.string()).default([])
|
|
8126
|
-
});
|
|
8127
8154
|
|
|
8128
8155
|
// ../tools/dist/registry.js
|
|
8129
8156
|
var BUILTIN_TOOLS = [
|
|
@@ -8233,26 +8260,296 @@ function createDefaultToolRegistry() {
|
|
|
8233
8260
|
return new ToolRegistry(BUILTIN_TOOLS);
|
|
8234
8261
|
}
|
|
8235
8262
|
|
|
8263
|
+
// ../tools/dist/runtime.js
|
|
8264
|
+
var import_promises3 = require("node:fs/promises");
|
|
8265
|
+
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
8266
|
+
var import_node_process = __toESM(require("node:process"), 1);
|
|
8267
|
+
var import_node_child_process = require("node:child_process");
|
|
8268
|
+
var import_node_os = require("node:os");
|
|
8269
|
+
var DEFAULT_CAPTURE_LIMIT = 16e3;
|
|
8270
|
+
function truncateOutput(value) {
|
|
8271
|
+
if (!value) {
|
|
8272
|
+
return value;
|
|
8273
|
+
}
|
|
8274
|
+
if (value.length <= DEFAULT_CAPTURE_LIMIT) {
|
|
8275
|
+
return value;
|
|
8276
|
+
}
|
|
8277
|
+
const omitted = value.length - DEFAULT_CAPTURE_LIMIT;
|
|
8278
|
+
return `${value.slice(0, DEFAULT_CAPTURE_LIMIT)}
|
|
8279
|
+
... [truncated ${omitted} chars]`;
|
|
8280
|
+
}
|
|
8281
|
+
function resolveWorkspacePath(cwd, filePath) {
|
|
8282
|
+
const resolved = import_node_path3.default.resolve(cwd, filePath);
|
|
8283
|
+
const relative = import_node_path3.default.relative(cwd, resolved);
|
|
8284
|
+
if (relative.startsWith("..") || import_node_path3.default.isAbsolute(relative)) {
|
|
8285
|
+
throw new Error(`Path "${filePath}" escapes the workspace.`);
|
|
8286
|
+
}
|
|
8287
|
+
return resolved;
|
|
8288
|
+
}
|
|
8289
|
+
async function runSpawn(command, args, cwd, timeoutMs) {
|
|
8290
|
+
return new Promise((resolve, reject) => {
|
|
8291
|
+
const child = (0, import_node_child_process.spawn)(command, args, {
|
|
8292
|
+
cwd,
|
|
8293
|
+
env: import_node_process.default.env,
|
|
8294
|
+
stdio: [
|
|
8295
|
+
"ignore",
|
|
8296
|
+
"pipe",
|
|
8297
|
+
"pipe"
|
|
8298
|
+
]
|
|
8299
|
+
});
|
|
8300
|
+
const stdout = [];
|
|
8301
|
+
const stderr = [];
|
|
8302
|
+
let settled = false;
|
|
8303
|
+
let timedOut = false;
|
|
8304
|
+
const timer = setTimeout(() => {
|
|
8305
|
+
timedOut = true;
|
|
8306
|
+
child.kill("SIGTERM");
|
|
8307
|
+
setTimeout(() => child.kill("SIGKILL"), 1e3).unref();
|
|
8308
|
+
}, timeoutMs);
|
|
8309
|
+
child.stdout.on("data", (chunk) => {
|
|
8310
|
+
stdout.push(String(chunk));
|
|
8311
|
+
});
|
|
8312
|
+
child.stderr.on("data", (chunk) => {
|
|
8313
|
+
stderr.push(String(chunk));
|
|
8314
|
+
});
|
|
8315
|
+
child.on("error", (error) => {
|
|
8316
|
+
if (settled) {
|
|
8317
|
+
return;
|
|
8318
|
+
}
|
|
8319
|
+
settled = true;
|
|
8320
|
+
clearTimeout(timer);
|
|
8321
|
+
reject(error);
|
|
8322
|
+
});
|
|
8323
|
+
child.on("close", (code) => {
|
|
8324
|
+
if (settled) {
|
|
8325
|
+
return;
|
|
8326
|
+
}
|
|
8327
|
+
settled = true;
|
|
8328
|
+
clearTimeout(timer);
|
|
8329
|
+
resolve({
|
|
8330
|
+
code,
|
|
8331
|
+
stdout: stdout.join(""),
|
|
8332
|
+
stderr: stderr.join(""),
|
|
8333
|
+
timedOut
|
|
8334
|
+
});
|
|
8335
|
+
});
|
|
8336
|
+
});
|
|
8337
|
+
}
|
|
8338
|
+
async function runShellCommand(toolId, command, cwd, timeoutMs) {
|
|
8339
|
+
const shell = import_node_process.default.env.SHELL ?? "/bin/sh";
|
|
8340
|
+
const result = await runSpawn(shell, [
|
|
8341
|
+
"-lc",
|
|
8342
|
+
command
|
|
8343
|
+
], cwd, timeoutMs);
|
|
8344
|
+
const success = !result.timedOut && result.code === 0;
|
|
8345
|
+
const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : success ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
|
|
8346
|
+
return ToolResultSchema.parse({
|
|
8347
|
+
toolId,
|
|
8348
|
+
success,
|
|
8349
|
+
summary,
|
|
8350
|
+
stdout: truncateOutput(result.stdout),
|
|
8351
|
+
stderr: truncateOutput(result.stderr)
|
|
8352
|
+
});
|
|
8353
|
+
}
|
|
8354
|
+
async function executeFileRead(input, context) {
|
|
8355
|
+
const rawPath = typeof input.path === "string" ? input.path : null;
|
|
8356
|
+
if (!rawPath) {
|
|
8357
|
+
throw new Error("file.read requires a string path.");
|
|
8358
|
+
}
|
|
8359
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
8360
|
+
const contents = await (0, import_promises3.readFile)(targetPath, "utf8");
|
|
8361
|
+
return ToolResultSchema.parse({
|
|
8362
|
+
toolId: "file.read",
|
|
8363
|
+
success: true,
|
|
8364
|
+
summary: `Read ${import_node_path3.default.relative(context.cwd, targetPath) || import_node_path3.default.basename(targetPath)}.`,
|
|
8365
|
+
stdout: truncateOutput(contents),
|
|
8366
|
+
artifacts: [
|
|
8367
|
+
targetPath
|
|
8368
|
+
]
|
|
8369
|
+
});
|
|
8370
|
+
}
|
|
8371
|
+
async function executeShell(input, context, timeoutMs) {
|
|
8372
|
+
const command = typeof input.command === "string" ? input.command : null;
|
|
8373
|
+
if (!command) {
|
|
8374
|
+
throw new Error("shell.exec requires a command string.");
|
|
8375
|
+
}
|
|
8376
|
+
return runShellCommand("shell.exec", command, context.cwd, timeoutMs);
|
|
8377
|
+
}
|
|
8378
|
+
async function executeGitStatus(_input, context, timeoutMs) {
|
|
8379
|
+
return runShellCommand("git.status", "git status --short --branch", context.cwd, timeoutMs);
|
|
8380
|
+
}
|
|
8381
|
+
async function executeTests(input, context, timeoutMs) {
|
|
8382
|
+
const command = typeof input.command === "string" && input.command.trim().length > 0 ? input.command : "npm test";
|
|
8383
|
+
const result = await runShellCommand("tests.run", command, context.cwd, timeoutMs);
|
|
8384
|
+
return ToolResultSchema.parse({
|
|
8385
|
+
...result,
|
|
8386
|
+
summary: result.success ? `Verification command passed: ${command}` : `Verification command failed: ${command}`
|
|
8387
|
+
});
|
|
8388
|
+
}
|
|
8389
|
+
async function executeFilePatch(input, context, timeoutMs) {
|
|
8390
|
+
const patch = typeof input.patch === "string" ? input.patch : null;
|
|
8391
|
+
if (!patch) {
|
|
8392
|
+
throw new Error("file.patch requires a unified diff in the patch field.");
|
|
8393
|
+
}
|
|
8394
|
+
const tempDir = await (0, import_promises3.mkdtemp)(import_node_path3.default.join((0, import_node_os.tmpdir)(), "kimbho-patch-"));
|
|
8395
|
+
const patchPath = import_node_path3.default.join(tempDir, "change.patch");
|
|
8396
|
+
try {
|
|
8397
|
+
await (0, import_promises3.writeFile)(patchPath, patch, "utf8");
|
|
8398
|
+
const result = await runSpawn("git", [
|
|
8399
|
+
"apply",
|
|
8400
|
+
"--recount",
|
|
8401
|
+
"--whitespace=nowarn",
|
|
8402
|
+
patchPath
|
|
8403
|
+
], context.cwd, timeoutMs);
|
|
8404
|
+
const success = !result.timedOut && result.code === 0;
|
|
8405
|
+
return ToolResultSchema.parse({
|
|
8406
|
+
toolId: "file.patch",
|
|
8407
|
+
success,
|
|
8408
|
+
summary: success ? "Patch applied successfully." : result.timedOut ? `Patch timed out after ${timeoutMs}ms.` : `Patch failed with code ${result.code ?? "unknown"}.`,
|
|
8409
|
+
stdout: truncateOutput(result.stdout),
|
|
8410
|
+
stderr: truncateOutput(result.stderr),
|
|
8411
|
+
artifacts: []
|
|
8412
|
+
});
|
|
8413
|
+
} finally {
|
|
8414
|
+
await (0, import_promises3.rm)(tempDir, { recursive: true, force: true });
|
|
8415
|
+
}
|
|
8416
|
+
}
|
|
8417
|
+
var ToolRuntime = class {
|
|
8418
|
+
registry;
|
|
8419
|
+
executors;
|
|
8420
|
+
constructor(registry = createDefaultToolRegistry()) {
|
|
8421
|
+
this.registry = registry;
|
|
8422
|
+
this.executors = /* @__PURE__ */ new Map([
|
|
8423
|
+
[
|
|
8424
|
+
"file.read",
|
|
8425
|
+
(input, context) => executeFileRead(input, context)
|
|
8426
|
+
],
|
|
8427
|
+
[
|
|
8428
|
+
"file.patch",
|
|
8429
|
+
executeFilePatch
|
|
8430
|
+
],
|
|
8431
|
+
[
|
|
8432
|
+
"shell.exec",
|
|
8433
|
+
executeShell
|
|
8434
|
+
],
|
|
8435
|
+
[
|
|
8436
|
+
"git.status",
|
|
8437
|
+
executeGitStatus
|
|
8438
|
+
],
|
|
8439
|
+
[
|
|
8440
|
+
"tests.run",
|
|
8441
|
+
executeTests
|
|
8442
|
+
]
|
|
8443
|
+
]);
|
|
8444
|
+
}
|
|
8445
|
+
async run(toolId, input, context) {
|
|
8446
|
+
const descriptor = this.registry.get(toolId);
|
|
8447
|
+
if (!descriptor) {
|
|
8448
|
+
throw new Error(`Unknown tool "${toolId}".`);
|
|
8449
|
+
}
|
|
8450
|
+
const executor = this.executors.get(toolId);
|
|
8451
|
+
if (!executor) {
|
|
8452
|
+
throw new Error(`No executor registered for "${toolId}".`);
|
|
8453
|
+
}
|
|
8454
|
+
return executor(input, context, descriptor.timeoutMs);
|
|
8455
|
+
}
|
|
8456
|
+
};
|
|
8457
|
+
|
|
8236
8458
|
// ../agent-runtime/dist/orchestrator.js
|
|
8237
8459
|
function createSessionId() {
|
|
8238
8460
|
return `session-${Date.now()}`;
|
|
8239
8461
|
}
|
|
8462
|
+
function createEventId(type, taskId) {
|
|
8463
|
+
return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8464
|
+
}
|
|
8240
8465
|
function isTaskReady(task, completedTaskIds) {
|
|
8241
8466
|
return task.status === "pending" && task.dependsOn.every((taskId) => completedTaskIds.has(taskId));
|
|
8242
8467
|
}
|
|
8468
|
+
function completedTaskIdsFromPlan(plan) {
|
|
8469
|
+
return new Set(flattenPlanTasks(plan).filter((task) => task.status === "completed").map((task) => task.id));
|
|
8470
|
+
}
|
|
8471
|
+
function deriveStatus(plan, readyTasks, blockedTasks) {
|
|
8472
|
+
const tasks = flattenPlanTasks(plan);
|
|
8473
|
+
const completed = tasks.filter((task) => task.status === "completed").length;
|
|
8474
|
+
if (completed === tasks.length && tasks.length > 0) {
|
|
8475
|
+
return "completed";
|
|
8476
|
+
}
|
|
8477
|
+
if (readyTasks.length > 0) {
|
|
8478
|
+
return completed > 0 ? "running" : "ready";
|
|
8479
|
+
}
|
|
8480
|
+
if (blockedTasks.length > 0) {
|
|
8481
|
+
return "blocked";
|
|
8482
|
+
}
|
|
8483
|
+
return "planned";
|
|
8484
|
+
}
|
|
8485
|
+
function updateTaskStatus(plan, taskId, status) {
|
|
8486
|
+
const milestones = plan.milestones.map((milestone) => ({
|
|
8487
|
+
...milestone,
|
|
8488
|
+
tasks: milestone.tasks.map((task) => task.id === taskId ? {
|
|
8489
|
+
...task,
|
|
8490
|
+
status
|
|
8491
|
+
} : task)
|
|
8492
|
+
}));
|
|
8493
|
+
return {
|
|
8494
|
+
...plan,
|
|
8495
|
+
milestones
|
|
8496
|
+
};
|
|
8497
|
+
}
|
|
8498
|
+
function maybeAppendNote(notes, note) {
|
|
8499
|
+
return notes.at(-1) === note ? notes : [
|
|
8500
|
+
...notes,
|
|
8501
|
+
note
|
|
8502
|
+
];
|
|
8503
|
+
}
|
|
8504
|
+
function renderToolResultSection(results) {
|
|
8505
|
+
return results.map((result) => {
|
|
8506
|
+
const lines = [
|
|
8507
|
+
`## ${result.toolId}`,
|
|
8508
|
+
`- success: ${result.success ? "yes" : "no"}`,
|
|
8509
|
+
`- summary: ${result.summary}`
|
|
8510
|
+
];
|
|
8511
|
+
if (result.stdout) {
|
|
8512
|
+
lines.push("");
|
|
8513
|
+
lines.push("```text");
|
|
8514
|
+
lines.push(result.stdout);
|
|
8515
|
+
lines.push("```");
|
|
8516
|
+
}
|
|
8517
|
+
if (result.stderr) {
|
|
8518
|
+
lines.push("");
|
|
8519
|
+
lines.push("```text");
|
|
8520
|
+
lines.push(result.stderr);
|
|
8521
|
+
lines.push("```");
|
|
8522
|
+
}
|
|
8523
|
+
return lines.join("\n");
|
|
8524
|
+
}).join("\n\n");
|
|
8525
|
+
}
|
|
8526
|
+
function extractPackageScripts(toolResults) {
|
|
8527
|
+
const packageResult = toolResults.find((result) => result.artifacts.some((artifact) => artifact.endsWith("package.json")) && result.stdout);
|
|
8528
|
+
if (!packageResult?.stdout) {
|
|
8529
|
+
return [];
|
|
8530
|
+
}
|
|
8531
|
+
try {
|
|
8532
|
+
const parsed = JSON.parse(packageResult.stdout);
|
|
8533
|
+
return Object.keys(parsed.scripts ?? {}).sort();
|
|
8534
|
+
} catch {
|
|
8535
|
+
return [];
|
|
8536
|
+
}
|
|
8537
|
+
}
|
|
8243
8538
|
var ExecutionOrchestrator = class {
|
|
8244
8539
|
toolRegistry;
|
|
8245
|
-
|
|
8540
|
+
toolRuntime;
|
|
8541
|
+
constructor(toolRegistry = createDefaultToolRegistry(), toolRuntime = new ToolRuntime(toolRegistry)) {
|
|
8246
8542
|
this.toolRegistry = toolRegistry;
|
|
8543
|
+
this.toolRuntime = toolRuntime;
|
|
8247
8544
|
}
|
|
8248
|
-
buildEnvelope(request, plan) {
|
|
8545
|
+
buildEnvelope(request, plan, sessionId = createSessionId()) {
|
|
8249
8546
|
const tasks = flattenPlanTasks(plan);
|
|
8250
|
-
const completedTaskIds =
|
|
8547
|
+
const completedTaskIds = completedTaskIdsFromPlan(plan);
|
|
8251
8548
|
const readyTasks = tasks.filter((task) => isTaskReady(task, completedTaskIds));
|
|
8252
8549
|
const blockedTasks = tasks.filter((task) => task.status !== "completed" && !isTaskReady(task, completedTaskIds));
|
|
8253
8550
|
const assignedAgents = this.selectAgentsForTasks(readyTasks);
|
|
8254
8551
|
return {
|
|
8255
|
-
sessionId
|
|
8552
|
+
sessionId,
|
|
8256
8553
|
request,
|
|
8257
8554
|
plan,
|
|
8258
8555
|
readyTasks,
|
|
@@ -8260,9 +8557,9 @@ var ExecutionOrchestrator = class {
|
|
|
8260
8557
|
assignedAgents
|
|
8261
8558
|
};
|
|
8262
8559
|
}
|
|
8263
|
-
createSessionSnapshot(envelope) {
|
|
8560
|
+
createSessionSnapshot(envelope, overrides = {}) {
|
|
8264
8561
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
8265
|
-
const status = envelope.readyTasks.
|
|
8562
|
+
const status = deriveStatus(envelope.plan, envelope.readyTasks, envelope.blockedTasks);
|
|
8266
8563
|
return SessionSnapshotSchema.parse({
|
|
8267
8564
|
id: envelope.sessionId,
|
|
8268
8565
|
goal: envelope.request.goal,
|
|
@@ -8274,11 +8571,75 @@ var ExecutionOrchestrator = class {
|
|
|
8274
8571
|
plan: envelope.plan,
|
|
8275
8572
|
readyTaskIds: envelope.readyTasks.map((task) => task.id),
|
|
8276
8573
|
blockedTaskIds: envelope.blockedTasks.map((task) => task.id),
|
|
8574
|
+
completedTaskIds: Array.from(completedTaskIdsFromPlan(envelope.plan)),
|
|
8277
8575
|
assignedAgents: envelope.assignedAgents.map((agent) => agent.role),
|
|
8278
8576
|
notes: [
|
|
8279
|
-
"Initial session snapshot created from the current plan."
|
|
8280
|
-
|
|
8281
|
-
]
|
|
8577
|
+
"Initial session snapshot created from the current plan."
|
|
8578
|
+
],
|
|
8579
|
+
events: [],
|
|
8580
|
+
...overrides
|
|
8581
|
+
});
|
|
8582
|
+
}
|
|
8583
|
+
async continueSession(session, options = {}) {
|
|
8584
|
+
let workingPlan = session.plan;
|
|
8585
|
+
let notes = [
|
|
8586
|
+
...session.notes
|
|
8587
|
+
];
|
|
8588
|
+
let events = [
|
|
8589
|
+
...session.events
|
|
8590
|
+
];
|
|
8591
|
+
const maxAutoTasks = options.maxAutoTasks ?? 2;
|
|
8592
|
+
let executedTasks = 0;
|
|
8593
|
+
while (executedTasks < maxAutoTasks) {
|
|
8594
|
+
const envelope = this.buildEnvelope(session.request, workingPlan, session.id);
|
|
8595
|
+
const autoTask = envelope.readyTasks.find((task) => this.canAutoExecuteTask(task));
|
|
8596
|
+
if (!autoTask) {
|
|
8597
|
+
const nextTask = envelope.readyTasks[0];
|
|
8598
|
+
if (nextTask) {
|
|
8599
|
+
const note = `Execution paused at ${nextTask.id} (${nextTask.title}): no built-in executor is wired for ${nextTask.agentRole} yet.`;
|
|
8600
|
+
notes = maybeAppendNote(notes, note);
|
|
8601
|
+
}
|
|
8602
|
+
return this.createSessionSnapshot(this.buildEnvelope(session.request, workingPlan, session.id), {
|
|
8603
|
+
startedAt: session.startedAt,
|
|
8604
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8605
|
+
notes,
|
|
8606
|
+
events
|
|
8607
|
+
});
|
|
8608
|
+
}
|
|
8609
|
+
events.push({
|
|
8610
|
+
id: createEventId("task-started", autoTask.id),
|
|
8611
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8612
|
+
type: "task-started",
|
|
8613
|
+
taskId: autoTask.id,
|
|
8614
|
+
agentRole: autoTask.agentRole,
|
|
8615
|
+
message: `Started ${autoTask.id}: ${autoTask.title}`,
|
|
8616
|
+
toolResults: [],
|
|
8617
|
+
artifacts: []
|
|
8618
|
+
});
|
|
8619
|
+
const outcome = await this.executeTask(session.id, autoTask, session.request, workingPlan);
|
|
8620
|
+
workingPlan = updateTaskStatus(workingPlan, autoTask.id, outcome.status === "completed" ? "completed" : "blocked");
|
|
8621
|
+
notes = maybeAppendNote(notes, outcome.summary);
|
|
8622
|
+
events.push({
|
|
8623
|
+
id: createEventId(outcome.status === "completed" ? "task-completed" : "task-blocked", autoTask.id),
|
|
8624
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8625
|
+
type: outcome.status === "completed" ? "task-completed" : "task-blocked",
|
|
8626
|
+
taskId: autoTask.id,
|
|
8627
|
+
agentRole: autoTask.agentRole,
|
|
8628
|
+
message: outcome.summary,
|
|
8629
|
+
toolResults: outcome.toolResults,
|
|
8630
|
+
artifacts: outcome.artifacts
|
|
8631
|
+
});
|
|
8632
|
+
executedTasks += 1;
|
|
8633
|
+
}
|
|
8634
|
+
const postLimitEnvelope = this.buildEnvelope(session.request, workingPlan, session.id);
|
|
8635
|
+
if (postLimitEnvelope.readyTasks.length > 0) {
|
|
8636
|
+
notes = maybeAppendNote(notes, `Auto execution limit reached after ${executedTasks} task${executedTasks === 1 ? "" : "s"}.`);
|
|
8637
|
+
}
|
|
8638
|
+
return this.createSessionSnapshot(postLimitEnvelope, {
|
|
8639
|
+
startedAt: session.startedAt,
|
|
8640
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8641
|
+
notes,
|
|
8642
|
+
events
|
|
8282
8643
|
});
|
|
8283
8644
|
}
|
|
8284
8645
|
selectAgentsForTasks(tasks) {
|
|
@@ -8288,6 +8649,107 @@ var ExecutionOrchestrator = class {
|
|
|
8288
8649
|
toolsForAgent(role) {
|
|
8289
8650
|
return this.toolRegistry.byRole(role);
|
|
8290
8651
|
}
|
|
8652
|
+
canAutoExecuteTask(task) {
|
|
8653
|
+
return task.agentRole === "repo-analyst" || task.agentRole === "planner";
|
|
8654
|
+
}
|
|
8655
|
+
async executeTask(sessionId, task, request, plan) {
|
|
8656
|
+
if (task.agentRole === "repo-analyst") {
|
|
8657
|
+
return this.executeRepoAnalysisTask(sessionId, task, request);
|
|
8658
|
+
}
|
|
8659
|
+
if (task.agentRole === "planner") {
|
|
8660
|
+
return this.executePlannerTask(sessionId, task, request, plan);
|
|
8661
|
+
}
|
|
8662
|
+
return {
|
|
8663
|
+
status: "blocked",
|
|
8664
|
+
summary: `Task ${task.id} is ready, but no executor is available for ${task.agentRole}.`,
|
|
8665
|
+
toolResults: [],
|
|
8666
|
+
artifacts: []
|
|
8667
|
+
};
|
|
8668
|
+
}
|
|
8669
|
+
async executeRepoAnalysisTask(sessionId, task, request) {
|
|
8670
|
+
const context = { cwd: request.cwd };
|
|
8671
|
+
const toolResults = await Promise.all([
|
|
8672
|
+
this.safeRunTool("git.status", {}, context),
|
|
8673
|
+
this.safeRunTool("file.read", { path: "package.json" }, context),
|
|
8674
|
+
this.safeRunTool("file.read", { path: "README.md" }, context)
|
|
8675
|
+
]);
|
|
8676
|
+
const successfulResults = toolResults.filter((result) => result.success);
|
|
8677
|
+
const scripts = extractPackageScripts(successfulResults);
|
|
8678
|
+
const artifactPath = await this.writeLogArtifact(sessionId, "repo-analysis", request.cwd, [
|
|
8679
|
+
`# Repo Analysis`,
|
|
8680
|
+
``,
|
|
8681
|
+
`Goal: ${request.goal}`,
|
|
8682
|
+
`Workspace: ${request.cwd}`,
|
|
8683
|
+
`Task: ${task.id} - ${task.title}`,
|
|
8684
|
+
``,
|
|
8685
|
+
`## Observations`,
|
|
8686
|
+
`- Successful tool probes: ${successfulResults.length}/${toolResults.length}`,
|
|
8687
|
+
`- Workspace state: ${request.workspaceState}`,
|
|
8688
|
+
`- Detected scripts: ${scripts.length > 0 ? scripts.join(", ") : "none"}`,
|
|
8689
|
+
``,
|
|
8690
|
+
renderToolResultSection(toolResults)
|
|
8691
|
+
].join("\n"));
|
|
8692
|
+
return {
|
|
8693
|
+
status: "completed",
|
|
8694
|
+
summary: `Completed ${task.id}: captured repo analysis and saved ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8695
|
+
toolResults,
|
|
8696
|
+
artifacts: [
|
|
8697
|
+
artifactPath
|
|
8698
|
+
]
|
|
8699
|
+
};
|
|
8700
|
+
}
|
|
8701
|
+
async executePlannerTask(sessionId, task, request, plan) {
|
|
8702
|
+
const artifactPath = await this.writeLogArtifact(sessionId, "architecture-brief", request.cwd, [
|
|
8703
|
+
`# Architecture Brief`,
|
|
8704
|
+
``,
|
|
8705
|
+
`Goal: ${request.goal}`,
|
|
8706
|
+
`Summary: ${plan.summary}`,
|
|
8707
|
+
`Repo Strategy: ${plan.repoStrategy.mode} - ${plan.repoStrategy.reasoning}`,
|
|
8708
|
+
``,
|
|
8709
|
+
`## Assumptions`,
|
|
8710
|
+
...plan.assumptions.map((assumption) => `- ${assumption}`),
|
|
8711
|
+
``,
|
|
8712
|
+
`## Milestones`,
|
|
8713
|
+
...plan.milestones.flatMap((milestone) => [
|
|
8714
|
+
`### ${milestone.title}`,
|
|
8715
|
+
milestone.objective,
|
|
8716
|
+
...milestone.tasks.map((milestoneTask) => `- ${milestoneTask.id}: ${milestoneTask.title} [${milestoneTask.agentRole}]`),
|
|
8717
|
+
``
|
|
8718
|
+
]),
|
|
8719
|
+
`## Verification`,
|
|
8720
|
+
...plan.verificationChecklist.map((item) => `- ${item}`)
|
|
8721
|
+
].join("\n"));
|
|
8722
|
+
return {
|
|
8723
|
+
status: "completed",
|
|
8724
|
+
summary: `Completed ${task.id}: wrote architecture brief ${import_node_path4.default.basename(artifactPath)}.`,
|
|
8725
|
+
toolResults: [],
|
|
8726
|
+
artifacts: [
|
|
8727
|
+
artifactPath
|
|
8728
|
+
]
|
|
8729
|
+
};
|
|
8730
|
+
}
|
|
8731
|
+
async safeRunTool(toolId, input, context) {
|
|
8732
|
+
try {
|
|
8733
|
+
return await this.toolRuntime.run(toolId, input, context);
|
|
8734
|
+
} catch (error) {
|
|
8735
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8736
|
+
return {
|
|
8737
|
+
toolId,
|
|
8738
|
+
success: false,
|
|
8739
|
+
summary: `${toolId} failed: ${message}`,
|
|
8740
|
+
stderr: message,
|
|
8741
|
+
artifacts: []
|
|
8742
|
+
};
|
|
8743
|
+
}
|
|
8744
|
+
}
|
|
8745
|
+
async writeLogArtifact(sessionId, label, cwd, content) {
|
|
8746
|
+
await ensureKimbhoDir(cwd);
|
|
8747
|
+
const logsDir = import_node_path4.default.join(resolveKimbhoDir(cwd), "logs");
|
|
8748
|
+
const outputPath = import_node_path4.default.join(logsDir, `${sessionId}-${label}.md`);
|
|
8749
|
+
await (0, import_promises4.writeFile)(outputPath, `${content}
|
|
8750
|
+
`, "utf8");
|
|
8751
|
+
return outputPath;
|
|
8752
|
+
}
|
|
8291
8753
|
};
|
|
8292
8754
|
|
|
8293
8755
|
// src/commands/agents.ts
|
|
@@ -8313,7 +8775,7 @@ function createAgentsCommand() {
|
|
|
8313
8775
|
}
|
|
8314
8776
|
|
|
8315
8777
|
// src/commands/brains.ts
|
|
8316
|
-
var
|
|
8778
|
+
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
8317
8779
|
|
|
8318
8780
|
// ../brains/dist/templates.js
|
|
8319
8781
|
var BUILTIN_PROVIDER_TEMPLATES = [
|
|
@@ -8389,8 +8851,8 @@ function buildProviderFromTemplate(templateId, options = {}) {
|
|
|
8389
8851
|
}
|
|
8390
8852
|
|
|
8391
8853
|
// ../brains/dist/registry.js
|
|
8392
|
-
var
|
|
8393
|
-
var
|
|
8854
|
+
var import_promises5 = require("node:fs/promises");
|
|
8855
|
+
var import_node_path5 = __toESM(require("node:path"), 1);
|
|
8394
8856
|
var import_node_url = require("node:url");
|
|
8395
8857
|
function resolveApiKey(definition) {
|
|
8396
8858
|
if (!definition.apiKeyEnv) {
|
|
@@ -8446,7 +8908,7 @@ function buildProviderHeaders(definition, apiKey, includeJsonContentType = true)
|
|
|
8446
8908
|
}
|
|
8447
8909
|
function filterModels(models, input = {}) {
|
|
8448
8910
|
const normalized = input.search?.trim().toLowerCase();
|
|
8449
|
-
const filtered = normalized ? models.filter((model) => [model.id, model.name
|
|
8911
|
+
const filtered = normalized ? models.filter((model) => [model.id, model.name].filter((value) => Boolean(value)).some((value) => value.toLowerCase().includes(normalized))) : models;
|
|
8450
8912
|
return typeof input.limit === "number" ? filtered.slice(0, input.limit) : filtered;
|
|
8451
8913
|
}
|
|
8452
8914
|
function mapOpenAIStyleModels(providerId, payload, input) {
|
|
@@ -8828,8 +9290,8 @@ async function createCustomModuleProvider(definition, cwd) {
|
|
|
8828
9290
|
if (!definition.modulePath) {
|
|
8829
9291
|
throw new Error(`Provider "${definition.id}" requires modulePath.`);
|
|
8830
9292
|
}
|
|
8831
|
-
const modulePath =
|
|
8832
|
-
await (0,
|
|
9293
|
+
const modulePath = import_node_path5.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path5.default.join(cwd, definition.modulePath);
|
|
9294
|
+
await (0, import_promises5.access)(modulePath);
|
|
8833
9295
|
const module2 = await import((0, import_node_url.pathToFileURL)(modulePath).href);
|
|
8834
9296
|
const createProvider = typeof module2.createProvider === "function" ? module2.createProvider : typeof module2.default === "function" ? module2.default : null;
|
|
8835
9297
|
if (!createProvider) {
|
|
@@ -8978,6 +9440,40 @@ function renderPlan(plan) {
|
|
|
8978
9440
|
function renderJson(value) {
|
|
8979
9441
|
return JSON.stringify(value, null, 2);
|
|
8980
9442
|
}
|
|
9443
|
+
function renderSession(session) {
|
|
9444
|
+
const taskCounts = countTasksByStatus(session.plan);
|
|
9445
|
+
const lines = [
|
|
9446
|
+
`Session: ${session.id}`,
|
|
9447
|
+
`Goal: ${session.goal}`,
|
|
9448
|
+
`Status: ${session.status}`,
|
|
9449
|
+
`Tasks: ${flattenPlanTasks(session.plan).length} total | completed ${taskCounts.completed} | pending ${taskCounts.pending} | blocked ${taskCounts.blocked}`,
|
|
9450
|
+
`Assigned agents: ${session.assignedAgents.join(", ") || "none"}`,
|
|
9451
|
+
`Ready tasks: ${session.readyTaskIds.join(", ") || "none"}`,
|
|
9452
|
+
`Blocked tasks: ${session.blockedTaskIds.join(", ") || "none"}`
|
|
9453
|
+
];
|
|
9454
|
+
if (session.notes.length > 0) {
|
|
9455
|
+
lines.push("");
|
|
9456
|
+
lines.push("Notes:");
|
|
9457
|
+
for (const note of session.notes.slice(-5)) {
|
|
9458
|
+
lines.push(`- ${note}`);
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
if (session.events.length > 0) {
|
|
9462
|
+
lines.push("");
|
|
9463
|
+
lines.push("Recent Events:");
|
|
9464
|
+
for (const event of session.events.slice(-5)) {
|
|
9465
|
+
const target = event.taskId ? `${event.taskId}` : "session";
|
|
9466
|
+
lines.push(`- ${event.type} | ${target} | ${event.message}`);
|
|
9467
|
+
for (const artifact of event.artifacts) {
|
|
9468
|
+
lines.push(` artifact: ${artifact}`);
|
|
9469
|
+
}
|
|
9470
|
+
for (const toolResult of event.toolResults) {
|
|
9471
|
+
lines.push(` tool: ${toolResult.toolId} | ${toolResult.success ? "ok" : "fail"} | ${toolResult.summary}`);
|
|
9472
|
+
}
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
return lines.join("\n");
|
|
9476
|
+
}
|
|
8981
9477
|
|
|
8982
9478
|
// src/commands/brains.ts
|
|
8983
9479
|
function parseNumber(value) {
|
|
@@ -8993,7 +9489,7 @@ function requireConfigMessage() {
|
|
|
8993
9489
|
function createBrainsCommand() {
|
|
8994
9490
|
const command = new Command("brains").description("Inspect and assign the LLM brains used by Kimbho roles.");
|
|
8995
9491
|
command.command("list").description("List brain-role assignments.").option("--json", "Print roles as JSON", false).action(async (options) => {
|
|
8996
|
-
const config = await loadConfig(
|
|
9492
|
+
const config = await loadConfig(import_node_process2.default.cwd());
|
|
8997
9493
|
if (!config) {
|
|
8998
9494
|
requireConfigMessage();
|
|
8999
9495
|
}
|
|
@@ -9023,7 +9519,7 @@ function createBrainsCommand() {
|
|
|
9023
9519
|
}
|
|
9024
9520
|
});
|
|
9025
9521
|
command.command("assign").description("Assign a provider/model pair to a brain role.").requiredOption("--role <role>", "Brain role: planner, coder, reviewer, or fast").requiredOption("--provider <provider>", "Provider id to assign").option("--model <model>", "Model override for this role").option("--temperature <value>", "Temperature override", parseNumber).option("--max-tokens <value>", "Max token override", parseNumber).option("--prompt-preamble <text>", "Additional prompt preamble for this role").action(async (options) => {
|
|
9026
|
-
const config = await loadConfig(
|
|
9522
|
+
const config = await loadConfig(import_node_process2.default.cwd());
|
|
9027
9523
|
if (!config) {
|
|
9028
9524
|
requireConfigMessage();
|
|
9029
9525
|
}
|
|
@@ -9047,12 +9543,12 @@ function createBrainsCommand() {
|
|
|
9047
9543
|
promptPreamble: options.promptPreamble
|
|
9048
9544
|
} : {}
|
|
9049
9545
|
});
|
|
9050
|
-
const outputPath = await saveConfig(nextConfig,
|
|
9546
|
+
const outputPath = await saveConfig(nextConfig, import_node_process2.default.cwd());
|
|
9051
9547
|
console.log(`Updated ${outputPath}`);
|
|
9052
9548
|
console.log(`Brain ${role} -> ${provider.id}/${options.model ?? provider.defaultModel ?? "model not set"}`);
|
|
9053
9549
|
});
|
|
9054
9550
|
command.command("test").description("Send a prompt through one configured brain role.").argument("<role>", "Brain role to invoke").argument("<prompt>", "Prompt to send").action(async (role, prompt) => {
|
|
9055
|
-
const config = await loadConfig(
|
|
9551
|
+
const config = await loadConfig(import_node_process2.default.cwd());
|
|
9056
9552
|
if (!config) {
|
|
9057
9553
|
requireConfigMessage();
|
|
9058
9554
|
}
|
|
@@ -9078,10 +9574,10 @@ function createBrainsCommand() {
|
|
|
9078
9574
|
}
|
|
9079
9575
|
|
|
9080
9576
|
// src/commands/doctor.ts
|
|
9081
|
-
var
|
|
9082
|
-
var
|
|
9577
|
+
var import_node_process3 = __toESM(require("node:process"), 1);
|
|
9578
|
+
var import_node_child_process2 = require("node:child_process");
|
|
9083
9579
|
function commandVersion(binary) {
|
|
9084
|
-
const result = (0,
|
|
9580
|
+
const result = (0, import_node_child_process2.spawnSync)(binary, ["--version"], {
|
|
9085
9581
|
encoding: "utf8"
|
|
9086
9582
|
});
|
|
9087
9583
|
if (result.status === 0) {
|
|
@@ -9104,19 +9600,19 @@ function createDoctorCommand() {
|
|
|
9104
9600
|
{
|
|
9105
9601
|
name: "node",
|
|
9106
9602
|
ok: true,
|
|
9107
|
-
detail:
|
|
9603
|
+
detail: import_node_process3.default.version
|
|
9108
9604
|
},
|
|
9109
9605
|
commandVersion("npm"),
|
|
9110
9606
|
commandVersion("git"),
|
|
9111
9607
|
commandVersion("docker")
|
|
9112
9608
|
];
|
|
9113
|
-
const config = await loadConfig(
|
|
9114
|
-
const registry = createDefaultBrainProviderRegistry(
|
|
9609
|
+
const config = await loadConfig(import_node_process3.default.cwd());
|
|
9610
|
+
const registry = createDefaultBrainProviderRegistry(import_node_process3.default.cwd());
|
|
9115
9611
|
if (config) {
|
|
9116
9612
|
checks.push({
|
|
9117
9613
|
name: "config",
|
|
9118
9614
|
ok: true,
|
|
9119
|
-
detail: `${resolveConfigPath(
|
|
9615
|
+
detail: `${resolveConfigPath(import_node_process3.default.cwd())} (${config.providers.length} providers)`
|
|
9120
9616
|
});
|
|
9121
9617
|
for (const provider of config.providers) {
|
|
9122
9618
|
try {
|
|
@@ -9153,7 +9649,7 @@ function createDoctorCommand() {
|
|
|
9153
9649
|
checks.push({
|
|
9154
9650
|
name: "config",
|
|
9155
9651
|
ok: false,
|
|
9156
|
-
detail: `missing (${resolveConfigPath(
|
|
9652
|
+
detail: `missing (${resolveConfigPath(import_node_process3.default.cwd())})`
|
|
9157
9653
|
});
|
|
9158
9654
|
}
|
|
9159
9655
|
for (const check of checks) {
|
|
@@ -9163,13 +9659,13 @@ function createDoctorCommand() {
|
|
|
9163
9659
|
}
|
|
9164
9660
|
|
|
9165
9661
|
// src/commands/init.ts
|
|
9166
|
-
var
|
|
9662
|
+
var import_node_process4 = __toESM(require("node:process"), 1);
|
|
9167
9663
|
function createInitCommand() {
|
|
9168
9664
|
return new Command("init").description("Create a local .kimbho/config.json file with an initial brain/provider setup.").option("--template <template>", "Provider template to use for the initial provider").option("--provider-id <id>", "Identifier for the initial provider").option("--driver <driver>", "Provider driver to use when no template is supplied").option("--label <label>", "Human-readable provider label").option("--model <model>", "Default model to use for planner/coder/reviewer brains").option("--fast-model <model>", "Model to use for the fast utility brain").option("--api-key-env <env>", "Environment variable that stores the provider API key").option("--base-url <url>", "Base URL or endpoint for the provider driver").option("--module-path <path>", "Module path for a custom provider driver").option("--force", "Overwrite an existing config file", false).action(async (options) => {
|
|
9169
|
-
const existing = await loadConfig(
|
|
9665
|
+
const existing = await loadConfig(import_node_process4.default.cwd());
|
|
9170
9666
|
if (existing && !options.force) {
|
|
9171
9667
|
console.error("Config already exists. Re-run with --force to overwrite it.");
|
|
9172
|
-
|
|
9668
|
+
import_node_process4.default.exitCode = 1;
|
|
9173
9669
|
return;
|
|
9174
9670
|
}
|
|
9175
9671
|
const templateId = options.template ?? (!options.driver ? "openai" : null);
|
|
@@ -9203,13 +9699,13 @@ function createInitCommand() {
|
|
|
9203
9699
|
defaultModel: options.model ?? provider.defaultModel,
|
|
9204
9700
|
fastModel: options.fastModel ?? options.model ?? provider.defaultModel
|
|
9205
9701
|
});
|
|
9206
|
-
const outputPath = await saveConfig(nextConfig,
|
|
9702
|
+
const outputPath = await saveConfig(nextConfig, import_node_process4.default.cwd());
|
|
9207
9703
|
console.log(`Created ${outputPath}`);
|
|
9208
9704
|
});
|
|
9209
9705
|
}
|
|
9210
9706
|
|
|
9211
9707
|
// src/commands/models.ts
|
|
9212
|
-
var
|
|
9708
|
+
var import_node_process5 = __toESM(require("node:process"), 1);
|
|
9213
9709
|
function parseInteger(value) {
|
|
9214
9710
|
const parsed = Number.parseInt(value, 10);
|
|
9215
9711
|
if (!Number.isFinite(parsed)) {
|
|
@@ -9249,18 +9745,26 @@ function renderModelLine(model) {
|
|
|
9249
9745
|
].filter((value) => Boolean(value));
|
|
9250
9746
|
return details.length > 0 ? `${model.id} | ${details.join(" | ")}` : model.id;
|
|
9251
9747
|
}
|
|
9748
|
+
function filterCachedModels(models, options) {
|
|
9749
|
+
const normalizedSearch = options.search?.trim().toLowerCase();
|
|
9750
|
+
const filtered = normalizedSearch ? models.filter((model) => model.id.toLowerCase().includes(normalizedSearch)) : models;
|
|
9751
|
+
return typeof options.limit === "number" ? filtered.slice(0, options.limit) : filtered;
|
|
9752
|
+
}
|
|
9252
9753
|
async function fetchProviderModels(provider, options) {
|
|
9253
|
-
const cachedModels =
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9754
|
+
const cachedModels = filterCachedModels(
|
|
9755
|
+
provider.models.map((modelId) => ({
|
|
9756
|
+
id: modelId,
|
|
9757
|
+
providerId: provider.id
|
|
9758
|
+
})),
|
|
9759
|
+
options
|
|
9760
|
+
);
|
|
9257
9761
|
if (options.cached) {
|
|
9258
9762
|
return {
|
|
9259
9763
|
source: "cache",
|
|
9260
9764
|
models: cachedModels
|
|
9261
9765
|
};
|
|
9262
9766
|
}
|
|
9263
|
-
const registry = createDefaultBrainProviderRegistry(
|
|
9767
|
+
const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
|
|
9264
9768
|
try {
|
|
9265
9769
|
const models = await registry.listModels(provider, {
|
|
9266
9770
|
...options.search ? {
|
|
@@ -9287,7 +9791,7 @@ async function fetchProviderModels(provider, options) {
|
|
|
9287
9791
|
function createModelsCommand() {
|
|
9288
9792
|
const command = new Command("models").description("Discover and select models from configured providers.");
|
|
9289
9793
|
command.command("list").description("List models for one provider or all configured providers.").argument("[provider]", "Optional provider id to inspect").option("--search <term>", "Filter the returned models by search text").option("--limit <count>", "Maximum number of models to print", parseInteger, 25).option("--cached", "Use cached models from config instead of remote discovery", false).option("--sync", "Persist discovered model ids back into config", false).option("--json", "Print models as JSON", false).action(async (providerId, options) => {
|
|
9290
|
-
let config = await loadConfig(
|
|
9794
|
+
let config = await loadConfig(import_node_process5.default.cwd());
|
|
9291
9795
|
if (!config) {
|
|
9292
9796
|
requireConfigMessage2();
|
|
9293
9797
|
}
|
|
@@ -9316,7 +9820,7 @@ function createModelsCommand() {
|
|
|
9316
9820
|
}
|
|
9317
9821
|
}
|
|
9318
9822
|
if (mutated) {
|
|
9319
|
-
await saveConfig(config,
|
|
9823
|
+
await saveConfig(config, import_node_process5.default.cwd());
|
|
9320
9824
|
}
|
|
9321
9825
|
if (options.json) {
|
|
9322
9826
|
console.log(renderJson(groups));
|
|
@@ -9333,7 +9837,7 @@ function createModelsCommand() {
|
|
|
9333
9837
|
}
|
|
9334
9838
|
});
|
|
9335
9839
|
command.command("sync").description("Fetch remote models for one provider and cache the ids in config.").argument("<provider>", "Provider id to sync").option("--search <term>", "Optional search filter to limit what gets cached").option("--limit <count>", "Maximum number of models to cache", parseInteger, 200).action(async (providerId, options) => {
|
|
9336
|
-
const config = await loadConfig(
|
|
9840
|
+
const config = await loadConfig(import_node_process5.default.cwd());
|
|
9337
9841
|
if (!config) {
|
|
9338
9842
|
requireConfigMessage2();
|
|
9339
9843
|
}
|
|
@@ -9341,7 +9845,7 @@ function createModelsCommand() {
|
|
|
9341
9845
|
if (!provider) {
|
|
9342
9846
|
throw new Error(`Unknown provider "${providerId}".`);
|
|
9343
9847
|
}
|
|
9344
|
-
const registry = createDefaultBrainProviderRegistry(
|
|
9848
|
+
const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
|
|
9345
9849
|
const models = await registry.listModels(provider, {
|
|
9346
9850
|
...options.search ? {
|
|
9347
9851
|
search: options.search
|
|
@@ -9352,12 +9856,12 @@ function createModelsCommand() {
|
|
|
9352
9856
|
...provider,
|
|
9353
9857
|
models: Array.from(new Set(models.map((model) => model.id)))
|
|
9354
9858
|
});
|
|
9355
|
-
const outputPath = await saveConfig(nextConfig,
|
|
9859
|
+
const outputPath = await saveConfig(nextConfig, import_node_process5.default.cwd());
|
|
9356
9860
|
console.log(`Updated ${outputPath}`);
|
|
9357
9861
|
console.log(`Synced ${models.length} models for ${provider.id}`);
|
|
9358
9862
|
});
|
|
9359
9863
|
command.command("use").description("Select a model for a provider and optionally assign it to a brain role.").argument("<model>", "Model id to select").requiredOption("--provider <provider>", "Provider id to use").option("--role <role>", "Brain role to assign: planner, coder, reviewer, or fast").option("--set-default", "Also set this model as the provider default", false).option("--force", "Skip remote model validation", false).option("--temperature <value>", "Temperature override for the role", parseNumber2).option("--max-tokens <value>", "Max token override for the role", parseInteger).action(async (model, options) => {
|
|
9360
|
-
let config = await loadConfig(
|
|
9864
|
+
let config = await loadConfig(import_node_process5.default.cwd());
|
|
9361
9865
|
if (!config) {
|
|
9362
9866
|
requireConfigMessage2();
|
|
9363
9867
|
}
|
|
@@ -9367,7 +9871,7 @@ function createModelsCommand() {
|
|
|
9367
9871
|
}
|
|
9368
9872
|
if (!options.force) {
|
|
9369
9873
|
try {
|
|
9370
|
-
const registry = createDefaultBrainProviderRegistry(
|
|
9874
|
+
const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
|
|
9371
9875
|
const models = await registry.listModels(provider, {
|
|
9372
9876
|
search: model,
|
|
9373
9877
|
limit: 500
|
|
@@ -9407,7 +9911,7 @@ function createModelsCommand() {
|
|
|
9407
9911
|
} : {}
|
|
9408
9912
|
});
|
|
9409
9913
|
}
|
|
9410
|
-
const outputPath = await saveConfig(config,
|
|
9914
|
+
const outputPath = await saveConfig(config, import_node_process5.default.cwd());
|
|
9411
9915
|
console.log(`Updated ${outputPath}`);
|
|
9412
9916
|
console.log(`Selected ${provider.id}/${model}`);
|
|
9413
9917
|
if (options.role) {
|
|
@@ -9421,8 +9925,8 @@ function createModelsCommand() {
|
|
|
9421
9925
|
}
|
|
9422
9926
|
|
|
9423
9927
|
// src/commands/plan.ts
|
|
9424
|
-
var
|
|
9425
|
-
var
|
|
9928
|
+
var import_promises6 = require("node:fs/promises");
|
|
9929
|
+
var import_node_process6 = __toESM(require("node:process"), 1);
|
|
9426
9930
|
|
|
9427
9931
|
// ../planner/dist/planner.js
|
|
9428
9932
|
function normalizeGoal(goal) {
|
|
@@ -9726,7 +10230,7 @@ function createPlan(input) {
|
|
|
9726
10230
|
|
|
9727
10231
|
// src/commands/plan.ts
|
|
9728
10232
|
async function detectWorkspaceState(cwd) {
|
|
9729
|
-
const entries = await (0,
|
|
10233
|
+
const entries = await (0, import_promises6.readdir)(cwd);
|
|
9730
10234
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
9731
10235
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
9732
10236
|
}
|
|
@@ -9737,7 +10241,7 @@ function createPlanCommand() {
|
|
|
9737
10241
|
(value, previous = []) => [...previous, value],
|
|
9738
10242
|
[]
|
|
9739
10243
|
).option("--json", "Print the plan as JSON", false).action(async (goal, options) => {
|
|
9740
|
-
const cwd =
|
|
10244
|
+
const cwd = import_node_process6.default.cwd();
|
|
9741
10245
|
const request = {
|
|
9742
10246
|
goal,
|
|
9743
10247
|
mode: "plan",
|
|
@@ -9774,14 +10278,14 @@ function createPlanCommand() {
|
|
|
9774
10278
|
}
|
|
9775
10279
|
|
|
9776
10280
|
// src/commands/providers.ts
|
|
9777
|
-
var
|
|
10281
|
+
var import_node_process7 = __toESM(require("node:process"), 1);
|
|
9778
10282
|
function requireConfigMessage3() {
|
|
9779
10283
|
throw new Error("No config found. Run `kimbho init` first.");
|
|
9780
10284
|
}
|
|
9781
10285
|
function createProvidersCommand() {
|
|
9782
10286
|
const command = new Command("providers").description("Manage configured model providers and built-in templates.");
|
|
9783
10287
|
command.command("list").description("List configured providers.").option("--json", "Print providers as JSON", false).action(async (options) => {
|
|
9784
|
-
const config = await loadConfig(
|
|
10288
|
+
const config = await loadConfig(import_node_process7.default.cwd());
|
|
9785
10289
|
if (!config) {
|
|
9786
10290
|
requireConfigMessage3();
|
|
9787
10291
|
}
|
|
@@ -9822,7 +10326,7 @@ function createProvidersCommand() {
|
|
|
9822
10326
|
(value, previous = []) => [...previous, value],
|
|
9823
10327
|
[]
|
|
9824
10328
|
).option("--module-path <path>", "Module path for a custom provider driver").action(async (options) => {
|
|
9825
|
-
const config = await loadConfig(
|
|
10329
|
+
const config = await loadConfig(import_node_process7.default.cwd());
|
|
9826
10330
|
if (!config) {
|
|
9827
10331
|
requireConfigMessage3();
|
|
9828
10332
|
}
|
|
@@ -9856,16 +10360,16 @@ function createProvidersCommand() {
|
|
|
9856
10360
|
} : {}
|
|
9857
10361
|
});
|
|
9858
10362
|
const nextConfig = upsertProvider(config, provider);
|
|
9859
|
-
const outputPath = await saveConfig(nextConfig,
|
|
10363
|
+
const outputPath = await saveConfig(nextConfig, import_node_process7.default.cwd());
|
|
9860
10364
|
console.log(`Updated ${outputPath}`);
|
|
9861
10365
|
console.log(`Provider ${provider.id} -> ${provider.driver}`);
|
|
9862
10366
|
});
|
|
9863
10367
|
command.command("check").description("Run health checks for the configured providers.").action(async () => {
|
|
9864
|
-
const config = await loadConfig(
|
|
10368
|
+
const config = await loadConfig(import_node_process7.default.cwd());
|
|
9865
10369
|
if (!config) {
|
|
9866
10370
|
requireConfigMessage3();
|
|
9867
10371
|
}
|
|
9868
|
-
const registry = createDefaultBrainProviderRegistry(
|
|
10372
|
+
const registry = createDefaultBrainProviderRegistry(import_node_process7.default.cwd());
|
|
9869
10373
|
for (const provider of config.providers) {
|
|
9870
10374
|
try {
|
|
9871
10375
|
const result = await registry.healthCheck(provider);
|
|
@@ -9880,52 +10384,67 @@ function createProvidersCommand() {
|
|
|
9880
10384
|
}
|
|
9881
10385
|
|
|
9882
10386
|
// src/commands/resume.ts
|
|
9883
|
-
var
|
|
10387
|
+
var import_node_process8 = __toESM(require("node:process"), 1);
|
|
10388
|
+
function parseCount(value) {
|
|
10389
|
+
const parsed = Number(value);
|
|
10390
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
10391
|
+
throw new Error(`Expected a positive integer, received "${value}".`);
|
|
10392
|
+
}
|
|
10393
|
+
return parsed;
|
|
10394
|
+
}
|
|
9884
10395
|
function createResumeCommand() {
|
|
9885
|
-
return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).action(async (options) => {
|
|
9886
|
-
const
|
|
10396
|
+
return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).option("--execute", "Continue auto-executing the ready-task frontier", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount, 2).action(async (options) => {
|
|
10397
|
+
const cwd = import_node_process8.default.cwd();
|
|
10398
|
+
const session = await loadLatestSession(cwd);
|
|
9887
10399
|
if (!session) {
|
|
9888
10400
|
console.error("No saved session found. Run `kimbho run` first.");
|
|
9889
|
-
|
|
10401
|
+
import_node_process8.default.exitCode = 1;
|
|
9890
10402
|
return;
|
|
9891
10403
|
}
|
|
10404
|
+
const snapshot = options.execute ? await new ExecutionOrchestrator().continueSession(session, {
|
|
10405
|
+
maxAutoTasks: options.maxAutoTasks
|
|
10406
|
+
}) : session;
|
|
10407
|
+
if (options.execute) {
|
|
10408
|
+
await saveSession(snapshot, cwd);
|
|
10409
|
+
}
|
|
9892
10410
|
if (options.json) {
|
|
9893
|
-
console.log(renderJson(
|
|
10411
|
+
console.log(renderJson(snapshot));
|
|
9894
10412
|
return;
|
|
9895
10413
|
}
|
|
9896
|
-
console.log(
|
|
9897
|
-
console.log(`Goal: ${session.goal}`);
|
|
9898
|
-
console.log(`Status: ${session.status}`);
|
|
9899
|
-
console.log(`Ready tasks: ${session.readyTaskIds.join(", ") || "none"}`);
|
|
9900
|
-
console.log(`Blocked tasks: ${session.blockedTaskIds.join(", ") || "none"}`);
|
|
9901
|
-
console.log(`Assigned agents: ${session.assignedAgents.join(", ") || "none"}`);
|
|
9902
|
-
console.log(`Notes: ${session.notes.join(" | ") || "none"}`);
|
|
10414
|
+
console.log(renderSession(snapshot));
|
|
9903
10415
|
});
|
|
9904
10416
|
}
|
|
9905
10417
|
|
|
9906
10418
|
// src/commands/run.ts
|
|
9907
|
-
var
|
|
9908
|
-
var
|
|
10419
|
+
var import_promises7 = require("node:fs/promises");
|
|
10420
|
+
var import_node_process9 = __toESM(require("node:process"), 1);
|
|
9909
10421
|
async function detectWorkspaceState2(cwd) {
|
|
9910
|
-
const entries = await (0,
|
|
10422
|
+
const entries = await (0, import_promises7.readdir)(cwd);
|
|
9911
10423
|
const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
|
|
9912
10424
|
return meaningfulEntries.length === 0 ? "empty" : "existing";
|
|
9913
10425
|
}
|
|
10426
|
+
function parseCount2(value) {
|
|
10427
|
+
const parsed = Number(value);
|
|
10428
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
10429
|
+
throw new Error(`Expected a positive integer, received "${value}".`);
|
|
10430
|
+
}
|
|
10431
|
+
return parsed;
|
|
10432
|
+
}
|
|
9914
10433
|
function createRunCommand() {
|
|
9915
10434
|
return new Command("run").description("Create or load a plan, then open a runnable Kimbho session snapshot.").argument("[goal]", "Optional goal; if omitted, Kimbho will use the latest saved plan").option("--stack <stack>", "Preferred stack or preset").option(
|
|
9916
10435
|
"--constraint <constraint>",
|
|
9917
10436
|
"Explicit execution constraint; can be provided multiple times",
|
|
9918
10437
|
(value, previous = []) => [...previous, value],
|
|
9919
10438
|
[]
|
|
9920
|
-
).option("--json", "Print the session snapshot as JSON", false).action(async (goal, options) => {
|
|
9921
|
-
const cwd =
|
|
10439
|
+
).option("--json", "Print the session snapshot as JSON", false).option("--snapshot-only", "Create the session without auto-executing ready tasks", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount2, 2).action(async (goal, options) => {
|
|
10440
|
+
const cwd = import_node_process9.default.cwd();
|
|
9922
10441
|
const orchestrator = new ExecutionOrchestrator();
|
|
9923
10442
|
let request;
|
|
9924
10443
|
let planPath = null;
|
|
9925
10444
|
let plan = goal ? null : await loadLatestPlan(cwd);
|
|
9926
10445
|
if (!plan && !goal) {
|
|
9927
10446
|
console.error("No saved plan found. Pass a goal or run `kimbho plan` first.");
|
|
9928
|
-
|
|
10447
|
+
import_node_process9.default.exitCode = 1;
|
|
9929
10448
|
return;
|
|
9930
10449
|
}
|
|
9931
10450
|
if (goal) {
|
|
@@ -9950,7 +10469,10 @@ function createRunCommand() {
|
|
|
9950
10469
|
};
|
|
9951
10470
|
}
|
|
9952
10471
|
const envelope = orchestrator.buildEnvelope(request, plan);
|
|
9953
|
-
const
|
|
10472
|
+
const initialSnapshot = orchestrator.createSessionSnapshot(envelope);
|
|
10473
|
+
const snapshot = options.snapshotOnly ? initialSnapshot : await orchestrator.continueSession(initialSnapshot, {
|
|
10474
|
+
maxAutoTasks: options.maxAutoTasks
|
|
10475
|
+
});
|
|
9954
10476
|
const sessionPath = await saveSession(snapshot, cwd);
|
|
9955
10477
|
if (options.json) {
|
|
9956
10478
|
console.log(
|
|
@@ -9964,15 +10486,12 @@ function createRunCommand() {
|
|
|
9964
10486
|
}
|
|
9965
10487
|
console.log(renderPlan(plan));
|
|
9966
10488
|
console.log("");
|
|
9967
|
-
console.log(
|
|
9968
|
-
console.log(
|
|
10489
|
+
console.log(renderSession(snapshot));
|
|
10490
|
+
console.log("");
|
|
9969
10491
|
if (planPath) {
|
|
9970
10492
|
console.log(`Saved plan: ${planPath}`);
|
|
9971
10493
|
}
|
|
9972
|
-
console.log(`
|
|
9973
|
-
console.log(`Ready tasks: ${snapshot.readyTaskIds.join(", ") || "none"}`);
|
|
9974
|
-
console.log(`Blocked tasks: ${snapshot.blockedTaskIds.join(", ") || "none"}`);
|
|
9975
|
-
console.log("Next step: wire executors into `run` so ready tasks can be acted on instead of only snapshotted.");
|
|
10494
|
+
console.log(`Saved session: ${sessionPath}`);
|
|
9976
10495
|
});
|
|
9977
10496
|
}
|
|
9978
10497
|
|
|
@@ -10040,14 +10559,15 @@ function createProgram(onOpenShell) {
|
|
|
10040
10559
|
}
|
|
10041
10560
|
|
|
10042
10561
|
// src/shell.ts
|
|
10043
|
-
var
|
|
10044
|
-
var
|
|
10562
|
+
var import_promises8 = require("node:readline/promises");
|
|
10563
|
+
var import_node_process10 = __toESM(require("node:process"), 1);
|
|
10045
10564
|
var AMBER = "\x1B[38;5;214m";
|
|
10046
10565
|
var BOLD = "\x1B[1m";
|
|
10047
10566
|
var DIM = "\x1B[2m";
|
|
10048
10567
|
var RESET = "\x1B[0m";
|
|
10049
10568
|
var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
|
|
10050
10569
|
"agents",
|
|
10570
|
+
"brain",
|
|
10051
10571
|
"brains",
|
|
10052
10572
|
"doctor",
|
|
10053
10573
|
"fix",
|
|
@@ -10057,15 +10577,30 @@ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
10057
10577
|
"models",
|
|
10058
10578
|
"new",
|
|
10059
10579
|
"plan",
|
|
10580
|
+
"provider",
|
|
10060
10581
|
"providers",
|
|
10061
10582
|
"quit",
|
|
10062
10583
|
"resume",
|
|
10063
10584
|
"review",
|
|
10064
10585
|
"run",
|
|
10065
10586
|
"scaffold",
|
|
10587
|
+
"select",
|
|
10066
10588
|
"shell",
|
|
10067
|
-
"status"
|
|
10589
|
+
"status",
|
|
10590
|
+
"use-model"
|
|
10068
10591
|
]);
|
|
10592
|
+
var MODEL_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
10593
|
+
"help",
|
|
10594
|
+
"list",
|
|
10595
|
+
"sync",
|
|
10596
|
+
"use"
|
|
10597
|
+
]);
|
|
10598
|
+
var BRAIN_ROLES = [
|
|
10599
|
+
"planner",
|
|
10600
|
+
"coder",
|
|
10601
|
+
"reviewer",
|
|
10602
|
+
"fast"
|
|
10603
|
+
];
|
|
10069
10604
|
function color(code, value) {
|
|
10070
10605
|
return `${code}${value}${RESET}`;
|
|
10071
10606
|
}
|
|
@@ -10078,12 +10613,14 @@ function renderBanner() {
|
|
|
10078
10613
|
"|_|\\_\\_|_| |_|____/|____/|_| |_|\\___/ "
|
|
10079
10614
|
].map((line) => color(AMBER, line)).join("\n");
|
|
10080
10615
|
}
|
|
10081
|
-
async function getShellSessionState(cwd) {
|
|
10616
|
+
async function getShellSessionState(cwd, focusRole) {
|
|
10082
10617
|
const config = await loadConfig(cwd);
|
|
10083
10618
|
if (!config) {
|
|
10084
10619
|
return {
|
|
10620
|
+
focusRole,
|
|
10085
10621
|
providerLabel: "unconfigured",
|
|
10086
10622
|
providerId: "-",
|
|
10623
|
+
focusModel: "not set",
|
|
10087
10624
|
coderModel: "not set",
|
|
10088
10625
|
plannerModel: "not set",
|
|
10089
10626
|
approvalMode: "manual",
|
|
@@ -10092,11 +10629,13 @@ async function getShellSessionState(cwd) {
|
|
|
10092
10629
|
configured: false
|
|
10093
10630
|
};
|
|
10094
10631
|
}
|
|
10095
|
-
const
|
|
10096
|
-
const provider = findProviderById(config,
|
|
10632
|
+
const focusSettings = config.brains[focusRole];
|
|
10633
|
+
const provider = findProviderById(config, focusSettings.providerId);
|
|
10097
10634
|
return {
|
|
10635
|
+
focusRole,
|
|
10098
10636
|
providerLabel: provider?.label ?? provider?.id ?? "unknown",
|
|
10099
|
-
providerId: provider?.id ??
|
|
10637
|
+
providerId: provider?.id ?? focusSettings.providerId,
|
|
10638
|
+
focusModel: resolveBrainModel(config, focusRole) ?? "not set",
|
|
10100
10639
|
coderModel: resolveBrainModel(config, "coder") ?? "not set",
|
|
10101
10640
|
plannerModel: resolveBrainModel(config, "planner") ?? "not set",
|
|
10102
10641
|
approvalMode: config.approvalMode,
|
|
@@ -10129,20 +10668,24 @@ function renderBox(lines) {
|
|
|
10129
10668
|
function renderHelp() {
|
|
10130
10669
|
return [
|
|
10131
10670
|
`${color(DIM, "Commands")}`,
|
|
10132
|
-
"/status
|
|
10133
|
-
"/
|
|
10134
|
-
"/
|
|
10135
|
-
"/
|
|
10136
|
-
"/
|
|
10137
|
-
"/
|
|
10138
|
-
"/
|
|
10139
|
-
"/providers
|
|
10140
|
-
"/
|
|
10141
|
-
"/
|
|
10142
|
-
"/
|
|
10143
|
-
"/
|
|
10144
|
-
"/
|
|
10145
|
-
"/
|
|
10671
|
+
"/status Show the active role, provider, and workspace state.",
|
|
10672
|
+
"/brain <role> Change shell focus to planner, coder, reviewer, or fast.",
|
|
10673
|
+
"/model Show current brain assignments.",
|
|
10674
|
+
"/providers List configured providers.",
|
|
10675
|
+
"/providers templates Show built-in provider templates.",
|
|
10676
|
+
"/providers add <tpl> [id] Add a provider from a built-in template.",
|
|
10677
|
+
"/providers use <id> [role] Use a provider for the active role.",
|
|
10678
|
+
"/providers check Run provider health checks.",
|
|
10679
|
+
"/models [search] Discover models for the active role's provider.",
|
|
10680
|
+
"/select <n> Select a model from the last numbered /models list.",
|
|
10681
|
+
"/use-model <id> Assign a model to the active role and provider.",
|
|
10682
|
+
"/plan <goal> Create a structured implementation plan.",
|
|
10683
|
+
"/run <goal> Start a Kimbho execution session for a goal.",
|
|
10684
|
+
"/resume Show the latest saved session.",
|
|
10685
|
+
"/agents Inspect agent roles and the active session.",
|
|
10686
|
+
"/doctor Check local environment and config.",
|
|
10687
|
+
"/clear Redraw the shell.",
|
|
10688
|
+
"/quit, /exit Leave the shell.",
|
|
10146
10689
|
"",
|
|
10147
10690
|
`${color(DIM, "Tip")}`,
|
|
10148
10691
|
"Type a natural-language goal directly and Kimbho will treat it as /run <goal>."
|
|
@@ -10151,6 +10694,8 @@ function renderHelp() {
|
|
|
10151
10694
|
function renderStartupCard(cwd, state) {
|
|
10152
10695
|
const cardLines = [
|
|
10153
10696
|
`${color(BOLD, "Kimbho CLI")} (v${KIMBHO_VERSION})`,
|
|
10697
|
+
renderCardLine("focus", state.focusRole),
|
|
10698
|
+
renderCardLine("model", state.focusModel),
|
|
10154
10699
|
renderCardLine("coder", state.coderModel),
|
|
10155
10700
|
renderCardLine("planner", state.plannerModel),
|
|
10156
10701
|
renderCardLine("provider", `${state.providerLabel} [${state.providerId}]`),
|
|
@@ -10158,23 +10703,23 @@ function renderStartupCard(cwd, state) {
|
|
|
10158
10703
|
renderCardLine("approval", state.approvalMode),
|
|
10159
10704
|
renderCardLine("sandbox", state.sandboxMode),
|
|
10160
10705
|
renderCardLine("preset", state.stackPreset),
|
|
10161
|
-
renderCardLine("shortcuts", "/
|
|
10706
|
+
renderCardLine("shortcuts", "/brain /providers /models /select /use-model /quit")
|
|
10162
10707
|
];
|
|
10163
10708
|
if (!state.configured) {
|
|
10164
|
-
cardLines.push("setup: run /init to create .kimbho/config.json");
|
|
10709
|
+
cardLines.push("setup: run /init or /providers add <template> to create .kimbho/config.json");
|
|
10165
10710
|
}
|
|
10166
10711
|
return renderBox(cardLines);
|
|
10167
10712
|
}
|
|
10168
10713
|
function formatPrompt(state) {
|
|
10169
|
-
const
|
|
10170
|
-
return `${color(AMBER, "kimbho")} ${color(DIM, `[${
|
|
10714
|
+
const model = state.focusModel === "not set" ? "unconfigured" : shortenMiddle(state.focusModel, 18);
|
|
10715
|
+
return `${color(AMBER, "kimbho")} ${color(DIM, `[${state.focusRole}:${model}]`)} > `;
|
|
10171
10716
|
}
|
|
10172
10717
|
function printHeader(cwd, state) {
|
|
10173
10718
|
console.log(renderBanner());
|
|
10174
10719
|
console.log(color(DIM, "Terminal-native coding agent"));
|
|
10175
10720
|
console.log(renderStartupCard(cwd, state));
|
|
10176
10721
|
console.log("");
|
|
10177
|
-
console.log(color(DIM, "Tip:
|
|
10722
|
+
console.log(color(DIM, "Tip: configure providers and models here, then describe the work and Kimbho will route it into /run."));
|
|
10178
10723
|
console.log(renderHelp());
|
|
10179
10724
|
console.log("");
|
|
10180
10725
|
}
|
|
@@ -10225,21 +10770,399 @@ function tokenizeInput(input) {
|
|
|
10225
10770
|
}
|
|
10226
10771
|
return tokens;
|
|
10227
10772
|
}
|
|
10228
|
-
function
|
|
10773
|
+
function normalizeInputTokens(input) {
|
|
10229
10774
|
const trimmed = input.trim();
|
|
10230
10775
|
if (!trimmed) {
|
|
10231
|
-
return
|
|
10776
|
+
return {
|
|
10777
|
+
normalizedInput: "",
|
|
10778
|
+
tokens: [],
|
|
10779
|
+
head: null
|
|
10780
|
+
};
|
|
10232
10781
|
}
|
|
10233
10782
|
const normalizedInput = trimmed.startsWith("kimbho ") ? trimmed.slice("kimbho ".length).trim() : trimmed;
|
|
10234
10783
|
const tokens = tokenizeInput(normalizedInput);
|
|
10235
|
-
|
|
10784
|
+
const firstToken = tokens[0];
|
|
10785
|
+
if (!firstToken) {
|
|
10786
|
+
return {
|
|
10787
|
+
normalizedInput,
|
|
10788
|
+
tokens,
|
|
10789
|
+
head: null
|
|
10790
|
+
};
|
|
10791
|
+
}
|
|
10792
|
+
return {
|
|
10793
|
+
normalizedInput,
|
|
10794
|
+
tokens,
|
|
10795
|
+
head: firstToken.startsWith("/") ? firstToken.slice(1) : firstToken
|
|
10796
|
+
};
|
|
10797
|
+
}
|
|
10798
|
+
function defaultProviderIdForTemplate(templateId) {
|
|
10799
|
+
switch (templateId) {
|
|
10800
|
+
case "openai":
|
|
10801
|
+
return "openai-main";
|
|
10802
|
+
case "anthropic":
|
|
10803
|
+
return "anthropic-main";
|
|
10804
|
+
case "openrouter":
|
|
10805
|
+
return "openrouter-main";
|
|
10806
|
+
case "ollama":
|
|
10807
|
+
return "ollama-local";
|
|
10808
|
+
case "lmstudio":
|
|
10809
|
+
return "lmstudio-local";
|
|
10810
|
+
default:
|
|
10811
|
+
return `${templateId}-main`;
|
|
10812
|
+
}
|
|
10813
|
+
}
|
|
10814
|
+
function buildCachedModels(provider, search, limit = 25) {
|
|
10815
|
+
const normalized = search?.trim().toLowerCase();
|
|
10816
|
+
const filtered = normalized ? provider.models.filter((modelId) => modelId.toLowerCase().includes(normalized)) : provider.models;
|
|
10817
|
+
return filtered.slice(0, limit).map((modelId) => ({
|
|
10818
|
+
id: modelId,
|
|
10819
|
+
providerId: provider.id
|
|
10820
|
+
}));
|
|
10821
|
+
}
|
|
10822
|
+
async function fetchModelsForProvider(cwd, config, provider, search, limit = 25) {
|
|
10823
|
+
const registry = createDefaultBrainProviderRegistry(cwd);
|
|
10824
|
+
const cachedModels = buildCachedModels(provider, search, limit);
|
|
10825
|
+
try {
|
|
10826
|
+
const models = await registry.listModels(provider, {
|
|
10827
|
+
...search ? {
|
|
10828
|
+
search
|
|
10829
|
+
} : {},
|
|
10830
|
+
limit
|
|
10831
|
+
});
|
|
10832
|
+
const nextConfig = upsertProvider(config, {
|
|
10833
|
+
...provider,
|
|
10834
|
+
models: Array.from(/* @__PURE__ */ new Set([
|
|
10835
|
+
...provider.models,
|
|
10836
|
+
...models.map((model) => model.id)
|
|
10837
|
+
]))
|
|
10838
|
+
});
|
|
10839
|
+
if (nextConfig !== config) {
|
|
10840
|
+
await saveConfig(nextConfig, cwd);
|
|
10841
|
+
}
|
|
10842
|
+
return {
|
|
10843
|
+
config: nextConfig,
|
|
10844
|
+
source: "remote",
|
|
10845
|
+
models
|
|
10846
|
+
};
|
|
10847
|
+
} catch (error) {
|
|
10848
|
+
if (cachedModels.length > 0) {
|
|
10849
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10850
|
+
return {
|
|
10851
|
+
config,
|
|
10852
|
+
source: "cache",
|
|
10853
|
+
note: `Using cached models because remote discovery failed: ${message}`,
|
|
10854
|
+
models: cachedModels
|
|
10855
|
+
};
|
|
10856
|
+
}
|
|
10857
|
+
throw error;
|
|
10858
|
+
}
|
|
10859
|
+
}
|
|
10860
|
+
async function assignModelToRole(cwd, role, providerId, model) {
|
|
10861
|
+
const config = await loadConfig(cwd);
|
|
10862
|
+
if (!config) {
|
|
10863
|
+
throw new Error("No config found. Run /init or /providers add first.");
|
|
10864
|
+
}
|
|
10865
|
+
const provider = findProviderById(config, providerId);
|
|
10866
|
+
if (!provider) {
|
|
10867
|
+
throw new Error(`Unknown provider "${providerId}".`);
|
|
10868
|
+
}
|
|
10869
|
+
const currentSettings = resolveBrainSettings(config, role);
|
|
10870
|
+
const nextConfig = assignBrain(
|
|
10871
|
+
upsertProvider(config, {
|
|
10872
|
+
...provider,
|
|
10873
|
+
defaultModel: model,
|
|
10874
|
+
models: Array.from(/* @__PURE__ */ new Set([
|
|
10875
|
+
...provider.models,
|
|
10876
|
+
model
|
|
10877
|
+
]))
|
|
10878
|
+
}),
|
|
10879
|
+
role,
|
|
10880
|
+
{
|
|
10881
|
+
providerId,
|
|
10882
|
+
model,
|
|
10883
|
+
...typeof currentSettings.temperature === "number" ? {
|
|
10884
|
+
temperature: currentSettings.temperature
|
|
10885
|
+
} : {},
|
|
10886
|
+
...typeof currentSettings.maxTokens === "number" ? {
|
|
10887
|
+
maxTokens: currentSettings.maxTokens
|
|
10888
|
+
} : {},
|
|
10889
|
+
...currentSettings.promptPreamble ? {
|
|
10890
|
+
promptPreamble: currentSettings.promptPreamble
|
|
10891
|
+
} : {}
|
|
10892
|
+
}
|
|
10893
|
+
);
|
|
10894
|
+
const outputPath = await saveConfig(nextConfig, cwd);
|
|
10895
|
+
return outputPath;
|
|
10896
|
+
}
|
|
10897
|
+
async function useProviderForRole(cwd, role, providerId) {
|
|
10898
|
+
const config = await loadConfig(cwd);
|
|
10899
|
+
if (!config) {
|
|
10900
|
+
throw new Error("No config found. Run /init or /providers add first.");
|
|
10901
|
+
}
|
|
10902
|
+
const provider = findProviderById(config, providerId);
|
|
10903
|
+
if (!provider) {
|
|
10904
|
+
throw new Error(`Unknown provider "${providerId}".`);
|
|
10905
|
+
}
|
|
10906
|
+
const currentSettings = resolveBrainSettings(config, role);
|
|
10907
|
+
const resolvedModel = provider.defaultModel ?? provider.models[0] ?? null;
|
|
10908
|
+
const nextConfig = assignBrain(config, role, {
|
|
10909
|
+
providerId: provider.id,
|
|
10910
|
+
...resolvedModel ? {
|
|
10911
|
+
model: resolvedModel
|
|
10912
|
+
} : {},
|
|
10913
|
+
...typeof currentSettings.temperature === "number" ? {
|
|
10914
|
+
temperature: currentSettings.temperature
|
|
10915
|
+
} : {},
|
|
10916
|
+
...typeof currentSettings.maxTokens === "number" ? {
|
|
10917
|
+
maxTokens: currentSettings.maxTokens
|
|
10918
|
+
} : {},
|
|
10919
|
+
...currentSettings.promptPreamble ? {
|
|
10920
|
+
promptPreamble: currentSettings.promptPreamble
|
|
10921
|
+
} : {}
|
|
10922
|
+
});
|
|
10923
|
+
const outputPath = await saveConfig(nextConfig, cwd);
|
|
10924
|
+
return {
|
|
10925
|
+
outputPath,
|
|
10926
|
+
model: resolvedModel
|
|
10927
|
+
};
|
|
10928
|
+
}
|
|
10929
|
+
function renderModelLine2(model) {
|
|
10930
|
+
const details = [
|
|
10931
|
+
model.name,
|
|
10932
|
+
model.contextLength ? `ctx ${model.contextLength}` : null,
|
|
10933
|
+
model.promptPrice ? `in ${model.promptPrice}` : null,
|
|
10934
|
+
model.completionPrice ? `out ${model.completionPrice}` : null,
|
|
10935
|
+
model.modality
|
|
10936
|
+
].filter((value) => Boolean(value));
|
|
10937
|
+
return details.length > 0 ? `${model.id} | ${details.join(" | ")}` : model.id;
|
|
10938
|
+
}
|
|
10939
|
+
async function printProviderList(cwd, focusRole) {
|
|
10940
|
+
const config = await loadConfig(cwd);
|
|
10941
|
+
if (!config) {
|
|
10942
|
+
console.log("No config found. Run /init or /providers add <template> first.");
|
|
10943
|
+
return;
|
|
10944
|
+
}
|
|
10945
|
+
const activeProviderId = config.brains[focusRole].providerId;
|
|
10946
|
+
for (const provider of config.providers) {
|
|
10947
|
+
const marker = provider.id === activeProviderId ? "*" : " ";
|
|
10948
|
+
const envState = provider.apiKeyEnv ? import_node_process10.default.env[provider.apiKeyEnv] ? "present" : `missing ${provider.apiKeyEnv}` : "no key required";
|
|
10949
|
+
console.log(`${marker} ${provider.id}`);
|
|
10950
|
+
console.log(` label: ${provider.label ?? "-"}`);
|
|
10951
|
+
console.log(` driver: ${provider.driver}`);
|
|
10952
|
+
console.log(` model: ${provider.defaultModel ?? "-"}`);
|
|
10953
|
+
console.log(` auth: ${envState}`);
|
|
10954
|
+
console.log(` cachedModels: ${provider.models.length}`);
|
|
10955
|
+
}
|
|
10956
|
+
}
|
|
10957
|
+
function printProviderTemplates() {
|
|
10958
|
+
for (const template of listProviderTemplates()) {
|
|
10959
|
+
console.log(`${template.id}`);
|
|
10960
|
+
console.log(` label: ${template.label}`);
|
|
10961
|
+
console.log(` driver: ${template.driver}`);
|
|
10962
|
+
console.log(` apiKeyEnv: ${template.defaultApiKeyEnv ?? "-"}`);
|
|
10963
|
+
console.log(` model: ${template.defaultModel ?? "-"}`);
|
|
10964
|
+
console.log(` notes: ${template.notes}`);
|
|
10965
|
+
}
|
|
10966
|
+
}
|
|
10967
|
+
async function addProviderFromTemplate(cwd, templateId, providerId) {
|
|
10968
|
+
const provider = buildProviderFromTemplate(templateId, {
|
|
10969
|
+
providerId: providerId ?? defaultProviderIdForTemplate(templateId)
|
|
10970
|
+
});
|
|
10971
|
+
const config = await loadConfig(cwd);
|
|
10972
|
+
if (!config) {
|
|
10973
|
+
const outputPath = await saveConfig(createDefaultConfig({ provider }), cwd);
|
|
10974
|
+
return outputPath;
|
|
10975
|
+
}
|
|
10976
|
+
return saveConfig(upsertProvider(config, provider), cwd);
|
|
10977
|
+
}
|
|
10978
|
+
async function printProviderHealth(cwd) {
|
|
10979
|
+
const config = await loadConfig(cwd);
|
|
10980
|
+
if (!config) {
|
|
10981
|
+
console.log("No config found. Run /init or /providers add <template> first.");
|
|
10982
|
+
return;
|
|
10983
|
+
}
|
|
10984
|
+
const registry = createDefaultBrainProviderRegistry(cwd);
|
|
10985
|
+
for (const provider of config.providers) {
|
|
10986
|
+
try {
|
|
10987
|
+
const result = await registry.healthCheck(provider);
|
|
10988
|
+
console.log(`${result.ok ? "PASS" : "FAIL"} ${provider.id}: ${provider.driver} | ${result.message}`);
|
|
10989
|
+
} catch (error) {
|
|
10990
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10991
|
+
console.log(`FAIL ${provider.id}: ${provider.driver} | ${message}`);
|
|
10992
|
+
}
|
|
10993
|
+
}
|
|
10994
|
+
}
|
|
10995
|
+
async function printBrainAssignments(cwd) {
|
|
10996
|
+
const config = await loadConfig(cwd);
|
|
10997
|
+
if (!config) {
|
|
10998
|
+
console.log("No config found. Run /init or /providers add <template> first.");
|
|
10999
|
+
return;
|
|
11000
|
+
}
|
|
11001
|
+
for (const role of BRAIN_ROLES) {
|
|
11002
|
+
const settings = resolveBrainSettings(config, role);
|
|
11003
|
+
console.log(`${role}`);
|
|
11004
|
+
console.log(` provider: ${settings.providerId}`);
|
|
11005
|
+
console.log(` model: ${resolveBrainModel(config, role) ?? "-"}`);
|
|
11006
|
+
console.log(` temperature: ${settings.temperature ?? "-"}`);
|
|
11007
|
+
console.log(` maxTokens: ${settings.maxTokens ?? "-"}`);
|
|
11008
|
+
}
|
|
11009
|
+
}
|
|
11010
|
+
function isBrainRole(value) {
|
|
11011
|
+
return BRAIN_ROLES.includes(value);
|
|
11012
|
+
}
|
|
11013
|
+
async function handleProvidersCommand(cwd, tokens, runtime) {
|
|
11014
|
+
const subcommand = tokens[1];
|
|
11015
|
+
if (!subcommand || subcommand === "list") {
|
|
11016
|
+
await printProviderList(cwd, runtime.focusRole);
|
|
11017
|
+
return;
|
|
11018
|
+
}
|
|
11019
|
+
if (subcommand === "templates") {
|
|
11020
|
+
printProviderTemplates();
|
|
11021
|
+
return;
|
|
11022
|
+
}
|
|
11023
|
+
if (subcommand === "check") {
|
|
11024
|
+
await printProviderHealth(cwd);
|
|
11025
|
+
return;
|
|
11026
|
+
}
|
|
11027
|
+
if (subcommand === "add") {
|
|
11028
|
+
const templateId = tokens[2];
|
|
11029
|
+
const providerId = tokens[3];
|
|
11030
|
+
if (!templateId) {
|
|
11031
|
+
throw new Error("Usage: /providers add <template> [provider-id]");
|
|
11032
|
+
}
|
|
11033
|
+
const outputPath = await addProviderFromTemplate(cwd, templateId, providerId);
|
|
11034
|
+
const resolvedProviderId = providerId ?? defaultProviderIdForTemplate(templateId);
|
|
11035
|
+
console.log(`Updated ${outputPath}`);
|
|
11036
|
+
console.log(`Added provider ${resolvedProviderId} from template ${templateId}`);
|
|
11037
|
+
console.log(`Next: /providers use ${resolvedProviderId}`);
|
|
11038
|
+
return;
|
|
11039
|
+
}
|
|
11040
|
+
if (subcommand === "use") {
|
|
11041
|
+
const providerId = tokens[2];
|
|
11042
|
+
const role = tokens[3];
|
|
11043
|
+
if (!providerId) {
|
|
11044
|
+
throw new Error("Usage: /providers use <provider-id> [role]");
|
|
11045
|
+
}
|
|
11046
|
+
const targetRole = role && isBrainRole(role) ? role : runtime.focusRole;
|
|
11047
|
+
const result = await useProviderForRole(cwd, targetRole, providerId);
|
|
11048
|
+
runtime.focusRole = targetRole;
|
|
11049
|
+
runtime.lastModels = null;
|
|
11050
|
+
console.log(`Updated ${result.outputPath}`);
|
|
11051
|
+
console.log(`Focus role ${targetRole} now uses provider ${providerId}${result.model ? ` (${result.model})` : ""}`);
|
|
11052
|
+
return;
|
|
11053
|
+
}
|
|
11054
|
+
throw new Error("Usage: /providers [list|templates|add|use|check]");
|
|
11055
|
+
}
|
|
11056
|
+
async function handleBrainCommand(cwd, tokens, runtime) {
|
|
11057
|
+
const subcommand = tokens[1];
|
|
11058
|
+
if (!subcommand || subcommand === "list") {
|
|
11059
|
+
await printBrainAssignments(cwd);
|
|
11060
|
+
return;
|
|
11061
|
+
}
|
|
11062
|
+
if (subcommand === "use" && tokens[2] && isBrainRole(tokens[2])) {
|
|
11063
|
+
runtime.focusRole = tokens[2];
|
|
11064
|
+
runtime.lastModels = null;
|
|
11065
|
+
console.log(`Shell focus role is now ${runtime.focusRole}.`);
|
|
11066
|
+
return;
|
|
11067
|
+
}
|
|
11068
|
+
if (isBrainRole(subcommand)) {
|
|
11069
|
+
runtime.focusRole = subcommand;
|
|
11070
|
+
runtime.lastModels = null;
|
|
11071
|
+
console.log(`Shell focus role is now ${runtime.focusRole}.`);
|
|
11072
|
+
return;
|
|
11073
|
+
}
|
|
11074
|
+
throw new Error("Usage: /brain [planner|coder|reviewer|fast]");
|
|
11075
|
+
}
|
|
11076
|
+
async function handleModelsCommand(cwd, tokens, runtime) {
|
|
11077
|
+
const config = await loadConfig(cwd);
|
|
11078
|
+
if (!config) {
|
|
11079
|
+
throw new Error("No config found. Run /init or /providers add <template> first.");
|
|
11080
|
+
}
|
|
11081
|
+
const providerId = config.brains[runtime.focusRole].providerId;
|
|
11082
|
+
const provider = findProviderById(config, providerId);
|
|
11083
|
+
if (!provider) {
|
|
11084
|
+
throw new Error(`Active role "${runtime.focusRole}" points to unknown provider "${providerId}".`);
|
|
11085
|
+
}
|
|
11086
|
+
const subcommand = tokens[1];
|
|
11087
|
+
if (subcommand === "use") {
|
|
11088
|
+
const modelId = tokens.slice(2).join(" ").trim();
|
|
11089
|
+
if (!modelId) {
|
|
11090
|
+
throw new Error("Usage: /models use <model-id>");
|
|
11091
|
+
}
|
|
11092
|
+
const outputPath = await assignModelToRole(cwd, runtime.focusRole, provider.id, modelId);
|
|
11093
|
+
console.log(`Updated ${outputPath}`);
|
|
11094
|
+
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11095
|
+
console.log(`Assigned role ${runtime.focusRole}`);
|
|
11096
|
+
return;
|
|
11097
|
+
}
|
|
11098
|
+
const search = !subcommand || MODEL_SUBCOMMANDS.has(subcommand) ? void 0 : tokens.slice(1).join(" ");
|
|
11099
|
+
const result = await fetchModelsForProvider(cwd, config, provider, search, 25);
|
|
11100
|
+
runtime.lastModels = {
|
|
11101
|
+
providerId: provider.id,
|
|
11102
|
+
role: runtime.focusRole,
|
|
11103
|
+
source: result.source,
|
|
11104
|
+
...search ? {
|
|
11105
|
+
search
|
|
11106
|
+
} : {},
|
|
11107
|
+
models: result.models
|
|
11108
|
+
};
|
|
11109
|
+
console.log(`${provider.id} (${result.source})`);
|
|
11110
|
+
if (result.note) {
|
|
11111
|
+
console.log(` note: ${result.note}`);
|
|
11112
|
+
}
|
|
11113
|
+
if (result.models.length === 0) {
|
|
11114
|
+
console.log(" no models found");
|
|
11115
|
+
return;
|
|
11116
|
+
}
|
|
11117
|
+
for (const [index, model] of result.models.entries()) {
|
|
11118
|
+
console.log(` ${index + 1}. ${renderModelLine2(model)}`);
|
|
11119
|
+
}
|
|
11120
|
+
console.log(``);
|
|
11121
|
+
console.log(`Use /select <number> or /use-model <model-id> to assign one to ${runtime.focusRole}.`);
|
|
11122
|
+
}
|
|
11123
|
+
async function handleModelSelection(cwd, modelId, runtime) {
|
|
11124
|
+
const config = await loadConfig(cwd);
|
|
11125
|
+
if (!config) {
|
|
11126
|
+
throw new Error("No config found. Run /init or /providers add <template> first.");
|
|
11127
|
+
}
|
|
11128
|
+
const providerId = config.brains[runtime.focusRole].providerId;
|
|
11129
|
+
const provider = findProviderById(config, providerId);
|
|
11130
|
+
if (!provider) {
|
|
11131
|
+
throw new Error(`Active role "${runtime.focusRole}" points to unknown provider "${providerId}".`);
|
|
11132
|
+
}
|
|
11133
|
+
const outputPath = await assignModelToRole(cwd, runtime.focusRole, provider.id, modelId);
|
|
11134
|
+
console.log(`Updated ${outputPath}`);
|
|
11135
|
+
console.log(`Selected ${provider.id}/${modelId}`);
|
|
11136
|
+
console.log(`Assigned role ${runtime.focusRole}`);
|
|
11137
|
+
}
|
|
11138
|
+
async function handleSelectCommand(cwd, tokens, runtime) {
|
|
11139
|
+
const rawIndex = tokens[1];
|
|
11140
|
+
if (!rawIndex) {
|
|
11141
|
+
throw new Error("Usage: /select <number>");
|
|
11142
|
+
}
|
|
11143
|
+
if (!runtime.lastModels || runtime.lastModels.models.length === 0) {
|
|
11144
|
+
throw new Error("No model list is active. Run /models first.");
|
|
11145
|
+
}
|
|
11146
|
+
const index = Number.parseInt(rawIndex, 10);
|
|
11147
|
+
if (!Number.isInteger(index) || index <= 0) {
|
|
11148
|
+
throw new Error(`Expected a positive model number, received "${rawIndex}".`);
|
|
11149
|
+
}
|
|
11150
|
+
const model = runtime.lastModels.models[index - 1];
|
|
11151
|
+
if (!model) {
|
|
11152
|
+
throw new Error(`Model number ${index} is out of range.`);
|
|
11153
|
+
}
|
|
11154
|
+
runtime.focusRole = runtime.lastModels.role;
|
|
11155
|
+
await handleModelSelection(cwd, model.id, runtime);
|
|
11156
|
+
}
|
|
11157
|
+
function toExternalCommandTokens(input, state) {
|
|
11158
|
+
const { normalizedInput, tokens, head } = normalizeInputTokens(input);
|
|
11159
|
+
if (!head || tokens.length === 0) {
|
|
10236
11160
|
return [];
|
|
10237
11161
|
}
|
|
10238
11162
|
const firstToken = tokens[0];
|
|
10239
11163
|
if (!firstToken) {
|
|
10240
11164
|
return [];
|
|
10241
11165
|
}
|
|
10242
|
-
const head = firstToken.startsWith("/") ? firstToken.slice(1) : firstToken;
|
|
10243
11166
|
if (head === "new") {
|
|
10244
11167
|
return [
|
|
10245
11168
|
"run",
|
|
@@ -10253,12 +11176,6 @@ function toCommandTokens(input) {
|
|
|
10253
11176
|
`scaffold ${goal}`.trim()
|
|
10254
11177
|
];
|
|
10255
11178
|
}
|
|
10256
|
-
if (head === "model") {
|
|
10257
|
-
return [
|
|
10258
|
-
"brains",
|
|
10259
|
-
"list"
|
|
10260
|
-
];
|
|
10261
|
-
}
|
|
10262
11179
|
if (!firstToken.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
|
|
10263
11180
|
return [
|
|
10264
11181
|
"run",
|
|
@@ -10268,91 +11185,157 @@ function toCommandTokens(input) {
|
|
|
10268
11185
|
tokens[0] = head;
|
|
10269
11186
|
return tokens;
|
|
10270
11187
|
}
|
|
11188
|
+
async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
11189
|
+
const trimmed = input.trim();
|
|
11190
|
+
if (!trimmed) {
|
|
11191
|
+
return;
|
|
11192
|
+
}
|
|
11193
|
+
if (trimmed === "/exit" || trimmed === "exit" || trimmed === "quit" || trimmed === "/quit") {
|
|
11194
|
+
throw new Error("__kimbho_exit__");
|
|
11195
|
+
}
|
|
11196
|
+
if (trimmed === "/help" || trimmed === "help" || trimmed === "?") {
|
|
11197
|
+
console.log(renderHelp());
|
|
11198
|
+
return;
|
|
11199
|
+
}
|
|
11200
|
+
if (trimmed === "/status" || trimmed === "status") {
|
|
11201
|
+
console.log(renderStartupCard(cwd, state));
|
|
11202
|
+
return;
|
|
11203
|
+
}
|
|
11204
|
+
if (trimmed === "/clear" || trimmed === "clear") {
|
|
11205
|
+
if (import_node_process10.default.stdout.isTTY) {
|
|
11206
|
+
import_node_process10.default.stdout.write("\x1Bc");
|
|
11207
|
+
}
|
|
11208
|
+
const nextState = await getShellSessionState(cwd, runtime.focusRole);
|
|
11209
|
+
printHeader(cwd, nextState);
|
|
11210
|
+
return;
|
|
11211
|
+
}
|
|
11212
|
+
const { tokens, head } = normalizeInputTokens(trimmed);
|
|
11213
|
+
if (!head) {
|
|
11214
|
+
return;
|
|
11215
|
+
}
|
|
11216
|
+
if (head === "provider" || head === "providers") {
|
|
11217
|
+
const subcommand = tokens[1];
|
|
11218
|
+
const canHandleLocally = !subcommand || subcommand === "list" || subcommand === "templates" || subcommand === "check" || subcommand === "use" || subcommand === "add" && Boolean(tokens[2]) && !tokens[2]?.startsWith("-");
|
|
11219
|
+
if (canHandleLocally) {
|
|
11220
|
+
await handleProvidersCommand(cwd, [
|
|
11221
|
+
"providers",
|
|
11222
|
+
...tokens.slice(1)
|
|
11223
|
+
], runtime);
|
|
11224
|
+
return;
|
|
11225
|
+
}
|
|
11226
|
+
}
|
|
11227
|
+
if (head === "brain" || head === "brains") {
|
|
11228
|
+
const subcommand = tokens[1];
|
|
11229
|
+
const roleArg = tokens[2];
|
|
11230
|
+
const canHandleLocally = !subcommand || subcommand === "list" || isBrainRole(subcommand) || subcommand === "use" && (roleArg ? isBrainRole(roleArg) : false);
|
|
11231
|
+
if (canHandleLocally) {
|
|
11232
|
+
await handleBrainCommand(cwd, [
|
|
11233
|
+
"brain",
|
|
11234
|
+
...tokens.slice(1)
|
|
11235
|
+
], runtime);
|
|
11236
|
+
return;
|
|
11237
|
+
}
|
|
11238
|
+
}
|
|
11239
|
+
if (head === "model") {
|
|
11240
|
+
await printBrainAssignments(cwd);
|
|
11241
|
+
return;
|
|
11242
|
+
}
|
|
11243
|
+
if (head === "models") {
|
|
11244
|
+
await handleModelsCommand(cwd, [
|
|
11245
|
+
"models",
|
|
11246
|
+
...tokens.slice(1)
|
|
11247
|
+
], runtime);
|
|
11248
|
+
return;
|
|
11249
|
+
}
|
|
11250
|
+
if (head === "use-model") {
|
|
11251
|
+
const modelId = tokens.slice(1).join(" ").trim();
|
|
11252
|
+
if (!modelId) {
|
|
11253
|
+
throw new Error("Usage: /use-model <model-id>");
|
|
11254
|
+
}
|
|
11255
|
+
await handleModelSelection(cwd, modelId, runtime);
|
|
11256
|
+
return;
|
|
11257
|
+
}
|
|
11258
|
+
if (head === "select") {
|
|
11259
|
+
await handleSelectCommand(cwd, [
|
|
11260
|
+
"select",
|
|
11261
|
+
...tokens.slice(1)
|
|
11262
|
+
], runtime);
|
|
11263
|
+
return;
|
|
11264
|
+
}
|
|
11265
|
+
const externalTokens = toExternalCommandTokens(trimmed, state);
|
|
11266
|
+
if (externalTokens.length === 0) {
|
|
11267
|
+
return;
|
|
11268
|
+
}
|
|
11269
|
+
await execute(externalTokens);
|
|
11270
|
+
}
|
|
10271
11271
|
async function runInteractiveShell(options) {
|
|
10272
|
-
const readline = (0,
|
|
10273
|
-
input:
|
|
10274
|
-
output:
|
|
10275
|
-
terminal: Boolean(
|
|
11272
|
+
const readline = (0, import_promises8.createInterface)({
|
|
11273
|
+
input: import_node_process10.default.stdin,
|
|
11274
|
+
output: import_node_process10.default.stdout,
|
|
11275
|
+
terminal: Boolean(import_node_process10.default.stdin.isTTY && import_node_process10.default.stdout.isTTY)
|
|
10276
11276
|
});
|
|
11277
|
+
const runtime = {
|
|
11278
|
+
focusRole: "coder",
|
|
11279
|
+
lastModels: null
|
|
11280
|
+
};
|
|
10277
11281
|
let closed = false;
|
|
10278
11282
|
readline.on("SIGINT", () => {
|
|
10279
11283
|
closed = true;
|
|
10280
11284
|
readline.close();
|
|
10281
11285
|
});
|
|
10282
|
-
let state = await getShellSessionState(options.cwd);
|
|
11286
|
+
let state = await getShellSessionState(options.cwd, runtime.focusRole);
|
|
10283
11287
|
printHeader(options.cwd, state);
|
|
10284
11288
|
while (!closed) {
|
|
10285
11289
|
let line;
|
|
10286
11290
|
try {
|
|
10287
|
-
state = await getShellSessionState(options.cwd);
|
|
11291
|
+
state = await getShellSessionState(options.cwd, runtime.focusRole);
|
|
10288
11292
|
line = await readline.question(formatPrompt(state));
|
|
10289
11293
|
} catch {
|
|
10290
11294
|
break;
|
|
10291
11295
|
}
|
|
10292
|
-
const trimmed = line.trim();
|
|
10293
|
-
if (!trimmed) {
|
|
10294
|
-
continue;
|
|
10295
|
-
}
|
|
10296
|
-
if (trimmed === "/exit" || trimmed === "exit" || trimmed === "quit" || trimmed === "/quit") {
|
|
10297
|
-
closed = true;
|
|
10298
|
-
break;
|
|
10299
|
-
}
|
|
10300
|
-
if (trimmed === "/help" || trimmed === "help" || trimmed === "?") {
|
|
10301
|
-
console.log(renderHelp());
|
|
10302
|
-
console.log("");
|
|
10303
|
-
continue;
|
|
10304
|
-
}
|
|
10305
|
-
if (trimmed === "/status" || trimmed === "status") {
|
|
10306
|
-
state = await getShellSessionState(options.cwd);
|
|
10307
|
-
console.log(renderStartupCard(options.cwd, state));
|
|
10308
|
-
console.log("");
|
|
10309
|
-
continue;
|
|
10310
|
-
}
|
|
10311
|
-
if (trimmed === "/clear" || trimmed === "clear") {
|
|
10312
|
-
if (import_node_process9.default.stdout.isTTY) {
|
|
10313
|
-
import_node_process9.default.stdout.write("\x1Bc");
|
|
10314
|
-
}
|
|
10315
|
-
state = await getShellSessionState(options.cwd);
|
|
10316
|
-
printHeader(options.cwd, state);
|
|
10317
|
-
continue;
|
|
10318
|
-
}
|
|
10319
11296
|
try {
|
|
10320
|
-
|
|
10321
|
-
if (tokens.length === 0) {
|
|
10322
|
-
continue;
|
|
10323
|
-
}
|
|
10324
|
-
await options.execute(tokens);
|
|
11297
|
+
await handleShellCommand(options.cwd, line, state, runtime, options.execute);
|
|
10325
11298
|
} catch (error) {
|
|
10326
11299
|
const message = error instanceof Error ? error.message : String(error);
|
|
11300
|
+
if (message === "__kimbho_exit__") {
|
|
11301
|
+
closed = true;
|
|
11302
|
+
break;
|
|
11303
|
+
}
|
|
10327
11304
|
console.error(message);
|
|
10328
11305
|
} finally {
|
|
10329
|
-
|
|
11306
|
+
import_node_process10.default.exitCode = 0;
|
|
10330
11307
|
}
|
|
10331
11308
|
if (!closed) {
|
|
10332
11309
|
console.log("");
|
|
10333
11310
|
}
|
|
10334
11311
|
}
|
|
10335
11312
|
readline.close();
|
|
10336
|
-
|
|
11313
|
+
import_node_process10.default.exitCode = 0;
|
|
10337
11314
|
console.log(color(DIM, "Leaving Kimbho."));
|
|
10338
11315
|
}
|
|
10339
11316
|
|
|
10340
11317
|
// src/index.ts
|
|
10341
11318
|
async function dispatchCliTokens(tokens) {
|
|
10342
|
-
const
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
11319
|
+
const normalizedTokens = normalizeCliTokens(tokens);
|
|
11320
|
+
await new Promise((resolve, reject) => {
|
|
11321
|
+
const child = (0, import_node_child_process3.spawn)(
|
|
11322
|
+
process.execPath,
|
|
11323
|
+
[
|
|
11324
|
+
...process.execArgv,
|
|
11325
|
+
process.argv[1] ?? "",
|
|
11326
|
+
...normalizedTokens
|
|
11327
|
+
],
|
|
11328
|
+
{
|
|
11329
|
+
cwd: process.cwd(),
|
|
11330
|
+
env: process.env,
|
|
11331
|
+
stdio: "inherit"
|
|
11332
|
+
}
|
|
11333
|
+
);
|
|
11334
|
+
child.on("error", reject);
|
|
11335
|
+
child.on("close", () => {
|
|
11336
|
+
resolve();
|
|
11337
|
+
});
|
|
11338
|
+
});
|
|
10356
11339
|
}
|
|
10357
11340
|
async function openShell() {
|
|
10358
11341
|
await runInteractiveShell({
|