@kimbho/kimbho-cli 0.1.0 → 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 process10 = 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) => process10.stdout.write(str),
1200
- writeErr: (str) => process10.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: () => process10.stdout.isTTY ? process10.stdout.columns : void 0,
1203
- getErrHelpWidth: () => process10.stderr.isTTY ? process10.stderr.columns : void 0,
1204
- getOutHasColors: () => useColor() ?? (process10.stdout.isTTY && process10.stdout.hasColors?.()),
1205
- getErrHasColors: () => useColor() ?? (process10.stderr.isTTY && process10.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
- process10.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 (process10.versions?.electron) {
1986
+ if (process12.versions?.electron) {
1987
1987
  parseOptions.from = "electron";
1988
1988
  }
1989
- const execArgv = process10.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 = process10.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 (process10.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 (process10.platform !== "win32") {
2193
+ if (process12.platform !== "win32") {
2194
2194
  if (launchWithNode) {
2195
2195
  args.unshift(executableFile);
2196
- args = incrementNodeInspectorPort(process10.execArgv).concat(args);
2197
- proc = childProcess.spawn(process10.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(process10.execArgv).concat(args);
2209
- proc = childProcess.spawn(process10.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
- process10.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
- process10.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
- process10.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 process10.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()}`, process10.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(process10.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 (process10.env.NO_COLOR || process10.env.FORCE_COLOR === "0" || process10.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 (process10.env.FORCE_COLOR || process10.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 {
@@ -3340,6 +3343,55 @@ var {
3340
3343
  Help
3341
3344
  } = import_index.default;
3342
3345
 
3346
+ // package.json
3347
+ var package_default = {
3348
+ name: "@kimbho/kimbho-cli",
3349
+ version: "0.1.2",
3350
+ description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
3351
+ type: "module",
3352
+ engines: {
3353
+ node: ">=20.0.0"
3354
+ },
3355
+ preferGlobal: true,
3356
+ bin: {
3357
+ kimbho: "dist/index.cjs"
3358
+ },
3359
+ main: "dist/index.cjs",
3360
+ exports: {
3361
+ ".": {
3362
+ default: "./dist/index.cjs"
3363
+ }
3364
+ },
3365
+ files: [
3366
+ "dist",
3367
+ "README.md"
3368
+ ],
3369
+ publishConfig: {
3370
+ access: "public"
3371
+ },
3372
+ scripts: {
3373
+ build: "npm run clean && npm run build:bundle && npm run chmod:bin",
3374
+ "build:bundle": "esbuild src/index.ts --bundle --platform=node --format=cjs --sourcemap --outfile=dist/index.cjs --banner:js='#!/usr/bin/env node'",
3375
+ "chmod:bin": `node -e "require('node:fs').chmodSync('dist/index.cjs', 0o755)"`,
3376
+ clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
3377
+ prepack: "npm run build"
3378
+ },
3379
+ dependencies: {
3380
+ commander: "^13.1.0"
3381
+ },
3382
+ devDependencies: {
3383
+ "@kimbho/agent-runtime": "file:../agent-runtime",
3384
+ "@kimbho/brains": "file:../brains",
3385
+ "@kimbho/core": "file:../core",
3386
+ "@kimbho/planner": "file:../planner",
3387
+ "@kimbho/tools": "file:../tools"
3388
+ }
3389
+ };
3390
+
3391
+ // src/metadata.ts
3392
+ var KIMBHO_VERSION = package_default.version;
3393
+ var KIMBHO_DESCRIPTION = "Kimbho CLI: a terminal-native coding agent for planning and execution.";
3394
+
3343
3395
  // ../agent-runtime/dist/agents/catalog.js
3344
3396
  var AGENT_CATALOG = {
3345
3397
  "session-orchestrator": {
@@ -3510,6 +3562,10 @@ function listAgentProfiles() {
3510
3562
  return Object.values(AGENT_CATALOG).sort((left, right) => left.role.localeCompare(right.role));
3511
3563
  }
3512
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
+
3513
3569
  // ../../node_modules/zod/v3/external.js
3514
3570
  var external_exports = {};
3515
3571
  __export(external_exports, {
@@ -3988,8 +4044,8 @@ function getErrorMap() {
3988
4044
 
3989
4045
  // ../../node_modules/zod/v3/helpers/parseUtil.js
3990
4046
  var makeIssue = (params) => {
3991
- const { data, path: path4, errorMaps, issueData } = params;
3992
- const fullPath = [...path4, ...issueData.path || []];
4047
+ const { data, path: path6, errorMaps, issueData } = params;
4048
+ const fullPath = [...path6, ...issueData.path || []];
3993
4049
  const fullIssue = {
3994
4050
  ...issueData,
3995
4051
  path: fullPath
@@ -4105,11 +4161,11 @@ var errorUtil;
4105
4161
 
4106
4162
  // ../../node_modules/zod/v3/types.js
4107
4163
  var ParseInputLazyPath = class {
4108
- constructor(parent, value, path4, key) {
4164
+ constructor(parent, value, path6, key) {
4109
4165
  this._cachedPath = [];
4110
4166
  this.parent = parent;
4111
4167
  this.data = value;
4112
- this._path = path4;
4168
+ this._path = path6;
4113
4169
  this._key = key;
4114
4170
  }
4115
4171
  get path() {
@@ -7759,6 +7815,16 @@ var KimbhoConfigSchema = external_exports.object({
7759
7815
  }
7760
7816
  });
7761
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
+
7762
7828
  // ../core/dist/contracts/session.js
7763
7829
  var SessionStatusSchema = external_exports.enum([
7764
7830
  "planned",
@@ -7767,6 +7833,22 @@ var SessionStatusSchema = external_exports.enum([
7767
7833
  "blocked",
7768
7834
  "completed"
7769
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
+ });
7770
7852
  var SessionSnapshotSchema = external_exports.object({
7771
7853
  id: external_exports.string().min(1),
7772
7854
  goal: external_exports.string().min(1),
@@ -7778,8 +7860,10 @@ var SessionSnapshotSchema = external_exports.object({
7778
7860
  plan: KimbhoPlanSchema,
7779
7861
  readyTaskIds: external_exports.array(external_exports.string()).default([]),
7780
7862
  blockedTaskIds: external_exports.array(external_exports.string()).default([]),
7863
+ completedTaskIds: external_exports.array(external_exports.string()).default([]),
7781
7864
  assignedAgents: external_exports.array(AgentRoleSchema).default([]),
7782
- notes: external_exports.array(external_exports.string()).default([])
7865
+ notes: external_exports.array(external_exports.string()).default([]),
7866
+ events: external_exports.array(SessionEventSchema).default([])
7783
7867
  });
7784
7868
 
7785
7869
  // ../core/dist/config/config.js
@@ -8067,14 +8151,6 @@ var ToolDescriptorSchema = external_exports.object({
8067
8151
  producesArtifacts: external_exports.boolean().default(false),
8068
8152
  allowedRoles: external_exports.array(AgentRoleSchema).default([])
8069
8153
  });
8070
- var ToolResultSchema = external_exports.object({
8071
- toolId: external_exports.string().min(1),
8072
- success: external_exports.boolean(),
8073
- summary: external_exports.string().min(1),
8074
- stdout: external_exports.string().optional(),
8075
- stderr: external_exports.string().optional(),
8076
- artifacts: external_exports.array(external_exports.string()).default([])
8077
- });
8078
8154
 
8079
8155
  // ../tools/dist/registry.js
8080
8156
  var BUILTIN_TOOLS = [
@@ -8184,26 +8260,296 @@ function createDefaultToolRegistry() {
8184
8260
  return new ToolRegistry(BUILTIN_TOOLS);
8185
8261
  }
8186
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
+
8187
8458
  // ../agent-runtime/dist/orchestrator.js
8188
8459
  function createSessionId() {
8189
8460
  return `session-${Date.now()}`;
8190
8461
  }
8462
+ function createEventId(type, taskId) {
8463
+ return `${type}-${taskId ?? "session"}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8464
+ }
8191
8465
  function isTaskReady(task, completedTaskIds) {
8192
8466
  return task.status === "pending" && task.dependsOn.every((taskId) => completedTaskIds.has(taskId));
8193
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
+ }
8194
8538
  var ExecutionOrchestrator = class {
8195
8539
  toolRegistry;
8196
- constructor(toolRegistry = createDefaultToolRegistry()) {
8540
+ toolRuntime;
8541
+ constructor(toolRegistry = createDefaultToolRegistry(), toolRuntime = new ToolRuntime(toolRegistry)) {
8197
8542
  this.toolRegistry = toolRegistry;
8543
+ this.toolRuntime = toolRuntime;
8198
8544
  }
8199
- buildEnvelope(request, plan) {
8545
+ buildEnvelope(request, plan, sessionId = createSessionId()) {
8200
8546
  const tasks = flattenPlanTasks(plan);
8201
- const completedTaskIds = new Set(tasks.filter((task) => task.status === "completed").map((task) => task.id));
8547
+ const completedTaskIds = completedTaskIdsFromPlan(plan);
8202
8548
  const readyTasks = tasks.filter((task) => isTaskReady(task, completedTaskIds));
8203
8549
  const blockedTasks = tasks.filter((task) => task.status !== "completed" && !isTaskReady(task, completedTaskIds));
8204
8550
  const assignedAgents = this.selectAgentsForTasks(readyTasks);
8205
8551
  return {
8206
- sessionId: createSessionId(),
8552
+ sessionId,
8207
8553
  request,
8208
8554
  plan,
8209
8555
  readyTasks,
@@ -8211,9 +8557,9 @@ var ExecutionOrchestrator = class {
8211
8557
  assignedAgents
8212
8558
  };
8213
8559
  }
8214
- createSessionSnapshot(envelope) {
8560
+ createSessionSnapshot(envelope, overrides = {}) {
8215
8561
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
8216
- const status = envelope.readyTasks.length > 0 ? "ready" : "blocked";
8562
+ const status = deriveStatus(envelope.plan, envelope.readyTasks, envelope.blockedTasks);
8217
8563
  return SessionSnapshotSchema.parse({
8218
8564
  id: envelope.sessionId,
8219
8565
  goal: envelope.request.goal,
@@ -8225,11 +8571,75 @@ var ExecutionOrchestrator = class {
8225
8571
  plan: envelope.plan,
8226
8572
  readyTaskIds: envelope.readyTasks.map((task) => task.id),
8227
8573
  blockedTaskIds: envelope.blockedTasks.map((task) => task.id),
8574
+ completedTaskIds: Array.from(completedTaskIdsFromPlan(envelope.plan)),
8228
8575
  assignedAgents: envelope.assignedAgents.map((agent) => agent.role),
8229
8576
  notes: [
8230
- "Initial session snapshot created from the current plan.",
8231
- "Execution tooling is not wired yet; this snapshot captures the ready-task frontier."
8232
- ]
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
8233
8643
  });
8234
8644
  }
8235
8645
  selectAgentsForTasks(tasks) {
@@ -8239,6 +8649,107 @@ var ExecutionOrchestrator = class {
8239
8649
  toolsForAgent(role) {
8240
8650
  return this.toolRegistry.byRole(role);
8241
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
+ }
8242
8753
  };
8243
8754
 
8244
8755
  // src/commands/agents.ts
@@ -8264,7 +8775,7 @@ function createAgentsCommand() {
8264
8775
  }
8265
8776
 
8266
8777
  // src/commands/brains.ts
8267
- var import_node_process = __toESM(require("node:process"), 1);
8778
+ var import_node_process2 = __toESM(require("node:process"), 1);
8268
8779
 
8269
8780
  // ../brains/dist/templates.js
8270
8781
  var BUILTIN_PROVIDER_TEMPLATES = [
@@ -8340,8 +8851,8 @@ function buildProviderFromTemplate(templateId, options = {}) {
8340
8851
  }
8341
8852
 
8342
8853
  // ../brains/dist/registry.js
8343
- var import_promises3 = require("node:fs/promises");
8344
- 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);
8345
8856
  var import_node_url = require("node:url");
8346
8857
  function resolveApiKey(definition) {
8347
8858
  if (!definition.apiKeyEnv) {
@@ -8779,8 +9290,8 @@ async function createCustomModuleProvider(definition, cwd) {
8779
9290
  if (!definition.modulePath) {
8780
9291
  throw new Error(`Provider "${definition.id}" requires modulePath.`);
8781
9292
  }
8782
- const modulePath = import_node_path3.default.isAbsolute(definition.modulePath) ? definition.modulePath : import_node_path3.default.join(cwd, definition.modulePath);
8783
- 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);
8784
9295
  const module2 = await import((0, import_node_url.pathToFileURL)(modulePath).href);
8785
9296
  const createProvider = typeof module2.createProvider === "function" ? module2.createProvider : typeof module2.default === "function" ? module2.default : null;
8786
9297
  if (!createProvider) {
@@ -8929,6 +9440,40 @@ function renderPlan(plan) {
8929
9440
  function renderJson(value) {
8930
9441
  return JSON.stringify(value, null, 2);
8931
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
+ }
8932
9477
 
8933
9478
  // src/commands/brains.ts
8934
9479
  function parseNumber(value) {
@@ -8944,7 +9489,7 @@ function requireConfigMessage() {
8944
9489
  function createBrainsCommand() {
8945
9490
  const command = new Command("brains").description("Inspect and assign the LLM brains used by Kimbho roles.");
8946
9491
  command.command("list").description("List brain-role assignments.").option("--json", "Print roles as JSON", false).action(async (options) => {
8947
- const config = await loadConfig(import_node_process.default.cwd());
9492
+ const config = await loadConfig(import_node_process2.default.cwd());
8948
9493
  if (!config) {
8949
9494
  requireConfigMessage();
8950
9495
  }
@@ -8974,7 +9519,7 @@ function createBrainsCommand() {
8974
9519
  }
8975
9520
  });
8976
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) => {
8977
- const config = await loadConfig(import_node_process.default.cwd());
9522
+ const config = await loadConfig(import_node_process2.default.cwd());
8978
9523
  if (!config) {
8979
9524
  requireConfigMessage();
8980
9525
  }
@@ -8998,12 +9543,12 @@ function createBrainsCommand() {
8998
9543
  promptPreamble: options.promptPreamble
8999
9544
  } : {}
9000
9545
  });
9001
- const outputPath = await saveConfig(nextConfig, import_node_process.default.cwd());
9546
+ const outputPath = await saveConfig(nextConfig, import_node_process2.default.cwd());
9002
9547
  console.log(`Updated ${outputPath}`);
9003
9548
  console.log(`Brain ${role} -> ${provider.id}/${options.model ?? provider.defaultModel ?? "model not set"}`);
9004
9549
  });
9005
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) => {
9006
- const config = await loadConfig(import_node_process.default.cwd());
9551
+ const config = await loadConfig(import_node_process2.default.cwd());
9007
9552
  if (!config) {
9008
9553
  requireConfigMessage();
9009
9554
  }
@@ -9029,10 +9574,10 @@ function createBrainsCommand() {
9029
9574
  }
9030
9575
 
9031
9576
  // src/commands/doctor.ts
9032
- var import_node_process2 = __toESM(require("node:process"), 1);
9033
- 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");
9034
9579
  function commandVersion(binary) {
9035
- const result = (0, import_node_child_process.spawnSync)(binary, ["--version"], {
9580
+ const result = (0, import_node_child_process2.spawnSync)(binary, ["--version"], {
9036
9581
  encoding: "utf8"
9037
9582
  });
9038
9583
  if (result.status === 0) {
@@ -9055,19 +9600,19 @@ function createDoctorCommand() {
9055
9600
  {
9056
9601
  name: "node",
9057
9602
  ok: true,
9058
- detail: import_node_process2.default.version
9603
+ detail: import_node_process3.default.version
9059
9604
  },
9060
9605
  commandVersion("npm"),
9061
9606
  commandVersion("git"),
9062
9607
  commandVersion("docker")
9063
9608
  ];
9064
- const config = await loadConfig(import_node_process2.default.cwd());
9065
- 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());
9066
9611
  if (config) {
9067
9612
  checks.push({
9068
9613
  name: "config",
9069
9614
  ok: true,
9070
- detail: `${resolveConfigPath(import_node_process2.default.cwd())} (${config.providers.length} providers)`
9615
+ detail: `${resolveConfigPath(import_node_process3.default.cwd())} (${config.providers.length} providers)`
9071
9616
  });
9072
9617
  for (const provider of config.providers) {
9073
9618
  try {
@@ -9104,7 +9649,7 @@ function createDoctorCommand() {
9104
9649
  checks.push({
9105
9650
  name: "config",
9106
9651
  ok: false,
9107
- detail: `missing (${resolveConfigPath(import_node_process2.default.cwd())})`
9652
+ detail: `missing (${resolveConfigPath(import_node_process3.default.cwd())})`
9108
9653
  });
9109
9654
  }
9110
9655
  for (const check of checks) {
@@ -9114,13 +9659,13 @@ function createDoctorCommand() {
9114
9659
  }
9115
9660
 
9116
9661
  // src/commands/init.ts
9117
- var import_node_process3 = __toESM(require("node:process"), 1);
9662
+ var import_node_process4 = __toESM(require("node:process"), 1);
9118
9663
  function createInitCommand() {
9119
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) => {
9120
- const existing = await loadConfig(import_node_process3.default.cwd());
9665
+ const existing = await loadConfig(import_node_process4.default.cwd());
9121
9666
  if (existing && !options.force) {
9122
9667
  console.error("Config already exists. Re-run with --force to overwrite it.");
9123
- import_node_process3.default.exitCode = 1;
9668
+ import_node_process4.default.exitCode = 1;
9124
9669
  return;
9125
9670
  }
9126
9671
  const templateId = options.template ?? (!options.driver ? "openai" : null);
@@ -9154,13 +9699,13 @@ function createInitCommand() {
9154
9699
  defaultModel: options.model ?? provider.defaultModel,
9155
9700
  fastModel: options.fastModel ?? options.model ?? provider.defaultModel
9156
9701
  });
9157
- const outputPath = await saveConfig(nextConfig, import_node_process3.default.cwd());
9702
+ const outputPath = await saveConfig(nextConfig, import_node_process4.default.cwd());
9158
9703
  console.log(`Created ${outputPath}`);
9159
9704
  });
9160
9705
  }
9161
9706
 
9162
9707
  // src/commands/models.ts
9163
- var import_node_process4 = __toESM(require("node:process"), 1);
9708
+ var import_node_process5 = __toESM(require("node:process"), 1);
9164
9709
  function parseInteger(value) {
9165
9710
  const parsed = Number.parseInt(value, 10);
9166
9711
  if (!Number.isFinite(parsed)) {
@@ -9211,7 +9756,7 @@ async function fetchProviderModels(provider, options) {
9211
9756
  models: cachedModels
9212
9757
  };
9213
9758
  }
9214
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9759
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9215
9760
  try {
9216
9761
  const models = await registry.listModels(provider, {
9217
9762
  ...options.search ? {
@@ -9238,7 +9783,7 @@ async function fetchProviderModels(provider, options) {
9238
9783
  function createModelsCommand() {
9239
9784
  const command = new Command("models").description("Discover and select models from configured providers.");
9240
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) => {
9241
- let config = await loadConfig(import_node_process4.default.cwd());
9786
+ let config = await loadConfig(import_node_process5.default.cwd());
9242
9787
  if (!config) {
9243
9788
  requireConfigMessage2();
9244
9789
  }
@@ -9267,7 +9812,7 @@ function createModelsCommand() {
9267
9812
  }
9268
9813
  }
9269
9814
  if (mutated) {
9270
- await saveConfig(config, import_node_process4.default.cwd());
9815
+ await saveConfig(config, import_node_process5.default.cwd());
9271
9816
  }
9272
9817
  if (options.json) {
9273
9818
  console.log(renderJson(groups));
@@ -9284,7 +9829,7 @@ function createModelsCommand() {
9284
9829
  }
9285
9830
  });
9286
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) => {
9287
- const config = await loadConfig(import_node_process4.default.cwd());
9832
+ const config = await loadConfig(import_node_process5.default.cwd());
9288
9833
  if (!config) {
9289
9834
  requireConfigMessage2();
9290
9835
  }
@@ -9292,7 +9837,7 @@ function createModelsCommand() {
9292
9837
  if (!provider) {
9293
9838
  throw new Error(`Unknown provider "${providerId}".`);
9294
9839
  }
9295
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9840
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9296
9841
  const models = await registry.listModels(provider, {
9297
9842
  ...options.search ? {
9298
9843
  search: options.search
@@ -9303,12 +9848,12 @@ function createModelsCommand() {
9303
9848
  ...provider,
9304
9849
  models: Array.from(new Set(models.map((model) => model.id)))
9305
9850
  });
9306
- const outputPath = await saveConfig(nextConfig, import_node_process4.default.cwd());
9851
+ const outputPath = await saveConfig(nextConfig, import_node_process5.default.cwd());
9307
9852
  console.log(`Updated ${outputPath}`);
9308
9853
  console.log(`Synced ${models.length} models for ${provider.id}`);
9309
9854
  });
9310
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) => {
9311
- let config = await loadConfig(import_node_process4.default.cwd());
9856
+ let config = await loadConfig(import_node_process5.default.cwd());
9312
9857
  if (!config) {
9313
9858
  requireConfigMessage2();
9314
9859
  }
@@ -9318,7 +9863,7 @@ function createModelsCommand() {
9318
9863
  }
9319
9864
  if (!options.force) {
9320
9865
  try {
9321
- const registry = createDefaultBrainProviderRegistry(import_node_process4.default.cwd());
9866
+ const registry = createDefaultBrainProviderRegistry(import_node_process5.default.cwd());
9322
9867
  const models = await registry.listModels(provider, {
9323
9868
  search: model,
9324
9869
  limit: 500
@@ -9358,7 +9903,7 @@ function createModelsCommand() {
9358
9903
  } : {}
9359
9904
  });
9360
9905
  }
9361
- const outputPath = await saveConfig(config, import_node_process4.default.cwd());
9906
+ const outputPath = await saveConfig(config, import_node_process5.default.cwd());
9362
9907
  console.log(`Updated ${outputPath}`);
9363
9908
  console.log(`Selected ${provider.id}/${model}`);
9364
9909
  if (options.role) {
@@ -9372,8 +9917,8 @@ function createModelsCommand() {
9372
9917
  }
9373
9918
 
9374
9919
  // src/commands/plan.ts
9375
- var import_promises4 = require("node:fs/promises");
9376
- 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);
9377
9922
 
9378
9923
  // ../planner/dist/planner.js
9379
9924
  function normalizeGoal(goal) {
@@ -9677,7 +10222,7 @@ function createPlan(input) {
9677
10222
 
9678
10223
  // src/commands/plan.ts
9679
10224
  async function detectWorkspaceState(cwd) {
9680
- const entries = await (0, import_promises4.readdir)(cwd);
10225
+ const entries = await (0, import_promises6.readdir)(cwd);
9681
10226
  const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
9682
10227
  return meaningfulEntries.length === 0 ? "empty" : "existing";
9683
10228
  }
@@ -9688,7 +10233,7 @@ function createPlanCommand() {
9688
10233
  (value, previous = []) => [...previous, value],
9689
10234
  []
9690
10235
  ).option("--json", "Print the plan as JSON", false).action(async (goal, options) => {
9691
- const cwd = import_node_process5.default.cwd();
10236
+ const cwd = import_node_process6.default.cwd();
9692
10237
  const request = {
9693
10238
  goal,
9694
10239
  mode: "plan",
@@ -9725,14 +10270,14 @@ function createPlanCommand() {
9725
10270
  }
9726
10271
 
9727
10272
  // src/commands/providers.ts
9728
- var import_node_process6 = __toESM(require("node:process"), 1);
10273
+ var import_node_process7 = __toESM(require("node:process"), 1);
9729
10274
  function requireConfigMessage3() {
9730
10275
  throw new Error("No config found. Run `kimbho init` first.");
9731
10276
  }
9732
10277
  function createProvidersCommand() {
9733
10278
  const command = new Command("providers").description("Manage configured model providers and built-in templates.");
9734
10279
  command.command("list").description("List configured providers.").option("--json", "Print providers as JSON", false).action(async (options) => {
9735
- const config = await loadConfig(import_node_process6.default.cwd());
10280
+ const config = await loadConfig(import_node_process7.default.cwd());
9736
10281
  if (!config) {
9737
10282
  requireConfigMessage3();
9738
10283
  }
@@ -9773,7 +10318,7 @@ function createProvidersCommand() {
9773
10318
  (value, previous = []) => [...previous, value],
9774
10319
  []
9775
10320
  ).option("--module-path <path>", "Module path for a custom provider driver").action(async (options) => {
9776
- const config = await loadConfig(import_node_process6.default.cwd());
10321
+ const config = await loadConfig(import_node_process7.default.cwd());
9777
10322
  if (!config) {
9778
10323
  requireConfigMessage3();
9779
10324
  }
@@ -9807,16 +10352,16 @@ function createProvidersCommand() {
9807
10352
  } : {}
9808
10353
  });
9809
10354
  const nextConfig = upsertProvider(config, provider);
9810
- const outputPath = await saveConfig(nextConfig, import_node_process6.default.cwd());
10355
+ const outputPath = await saveConfig(nextConfig, import_node_process7.default.cwd());
9811
10356
  console.log(`Updated ${outputPath}`);
9812
10357
  console.log(`Provider ${provider.id} -> ${provider.driver}`);
9813
10358
  });
9814
10359
  command.command("check").description("Run health checks for the configured providers.").action(async () => {
9815
- const config = await loadConfig(import_node_process6.default.cwd());
10360
+ const config = await loadConfig(import_node_process7.default.cwd());
9816
10361
  if (!config) {
9817
10362
  requireConfigMessage3();
9818
10363
  }
9819
- const registry = createDefaultBrainProviderRegistry(import_node_process6.default.cwd());
10364
+ const registry = createDefaultBrainProviderRegistry(import_node_process7.default.cwd());
9820
10365
  for (const provider of config.providers) {
9821
10366
  try {
9822
10367
  const result = await registry.healthCheck(provider);
@@ -9831,52 +10376,67 @@ function createProvidersCommand() {
9831
10376
  }
9832
10377
 
9833
10378
  // src/commands/resume.ts
9834
- 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
+ }
9835
10387
  function createResumeCommand() {
9836
- return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).action(async (options) => {
9837
- 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);
9838
10391
  if (!session) {
9839
10392
  console.error("No saved session found. Run `kimbho run` first.");
9840
- import_node_process7.default.exitCode = 1;
10393
+ import_node_process8.default.exitCode = 1;
9841
10394
  return;
9842
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
+ }
9843
10402
  if (options.json) {
9844
- console.log(renderJson(session));
10403
+ console.log(renderJson(snapshot));
9845
10404
  return;
9846
10405
  }
9847
- console.log(`Session: ${session.id}`);
9848
- console.log(`Goal: ${session.goal}`);
9849
- console.log(`Status: ${session.status}`);
9850
- console.log(`Ready tasks: ${session.readyTaskIds.join(", ") || "none"}`);
9851
- console.log(`Blocked tasks: ${session.blockedTaskIds.join(", ") || "none"}`);
9852
- console.log(`Assigned agents: ${session.assignedAgents.join(", ") || "none"}`);
9853
- console.log(`Notes: ${session.notes.join(" | ") || "none"}`);
10406
+ console.log(renderSession(snapshot));
9854
10407
  });
9855
10408
  }
9856
10409
 
9857
10410
  // src/commands/run.ts
9858
- var import_promises5 = require("node:fs/promises");
9859
- 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);
9860
10413
  async function detectWorkspaceState2(cwd) {
9861
- const entries = await (0, import_promises5.readdir)(cwd);
10414
+ const entries = await (0, import_promises7.readdir)(cwd);
9862
10415
  const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
9863
10416
  return meaningfulEntries.length === 0 ? "empty" : "existing";
9864
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
+ }
9865
10425
  function createRunCommand() {
9866
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(
9867
10427
  "--constraint <constraint>",
9868
10428
  "Explicit execution constraint; can be provided multiple times",
9869
10429
  (value, previous = []) => [...previous, value],
9870
10430
  []
9871
- ).option("--json", "Print the session snapshot as JSON", false).action(async (goal, options) => {
9872
- 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();
9873
10433
  const orchestrator = new ExecutionOrchestrator();
9874
10434
  let request;
9875
10435
  let planPath = null;
9876
10436
  let plan = goal ? null : await loadLatestPlan(cwd);
9877
10437
  if (!plan && !goal) {
9878
10438
  console.error("No saved plan found. Pass a goal or run `kimbho plan` first.");
9879
- import_node_process8.default.exitCode = 1;
10439
+ import_node_process9.default.exitCode = 1;
9880
10440
  return;
9881
10441
  }
9882
10442
  if (goal) {
@@ -9901,7 +10461,10 @@ function createRunCommand() {
9901
10461
  };
9902
10462
  }
9903
10463
  const envelope = orchestrator.buildEnvelope(request, plan);
9904
- 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
+ });
9905
10468
  const sessionPath = await saveSession(snapshot, cwd);
9906
10469
  if (options.json) {
9907
10470
  console.log(
@@ -9915,15 +10478,12 @@ function createRunCommand() {
9915
10478
  }
9916
10479
  console.log(renderPlan(plan));
9917
10480
  console.log("");
9918
- console.log(`Session: ${snapshot.id}`);
9919
- console.log(`Saved session: ${sessionPath}`);
10481
+ console.log(renderSession(snapshot));
10482
+ console.log("");
9920
10483
  if (planPath) {
9921
10484
  console.log(`Saved plan: ${planPath}`);
9922
10485
  }
9923
- console.log(`Assigned agents: ${snapshot.assignedAgents.join(", ") || "none"}`);
9924
- console.log(`Ready tasks: ${snapshot.readyTaskIds.join(", ") || "none"}`);
9925
- console.log(`Blocked tasks: ${snapshot.blockedTaskIds.join(", ") || "none"}`);
9926
- 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}`);
9927
10487
  });
9928
10488
  }
9929
10489
 
@@ -9948,36 +10508,398 @@ function createReviewCommand() {
9948
10508
  );
9949
10509
  }
9950
10510
 
9951
- // src/index.ts
9952
- var program2 = new Command();
9953
- program2.name("kimbho").description("Kimbho CLI: a terminal-native coding agent for planning and execution.").version("0.1.0");
9954
- program2.addCommand(createInitCommand());
9955
- program2.addCommand(createPlanCommand());
9956
- program2.addCommand(createDoctorCommand());
9957
- program2.addCommand(createAgentsCommand());
9958
- program2.addCommand(createProvidersCommand());
9959
- program2.addCommand(createModelsCommand());
9960
- program2.addCommand(createBrainsCommand());
9961
- program2.addCommand(createRunCommand());
9962
- program2.addCommand(createResumeCommand());
9963
- program2.addCommand(createFixCommand());
9964
- program2.addCommand(createReviewCommand());
9965
- var normalizedArgv = process.argv.map(
9966
- (value, index) => index === 2 && value.startsWith("/") ? value.slice(1) : value
9967
- );
9968
- var modelsSubcommands = /* @__PURE__ */ new Set([
10511
+ // src/program.ts
10512
+ var MODELS_SUBCOMMANDS = /* @__PURE__ */ new Set([
9969
10513
  "help",
9970
10514
  "list",
9971
10515
  "sync",
9972
10516
  "use"
9973
10517
  ]);
9974
- if (normalizedArgv[2] === "models") {
9975
- const candidate = normalizedArgv[3];
9976
- if (!candidate || candidate.startsWith("-") || !modelsSubcommands.has(candidate)) {
9977
- normalizedArgv.splice(3, 0, "list");
10518
+ function normalizeCliTokens(tokens) {
10519
+ const normalized = [...tokens];
10520
+ if (normalized[0]?.startsWith("/")) {
10521
+ normalized[0] = normalized[0].slice(1);
10522
+ }
10523
+ if (normalized[0] === "models") {
10524
+ const candidate = normalized[1];
10525
+ if (!candidate || candidate.startsWith("-") || !MODELS_SUBCOMMANDS.has(candidate)) {
10526
+ normalized.splice(1, 0, "list");
10527
+ }
10528
+ }
10529
+ return normalized;
10530
+ }
10531
+ function createProgram(onOpenShell) {
10532
+ const program2 = new Command();
10533
+ program2.name("kimbho").description(KIMBHO_DESCRIPTION).version(KIMBHO_VERSION);
10534
+ program2.addCommand(createInitCommand());
10535
+ program2.addCommand(createPlanCommand());
10536
+ program2.addCommand(createDoctorCommand());
10537
+ program2.addCommand(createAgentsCommand());
10538
+ program2.addCommand(createProvidersCommand());
10539
+ program2.addCommand(createModelsCommand());
10540
+ program2.addCommand(createBrainsCommand());
10541
+ program2.addCommand(createRunCommand());
10542
+ program2.addCommand(createResumeCommand());
10543
+ program2.addCommand(createFixCommand());
10544
+ program2.addCommand(createReviewCommand());
10545
+ if (onOpenShell) {
10546
+ program2.command("shell").description("Open the interactive Kimbho shell.").action(async () => {
10547
+ await onOpenShell();
10548
+ });
10549
+ }
10550
+ return program2;
10551
+ }
10552
+
10553
+ // src/shell.ts
10554
+ var import_promises8 = require("node:readline/promises");
10555
+ var import_node_process10 = __toESM(require("node:process"), 1);
10556
+ var AMBER = "\x1B[38;5;214m";
10557
+ var BOLD = "\x1B[1m";
10558
+ var DIM = "\x1B[2m";
10559
+ var RESET = "\x1B[0m";
10560
+ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
10561
+ "agents",
10562
+ "brains",
10563
+ "doctor",
10564
+ "fix",
10565
+ "help",
10566
+ "init",
10567
+ "model",
10568
+ "models",
10569
+ "new",
10570
+ "plan",
10571
+ "providers",
10572
+ "quit",
10573
+ "resume",
10574
+ "review",
10575
+ "run",
10576
+ "scaffold",
10577
+ "shell",
10578
+ "status"
10579
+ ]);
10580
+ function color(code, value) {
10581
+ return `${code}${value}${RESET}`;
10582
+ }
10583
+ function renderBanner() {
10584
+ return [
10585
+ " _ ___ __ __ ____ ____ _ _ ___ ",
10586
+ "| |/ (_) \\/ | __ )| _ \\| | | |/ _ \\",
10587
+ "| ' /| | |\\/| | _ \\| | | | |_| | | | |",
10588
+ "| . \\| | | | | |_) | |_| | _ | |_| |",
10589
+ "|_|\\_\\_|_| |_|____/|____/|_| |_|\\___/ "
10590
+ ].map((line) => color(AMBER, line)).join("\n");
10591
+ }
10592
+ async function getShellSessionState(cwd) {
10593
+ const config = await loadConfig(cwd);
10594
+ if (!config) {
10595
+ return {
10596
+ providerLabel: "unconfigured",
10597
+ providerId: "-",
10598
+ coderModel: "not set",
10599
+ plannerModel: "not set",
10600
+ approvalMode: "manual",
10601
+ sandboxMode: "workspace-write",
10602
+ stackPreset: "next-prisma-postgres",
10603
+ configured: false
10604
+ };
10605
+ }
10606
+ const coderSettings = config.brains.coder;
10607
+ const provider = findProviderById(config, coderSettings.providerId);
10608
+ return {
10609
+ providerLabel: provider?.label ?? provider?.id ?? "unknown",
10610
+ providerId: provider?.id ?? coderSettings.providerId,
10611
+ coderModel: resolveBrainModel(config, "coder") ?? "not set",
10612
+ plannerModel: resolveBrainModel(config, "planner") ?? "not set",
10613
+ approvalMode: config.approvalMode,
10614
+ sandboxMode: config.sandboxMode,
10615
+ stackPreset: config.stackPresets[0] ?? "none",
10616
+ configured: true
10617
+ };
10618
+ }
10619
+ function renderCardLine(label, value) {
10620
+ return `${label}: ${value}`;
10621
+ }
10622
+ function shortenMiddle(value, maxLength) {
10623
+ if (value.length <= maxLength) {
10624
+ return value;
10625
+ }
10626
+ const visible = maxLength - 3;
10627
+ const left = Math.ceil(visible / 2);
10628
+ const right = Math.floor(visible / 2);
10629
+ return `${value.slice(0, left)}...${value.slice(value.length - right)}`;
10630
+ }
10631
+ function renderBox(lines) {
10632
+ const width = lines.reduce((max, line) => Math.max(max, line.length), 0);
10633
+ const top = `+${"-".repeat(width + 2)}+`;
10634
+ return [
10635
+ top,
10636
+ ...lines.map((line) => `| ${line.padEnd(width, " ")} |`),
10637
+ top
10638
+ ].join("\n");
10639
+ }
10640
+ function renderHelp() {
10641
+ return [
10642
+ `${color(DIM, "Commands")}`,
10643
+ "/status Show the active model, provider, and workspace state.",
10644
+ "/plan <goal> Create a structured implementation plan.",
10645
+ "/run <goal> Start a Kimbho execution session for a goal.",
10646
+ "/new <goal> Alias for /run <goal>.",
10647
+ "/scaffold <goal> Alias for /run <goal> with scaffolding intent.",
10648
+ "/resume Show the latest saved session.",
10649
+ "/agents Inspect agent roles and the active session.",
10650
+ "/providers Manage model providers.",
10651
+ "/model Show the active planner/coder model selection.",
10652
+ "/models Discover and select provider models.",
10653
+ "/brains Inspect or assign planner/coder/reviewer brains.",
10654
+ "/doctor Check local environment and config.",
10655
+ "/clear Redraw the shell.",
10656
+ "/quit, /exit Leave the shell.",
10657
+ "",
10658
+ `${color(DIM, "Tip")}`,
10659
+ "Type a natural-language goal directly and Kimbho will treat it as /run <goal>."
10660
+ ].join("\n");
10661
+ }
10662
+ function renderStartupCard(cwd, state) {
10663
+ const cardLines = [
10664
+ `${color(BOLD, "Kimbho CLI")} (v${KIMBHO_VERSION})`,
10665
+ renderCardLine("coder", state.coderModel),
10666
+ renderCardLine("planner", state.plannerModel),
10667
+ renderCardLine("provider", `${state.providerLabel} [${state.providerId}]`),
10668
+ renderCardLine("directory", shortenMiddle(cwd, 78)),
10669
+ renderCardLine("approval", state.approvalMode),
10670
+ renderCardLine("sandbox", state.sandboxMode),
10671
+ renderCardLine("preset", state.stackPreset),
10672
+ renderCardLine("shortcuts", "/help /status /plan /run /models /brains /quit")
10673
+ ];
10674
+ if (!state.configured) {
10675
+ cardLines.push("setup: run /init to create .kimbho/config.json");
10676
+ }
10677
+ return renderBox(cardLines);
10678
+ }
10679
+ function formatPrompt(state) {
10680
+ const coderModel = state.coderModel === "not set" ? "unconfigured" : shortenMiddle(state.coderModel, 18);
10681
+ return `${color(AMBER, "kimbho")} ${color(DIM, `[${coderModel}]`)} > `;
10682
+ }
10683
+ function printHeader(cwd, state) {
10684
+ console.log(renderBanner());
10685
+ console.log(color(DIM, "Terminal-native coding agent"));
10686
+ console.log(renderStartupCard(cwd, state));
10687
+ console.log("");
10688
+ console.log(color(DIM, "Tip: describe the app or repo change you want, and Kimbho will route it into /run."));
10689
+ console.log(renderHelp());
10690
+ console.log("");
10691
+ }
10692
+ function tokenizeInput(input) {
10693
+ const tokens = [];
10694
+ let current = "";
10695
+ let quote = null;
10696
+ let escaping = false;
10697
+ for (const character of input) {
10698
+ if (escaping) {
10699
+ current += character;
10700
+ escaping = false;
10701
+ continue;
10702
+ }
10703
+ if (character === "\\") {
10704
+ escaping = true;
10705
+ continue;
10706
+ }
10707
+ if (quote) {
10708
+ if (character === quote) {
10709
+ quote = null;
10710
+ } else {
10711
+ current += character;
10712
+ }
10713
+ continue;
10714
+ }
10715
+ if (character === "'" || character === '"') {
10716
+ quote = character;
10717
+ continue;
10718
+ }
10719
+ if (/\s/.test(character)) {
10720
+ if (current) {
10721
+ tokens.push(current);
10722
+ current = "";
10723
+ }
10724
+ continue;
10725
+ }
10726
+ current += character;
10727
+ }
10728
+ if (quote) {
10729
+ throw new Error(`Unterminated ${quote === '"' ? "double" : "single"} quote.`);
10730
+ }
10731
+ if (escaping) {
10732
+ current += "\\";
10733
+ }
10734
+ if (current) {
10735
+ tokens.push(current);
10736
+ }
10737
+ return tokens;
10738
+ }
10739
+ function toCommandTokens(input) {
10740
+ const trimmed = input.trim();
10741
+ if (!trimmed) {
10742
+ return [];
10743
+ }
10744
+ const normalizedInput = trimmed.startsWith("kimbho ") ? trimmed.slice("kimbho ".length).trim() : trimmed;
10745
+ const tokens = tokenizeInput(normalizedInput);
10746
+ if (tokens.length === 0) {
10747
+ return [];
10748
+ }
10749
+ const firstToken = tokens[0];
10750
+ if (!firstToken) {
10751
+ return [];
10752
+ }
10753
+ const head = firstToken.startsWith("/") ? firstToken.slice(1) : firstToken;
10754
+ if (head === "new") {
10755
+ return [
10756
+ "run",
10757
+ normalizedInput.replace(/^\/?new\s+/, "")
10758
+ ];
10759
+ }
10760
+ if (head === "scaffold") {
10761
+ const goal = normalizedInput.replace(/^\/?scaffold\s+/, "");
10762
+ return [
10763
+ "run",
10764
+ `scaffold ${goal}`.trim()
10765
+ ];
10766
+ }
10767
+ if (head === "model") {
10768
+ return [
10769
+ "brains",
10770
+ "list"
10771
+ ];
9978
10772
  }
10773
+ if (!firstToken.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
10774
+ return [
10775
+ "run",
10776
+ normalizedInput
10777
+ ];
10778
+ }
10779
+ tokens[0] = head;
10780
+ return tokens;
10781
+ }
10782
+ async function runInteractiveShell(options) {
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)
10787
+ });
10788
+ let closed = false;
10789
+ readline.on("SIGINT", () => {
10790
+ closed = true;
10791
+ readline.close();
10792
+ });
10793
+ let state = await getShellSessionState(options.cwd);
10794
+ printHeader(options.cwd, state);
10795
+ while (!closed) {
10796
+ let line;
10797
+ try {
10798
+ state = await getShellSessionState(options.cwd);
10799
+ line = await readline.question(formatPrompt(state));
10800
+ } catch {
10801
+ break;
10802
+ }
10803
+ const trimmed = line.trim();
10804
+ if (!trimmed) {
10805
+ continue;
10806
+ }
10807
+ if (trimmed === "/exit" || trimmed === "exit" || trimmed === "quit" || trimmed === "/quit") {
10808
+ closed = true;
10809
+ break;
10810
+ }
10811
+ if (trimmed === "/help" || trimmed === "help" || trimmed === "?") {
10812
+ console.log(renderHelp());
10813
+ console.log("");
10814
+ continue;
10815
+ }
10816
+ if (trimmed === "/status" || trimmed === "status") {
10817
+ state = await getShellSessionState(options.cwd);
10818
+ console.log(renderStartupCard(options.cwd, state));
10819
+ console.log("");
10820
+ continue;
10821
+ }
10822
+ if (trimmed === "/clear" || trimmed === "clear") {
10823
+ if (import_node_process10.default.stdout.isTTY) {
10824
+ import_node_process10.default.stdout.write("\x1Bc");
10825
+ }
10826
+ state = await getShellSessionState(options.cwd);
10827
+ printHeader(options.cwd, state);
10828
+ continue;
10829
+ }
10830
+ try {
10831
+ const tokens = toCommandTokens(trimmed);
10832
+ if (tokens.length === 0) {
10833
+ continue;
10834
+ }
10835
+ await options.execute(tokens);
10836
+ } catch (error) {
10837
+ const message = error instanceof Error ? error.message : String(error);
10838
+ console.error(message);
10839
+ } finally {
10840
+ import_node_process10.default.exitCode = 0;
10841
+ }
10842
+ if (!closed) {
10843
+ console.log("");
10844
+ }
10845
+ }
10846
+ readline.close();
10847
+ import_node_process10.default.exitCode = 0;
10848
+ console.log(color(DIM, "Leaving Kimbho."));
10849
+ }
10850
+
10851
+ // src/index.ts
10852
+ async function dispatchCliTokens(tokens) {
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
+ });
10873
+ }
10874
+ async function openShell() {
10875
+ await runInteractiveShell({
10876
+ cwd: process.cwd(),
10877
+ execute: dispatchCliTokens
10878
+ });
10879
+ }
10880
+ async function main() {
10881
+ const tokens = normalizeCliTokens(process.argv.slice(2));
10882
+ if (tokens.length === 0) {
10883
+ if (process.stdin.isTTY && process.stdout.isTTY) {
10884
+ await openShell();
10885
+ return;
10886
+ }
10887
+ const helpProgram = createProgram(openShell);
10888
+ await helpProgram.parseAsync([
10889
+ "node",
10890
+ "kimbho",
10891
+ "--help"
10892
+ ]);
10893
+ return;
10894
+ }
10895
+ const program2 = createProgram(openShell);
10896
+ await program2.parseAsync([
10897
+ "node",
10898
+ "kimbho",
10899
+ ...tokens
10900
+ ]);
9979
10901
  }
9980
- program2.parseAsync(normalizedArgv).catch((error) => {
10902
+ main().catch((error) => {
9981
10903
  const message = error instanceof Error ? error.message : String(error);
9982
10904
  console.error(message);
9983
10905
  process.exitCode = 1;