@kimbho/kimbho-cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 path4 = require("node:path");
1150
+ var path6 = require("node:path");
1151
1151
  var fs = require("node:fs");
1152
- var process11 = require("node:process");
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) => process11.stdout.write(str),
1200
- writeErr: (str) => process11.stderr.write(str),
1199
+ writeOut: (str) => process12.stdout.write(str),
1200
+ writeErr: (str) => process12.stderr.write(str),
1201
1201
  outputError: (str, write) => write(str),
1202
- getOutHelpWidth: () => process11.stdout.isTTY ? process11.stdout.columns : void 0,
1203
- getErrHelpWidth: () => process11.stderr.isTTY ? process11.stderr.columns : void 0,
1204
- getOutHasColors: () => useColor() ?? (process11.stdout.isTTY && process11.stdout.hasColors?.()),
1205
- getErrHasColors: () => useColor() ?? (process11.stderr.isTTY && process11.stderr.hasColors?.()),
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
- process11.exit(exitCode);
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 (process11.versions?.electron) {
1986
+ if (process12.versions?.electron) {
1987
1987
  parseOptions.from = "electron";
1988
1988
  }
1989
- const execArgv = process11.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 = process11.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 (process11.defaultApp) {
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 = path4.resolve(baseDir, baseName);
2150
+ const localBin = path6.resolve(baseDir, baseName);
2151
2151
  if (fs.existsSync(localBin)) return localBin;
2152
- if (sourceExt.includes(path4.extname(baseName))) return void 0;
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 = path4.resolve(
2171
- path4.dirname(resolvedScriptPath),
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 = path4.basename(
2178
+ const legacyName = path6.basename(
2179
2179
  this._scriptPath,
2180
- path4.extname(this._scriptPath)
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(path4.extname(executableFile));
2191
+ launchWithNode = sourceExt.includes(path6.extname(executableFile));
2192
2192
  let proc;
2193
- if (process11.platform !== "win32") {
2193
+ if (process12.platform !== "win32") {
2194
2194
  if (launchWithNode) {
2195
2195
  args.unshift(executableFile);
2196
- args = incrementNodeInspectorPort(process11.execArgv).concat(args);
2197
- proc = childProcess.spawn(process11.argv[0], args, { stdio: "inherit" });
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(process11.execArgv).concat(args);
2209
- proc = childProcess.spawn(process11.execPath, args, { stdio: "inherit" });
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
- process11.on(signal, () => {
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
- process11.exit(code);
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
- process11.exit(1);
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 process11.env) {
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()}`, process11.env[option.envVar]);
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 = path4.basename(filename, path4.extname(filename));
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(path5) {
3053
- if (path5 === void 0) return this._executableDir;
3054
- this._executableDir = path5;
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(process11.exitCode ?? 0);
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 (process11.env.NO_COLOR || process11.env.FORCE_COLOR === "0" || process11.env.FORCE_COLOR === "false")
3293
+ if (process12.env.NO_COLOR || process12.env.FORCE_COLOR === "0" || process12.env.FORCE_COLOR === "false")
3294
3294
  return false;
3295
- if (process11.env.FORCE_COLOR || process11.env.CLICOLOR_FORCE !== void 0)
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.1",
3349
+ version: "0.1.2",
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: path4, errorMaps, issueData } = params;
4041
- const fullPath = [...path4, ...issueData.path || []];
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, path4, key) {
4164
+ constructor(parent, value, path6, key) {
4158
4165
  this._cachedPath = [];
4159
4166
  this.parent = parent;
4160
4167
  this.data = value;
4161
- this._path = path4;
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
- constructor(toolRegistry = createDefaultToolRegistry()) {
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 = new Set(tasks.filter((task) => task.status === "completed").map((task) => task.id));
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: createSessionId(),
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.length > 0 ? "ready" : "blocked";
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
- "Execution tooling is not wired yet; this snapshot captures the ready-task frontier."
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 import_node_process = __toESM(require("node:process"), 1);
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 import_promises3 = require("node:fs/promises");
8393
- var import_node_path3 = __toESM(require("node:path"), 1);
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) {
@@ -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 = import_node_path3.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path3.default.join(cwd, definition.modulePath);
8832
- await (0, import_promises3.access)(modulePath);
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(import_node_process.default.cwd());
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(import_node_process.default.cwd());
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, import_node_process.default.cwd());
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(import_node_process.default.cwd());
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 import_node_process2 = __toESM(require("node:process"), 1);
9082
- var import_node_child_process = require("node:child_process");
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, import_node_child_process.spawnSync)(binary, ["--version"], {
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: import_node_process2.default.version
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(import_node_process2.default.cwd());
9114
- const registry = createDefaultBrainProviderRegistry(import_node_process2.default.cwd());
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(import_node_process2.default.cwd())} (${config.providers.length} providers)`
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(import_node_process2.default.cwd())})`
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 import_node_process3 = __toESM(require("node:process"), 1);
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(import_node_process3.default.cwd());
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
- import_node_process3.default.exitCode = 1;
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, import_node_process3.default.cwd());
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 import_node_process4 = __toESM(require("node:process"), 1);
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)) {
@@ -9260,7 +9756,7 @@ async function fetchProviderModels(provider, options) {
9260
9756
  models: cachedModels
9261
9757
  };
9262
9758
  }
9263
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9759
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9264
9760
  try {
9265
9761
  const models = await registry.listModels(provider, {
9266
9762
  ...options.search ? {
@@ -9287,7 +9783,7 @@ async function fetchProviderModels(provider, options) {
9287
9783
  function createModelsCommand() {
9288
9784
  const command = new Command("models").description("Discover and select models from configured providers.");
9289
9785
  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(import_node_process4.default.cwd());
9786
+ let config = await loadConfig(import_node_process5.default.cwd());
9291
9787
  if (!config) {
9292
9788
  requireConfigMessage2();
9293
9789
  }
@@ -9316,7 +9812,7 @@ function createModelsCommand() {
9316
9812
  }
9317
9813
  }
9318
9814
  if (mutated) {
9319
- await saveConfig(config, import_node_process4.default.cwd());
9815
+ await saveConfig(config, import_node_process5.default.cwd());
9320
9816
  }
9321
9817
  if (options.json) {
9322
9818
  console.log(renderJson(groups));
@@ -9333,7 +9829,7 @@ function createModelsCommand() {
9333
9829
  }
9334
9830
  });
9335
9831
  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(import_node_process4.default.cwd());
9832
+ const config = await loadConfig(import_node_process5.default.cwd());
9337
9833
  if (!config) {
9338
9834
  requireConfigMessage2();
9339
9835
  }
@@ -9341,7 +9837,7 @@ function createModelsCommand() {
9341
9837
  if (!provider) {
9342
9838
  throw new Error(`Unknown provider "${providerId}".`);
9343
9839
  }
9344
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9840
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9345
9841
  const models = await registry.listModels(provider, {
9346
9842
  ...options.search ? {
9347
9843
  search: options.search
@@ -9352,12 +9848,12 @@ function createModelsCommand() {
9352
9848
  ...provider,
9353
9849
  models: Array.from(new Set(models.map((model) => model.id)))
9354
9850
  });
9355
- const outputPath = await saveConfig(nextConfig, import_node_process4.default.cwd());
9851
+ const outputPath = await saveConfig(nextConfig, import_node_process5.default.cwd());
9356
9852
  console.log(`Updated ${outputPath}`);
9357
9853
  console.log(`Synced ${models.length} models for ${provider.id}`);
9358
9854
  });
9359
9855
  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(import_node_process4.default.cwd());
9856
+ let config = await loadConfig(import_node_process5.default.cwd());
9361
9857
  if (!config) {
9362
9858
  requireConfigMessage2();
9363
9859
  }
@@ -9367,7 +9863,7 @@ function createModelsCommand() {
9367
9863
  }
9368
9864
  if (!options.force) {
9369
9865
  try {
9370
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9866
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9371
9867
  const models = await registry.listModels(provider, {
9372
9868
  search: model,
9373
9869
  limit: 500
@@ -9407,7 +9903,7 @@ function createModelsCommand() {
9407
9903
  } : {}
9408
9904
  });
9409
9905
  }
9410
- const outputPath = await saveConfig(config, import_node_process4.default.cwd());
9906
+ const outputPath = await saveConfig(config, import_node_process5.default.cwd());
9411
9907
  console.log(`Updated ${outputPath}`);
9412
9908
  console.log(`Selected ${provider.id}/${model}`);
9413
9909
  if (options.role) {
@@ -9421,8 +9917,8 @@ function createModelsCommand() {
9421
9917
  }
9422
9918
 
9423
9919
  // src/commands/plan.ts
9424
- var import_promises4 = require("node:fs/promises");
9425
- var import_node_process5 = __toESM(require("node:process"), 1);
9920
+ var import_promises6 = require("node:fs/promises");
9921
+ var import_node_process6 = __toESM(require("node:process"), 1);
9426
9922
 
9427
9923
  // ../planner/dist/planner.js
9428
9924
  function normalizeGoal(goal) {
@@ -9726,7 +10222,7 @@ function createPlan(input) {
9726
10222
 
9727
10223
  // src/commands/plan.ts
9728
10224
  async function detectWorkspaceState(cwd) {
9729
- const entries = await (0, import_promises4.readdir)(cwd);
10225
+ const entries = await (0, import_promises6.readdir)(cwd);
9730
10226
  const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
9731
10227
  return meaningfulEntries.length === 0 ? "empty" : "existing";
9732
10228
  }
@@ -9737,7 +10233,7 @@ function createPlanCommand() {
9737
10233
  (value, previous = []) => [...previous, value],
9738
10234
  []
9739
10235
  ).option("--json", "Print the plan as JSON", false).action(async (goal, options) => {
9740
- const cwd = import_node_process5.default.cwd();
10236
+ const cwd = import_node_process6.default.cwd();
9741
10237
  const request = {
9742
10238
  goal,
9743
10239
  mode: "plan",
@@ -9774,14 +10270,14 @@ function createPlanCommand() {
9774
10270
  }
9775
10271
 
9776
10272
  // src/commands/providers.ts
9777
- var import_node_process6 = __toESM(require("node:process"), 1);
10273
+ var import_node_process7 = __toESM(require("node:process"), 1);
9778
10274
  function requireConfigMessage3() {
9779
10275
  throw new Error("No config found. Run `kimbho init` first.");
9780
10276
  }
9781
10277
  function createProvidersCommand() {
9782
10278
  const command = new Command("providers").description("Manage configured model providers and built-in templates.");
9783
10279
  command.command("list").description("List configured providers.").option("--json", "Print providers as JSON", false).action(async (options) => {
9784
- const config = await loadConfig(import_node_process6.default.cwd());
10280
+ const config = await loadConfig(import_node_process7.default.cwd());
9785
10281
  if (!config) {
9786
10282
  requireConfigMessage3();
9787
10283
  }
@@ -9822,7 +10318,7 @@ function createProvidersCommand() {
9822
10318
  (value, previous = []) => [...previous, value],
9823
10319
  []
9824
10320
  ).option("--module-path <path>", "Module path for a custom provider driver").action(async (options) => {
9825
- const config = await loadConfig(import_node_process6.default.cwd());
10321
+ const config = await loadConfig(import_node_process7.default.cwd());
9826
10322
  if (!config) {
9827
10323
  requireConfigMessage3();
9828
10324
  }
@@ -9856,16 +10352,16 @@ function createProvidersCommand() {
9856
10352
  } : {}
9857
10353
  });
9858
10354
  const nextConfig = upsertProvider(config, provider);
9859
- const outputPath = await saveConfig(nextConfig, import_node_process6.default.cwd());
10355
+ const outputPath = await saveConfig(nextConfig, import_node_process7.default.cwd());
9860
10356
  console.log(`Updated ${outputPath}`);
9861
10357
  console.log(`Provider ${provider.id} -> ${provider.driver}`);
9862
10358
  });
9863
10359
  command.command("check").description("Run health checks for the configured providers.").action(async () => {
9864
- const config = await loadConfig(import_node_process6.default.cwd());
10360
+ const config = await loadConfig(import_node_process7.default.cwd());
9865
10361
  if (!config) {
9866
10362
  requireConfigMessage3();
9867
10363
  }
9868
- const registry = createDefaultBrainProviderRegistry(import_node_process6.default.cwd());
10364
+ const registry = createDefaultBrainProviderRegistry(import_node_process7.default.cwd());
9869
10365
  for (const provider of config.providers) {
9870
10366
  try {
9871
10367
  const result = await registry.healthCheck(provider);
@@ -9880,52 +10376,67 @@ function createProvidersCommand() {
9880
10376
  }
9881
10377
 
9882
10378
  // src/commands/resume.ts
9883
- var import_node_process7 = __toESM(require("node:process"), 1);
10379
+ var import_node_process8 = __toESM(require("node:process"), 1);
10380
+ function parseCount(value) {
10381
+ const parsed = Number(value);
10382
+ if (!Number.isInteger(parsed) || parsed <= 0) {
10383
+ throw new Error(`Expected a positive integer, received "${value}".`);
10384
+ }
10385
+ return parsed;
10386
+ }
9884
10387
  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 session = await loadLatestSession(import_node_process7.default.cwd());
10388
+ 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) => {
10389
+ const cwd = import_node_process8.default.cwd();
10390
+ const session = await loadLatestSession(cwd);
9887
10391
  if (!session) {
9888
10392
  console.error("No saved session found. Run `kimbho run` first.");
9889
- import_node_process7.default.exitCode = 1;
10393
+ import_node_process8.default.exitCode = 1;
9890
10394
  return;
9891
10395
  }
10396
+ const snapshot = options.execute ? await new ExecutionOrchestrator().continueSession(session, {
10397
+ maxAutoTasks: options.maxAutoTasks
10398
+ }) : session;
10399
+ if (options.execute) {
10400
+ await saveSession(snapshot, cwd);
10401
+ }
9892
10402
  if (options.json) {
9893
- console.log(renderJson(session));
10403
+ console.log(renderJson(snapshot));
9894
10404
  return;
9895
10405
  }
9896
- console.log(`Session: ${session.id}`);
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"}`);
10406
+ console.log(renderSession(snapshot));
9903
10407
  });
9904
10408
  }
9905
10409
 
9906
10410
  // src/commands/run.ts
9907
- var import_promises5 = require("node:fs/promises");
9908
- var import_node_process8 = __toESM(require("node:process"), 1);
10411
+ var import_promises7 = require("node:fs/promises");
10412
+ var import_node_process9 = __toESM(require("node:process"), 1);
9909
10413
  async function detectWorkspaceState2(cwd) {
9910
- const entries = await (0, import_promises5.readdir)(cwd);
10414
+ const entries = await (0, import_promises7.readdir)(cwd);
9911
10415
  const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
9912
10416
  return meaningfulEntries.length === 0 ? "empty" : "existing";
9913
10417
  }
10418
+ function parseCount2(value) {
10419
+ const parsed = Number(value);
10420
+ if (!Number.isInteger(parsed) || parsed <= 0) {
10421
+ throw new Error(`Expected a positive integer, received "${value}".`);
10422
+ }
10423
+ return parsed;
10424
+ }
9914
10425
  function createRunCommand() {
9915
10426
  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
10427
  "--constraint <constraint>",
9917
10428
  "Explicit execution constraint; can be provided multiple times",
9918
10429
  (value, previous = []) => [...previous, value],
9919
10430
  []
9920
- ).option("--json", "Print the session snapshot as JSON", false).action(async (goal, options) => {
9921
- const cwd = import_node_process8.default.cwd();
10431
+ ).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) => {
10432
+ const cwd = import_node_process9.default.cwd();
9922
10433
  const orchestrator = new ExecutionOrchestrator();
9923
10434
  let request;
9924
10435
  let planPath = null;
9925
10436
  let plan = goal ? null : await loadLatestPlan(cwd);
9926
10437
  if (!plan && !goal) {
9927
10438
  console.error("No saved plan found. Pass a goal or run `kimbho plan` first.");
9928
- import_node_process8.default.exitCode = 1;
10439
+ import_node_process9.default.exitCode = 1;
9929
10440
  return;
9930
10441
  }
9931
10442
  if (goal) {
@@ -9950,7 +10461,10 @@ function createRunCommand() {
9950
10461
  };
9951
10462
  }
9952
10463
  const envelope = orchestrator.buildEnvelope(request, plan);
9953
- const snapshot = orchestrator.createSessionSnapshot(envelope);
10464
+ const initialSnapshot = orchestrator.createSessionSnapshot(envelope);
10465
+ const snapshot = options.snapshotOnly ? initialSnapshot : await orchestrator.continueSession(initialSnapshot, {
10466
+ maxAutoTasks: options.maxAutoTasks
10467
+ });
9954
10468
  const sessionPath = await saveSession(snapshot, cwd);
9955
10469
  if (options.json) {
9956
10470
  console.log(
@@ -9964,15 +10478,12 @@ function createRunCommand() {
9964
10478
  }
9965
10479
  console.log(renderPlan(plan));
9966
10480
  console.log("");
9967
- console.log(`Session: ${snapshot.id}`);
9968
- console.log(`Saved session: ${sessionPath}`);
10481
+ console.log(renderSession(snapshot));
10482
+ console.log("");
9969
10483
  if (planPath) {
9970
10484
  console.log(`Saved plan: ${planPath}`);
9971
10485
  }
9972
- console.log(`Assigned agents: ${snapshot.assignedAgents.join(", ") || "none"}`);
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.");
10486
+ console.log(`Saved session: ${sessionPath}`);
9976
10487
  });
9977
10488
  }
9978
10489
 
@@ -10040,8 +10551,8 @@ function createProgram(onOpenShell) {
10040
10551
  }
10041
10552
 
10042
10553
  // src/shell.ts
10043
- var import_promises6 = require("node:readline/promises");
10044
- var import_node_process9 = __toESM(require("node:process"), 1);
10554
+ var import_promises8 = require("node:readline/promises");
10555
+ var import_node_process10 = __toESM(require("node:process"), 1);
10045
10556
  var AMBER = "\x1B[38;5;214m";
10046
10557
  var BOLD = "\x1B[1m";
10047
10558
  var DIM = "\x1B[2m";
@@ -10269,10 +10780,10 @@ function toCommandTokens(input) {
10269
10780
  return tokens;
10270
10781
  }
10271
10782
  async function runInteractiveShell(options) {
10272
- const readline = (0, import_promises6.createInterface)({
10273
- input: import_node_process9.default.stdin,
10274
- output: import_node_process9.default.stdout,
10275
- terminal: Boolean(import_node_process9.default.stdin.isTTY && import_node_process9.default.stdout.isTTY)
10783
+ const readline = (0, import_promises8.createInterface)({
10784
+ input: import_node_process10.default.stdin,
10785
+ output: import_node_process10.default.stdout,
10786
+ terminal: Boolean(import_node_process10.default.stdin.isTTY && import_node_process10.default.stdout.isTTY)
10276
10787
  });
10277
10788
  let closed = false;
10278
10789
  readline.on("SIGINT", () => {
@@ -10309,8 +10820,8 @@ async function runInteractiveShell(options) {
10309
10820
  continue;
10310
10821
  }
10311
10822
  if (trimmed === "/clear" || trimmed === "clear") {
10312
- if (import_node_process9.default.stdout.isTTY) {
10313
- import_node_process9.default.stdout.write("\x1Bc");
10823
+ if (import_node_process10.default.stdout.isTTY) {
10824
+ import_node_process10.default.stdout.write("\x1Bc");
10314
10825
  }
10315
10826
  state = await getShellSessionState(options.cwd);
10316
10827
  printHeader(options.cwd, state);
@@ -10326,33 +10837,39 @@ async function runInteractiveShell(options) {
10326
10837
  const message = error instanceof Error ? error.message : String(error);
10327
10838
  console.error(message);
10328
10839
  } finally {
10329
- import_node_process9.default.exitCode = 0;
10840
+ import_node_process10.default.exitCode = 0;
10330
10841
  }
10331
10842
  if (!closed) {
10332
10843
  console.log("");
10333
10844
  }
10334
10845
  }
10335
10846
  readline.close();
10336
- import_node_process9.default.exitCode = 0;
10847
+ import_node_process10.default.exitCode = 0;
10337
10848
  console.log(color(DIM, "Leaving Kimbho."));
10338
10849
  }
10339
10850
 
10340
10851
  // src/index.ts
10341
10852
  async function dispatchCliTokens(tokens) {
10342
- const program2 = createProgram();
10343
- program2.exitOverride();
10344
- try {
10345
- await program2.parseAsync([
10346
- "node",
10347
- "kimbho",
10348
- ...normalizeCliTokens(tokens)
10349
- ]);
10350
- } catch (error) {
10351
- if (error instanceof CommanderError) {
10352
- return;
10353
- }
10354
- throw error;
10355
- }
10853
+ const normalizedTokens = normalizeCliTokens(tokens);
10854
+ await new Promise((resolve, reject) => {
10855
+ const child = (0, import_node_child_process3.spawn)(
10856
+ process.execPath,
10857
+ [
10858
+ ...process.execArgv,
10859
+ process.argv[1] ?? "",
10860
+ ...normalizedTokens
10861
+ ],
10862
+ {
10863
+ cwd: process.cwd(),
10864
+ env: process.env,
10865
+ stdio: "inherit"
10866
+ }
10867
+ );
10868
+ child.on("error", reject);
10869
+ child.on("close", () => {
10870
+ resolve();
10871
+ });
10872
+ });
10356
10873
  }
10357
10874
  async function openShell() {
10358
10875
  await runInteractiveShell({