@kimbho/kimbho-cli 0.1.14 → 0.1.16

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,7 +1147,7 @@ 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 path21 = require("node:path");
1150
+ var path22 = require("node:path");
1151
1151
  var fs = require("node:fs");
1152
1152
  var process20 = require("node:process");
1153
1153
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -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 = path21.resolve(baseDir, baseName);
2150
+ const localBin = path22.resolve(baseDir, baseName);
2151
2151
  if (fs.existsSync(localBin)) return localBin;
2152
- if (sourceExt.includes(path21.extname(baseName))) return void 0;
2152
+ if (sourceExt.includes(path22.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 = path21.resolve(
2171
- path21.dirname(resolvedScriptPath),
2170
+ executableDir = path22.resolve(
2171
+ path22.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 = path21.basename(
2178
+ const legacyName = path22.basename(
2179
2179
  this._scriptPath,
2180
- path21.extname(this._scriptPath)
2180
+ path22.extname(this._scriptPath)
2181
2181
  );
2182
2182
  if (legacyName !== this._name) {
2183
2183
  localFile = findFile(
@@ -2188,7 +2188,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2188
2188
  }
2189
2189
  executableFile = localFile || executableFile;
2190
2190
  }
2191
- launchWithNode = sourceExt.includes(path21.extname(executableFile));
2191
+ launchWithNode = sourceExt.includes(path22.extname(executableFile));
2192
2192
  let proc;
2193
2193
  if (process20.platform !== "win32") {
2194
2194
  if (launchWithNode) {
@@ -3035,7 +3035,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3035
3035
  * @return {Command}
3036
3036
  */
3037
3037
  nameFromFilename(filename) {
3038
- this._name = path21.basename(filename, path21.extname(filename));
3038
+ this._name = path22.basename(filename, path22.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(path22) {
3053
- if (path22 === void 0) return this._executableDir;
3054
- this._executableDir = path22;
3052
+ executableDir(path23) {
3053
+ if (path23 === void 0) return this._executableDir;
3054
+ this._executableDir = path23;
3055
3055
  return this;
3056
3056
  }
3057
3057
  /**
@@ -6516,8 +6516,8 @@ var require_utils = __commonJS({
6516
6516
  }
6517
6517
  return ind;
6518
6518
  }
6519
- function removeDotSegments(path21) {
6520
- let input = path21;
6519
+ function removeDotSegments(path22) {
6520
+ let input = path22;
6521
6521
  const output = [];
6522
6522
  let nextSlash = -1;
6523
6523
  let len = 0;
@@ -6716,8 +6716,8 @@ var require_schemes = __commonJS({
6716
6716
  wsComponent.secure = void 0;
6717
6717
  }
6718
6718
  if (wsComponent.resourceName) {
6719
- const [path21, query] = wsComponent.resourceName.split("?");
6720
- wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
6719
+ const [path22, query] = wsComponent.resourceName.split("?");
6720
+ wsComponent.path = path22 && path22 !== "/" ? path22 : void 0;
6721
6721
  wsComponent.query = query;
6722
6722
  wsComponent.resourceName = void 0;
6723
6723
  }
@@ -10098,7 +10098,7 @@ var require_windows = __commonJS({
10098
10098
  module2.exports = isexe;
10099
10099
  isexe.sync = sync;
10100
10100
  var fs = require("fs");
10101
- function checkPathExt(path21, options) {
10101
+ function checkPathExt(path22, options) {
10102
10102
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
10103
10103
  if (!pathext) {
10104
10104
  return true;
@@ -10109,25 +10109,25 @@ var require_windows = __commonJS({
10109
10109
  }
10110
10110
  for (var i = 0; i < pathext.length; i++) {
10111
10111
  var p = pathext[i].toLowerCase();
10112
- if (p && path21.substr(-p.length).toLowerCase() === p) {
10112
+ if (p && path22.substr(-p.length).toLowerCase() === p) {
10113
10113
  return true;
10114
10114
  }
10115
10115
  }
10116
10116
  return false;
10117
10117
  }
10118
- function checkStat(stat2, path21, options) {
10118
+ function checkStat(stat2, path22, options) {
10119
10119
  if (!stat2.isSymbolicLink() && !stat2.isFile()) {
10120
10120
  return false;
10121
10121
  }
10122
- return checkPathExt(path21, options);
10122
+ return checkPathExt(path22, options);
10123
10123
  }
10124
- function isexe(path21, options, cb) {
10125
- fs.stat(path21, function(er, stat2) {
10126
- cb(er, er ? false : checkStat(stat2, path21, options));
10124
+ function isexe(path22, options, cb) {
10125
+ fs.stat(path22, function(er, stat2) {
10126
+ cb(er, er ? false : checkStat(stat2, path22, options));
10127
10127
  });
10128
10128
  }
10129
- function sync(path21, options) {
10130
- return checkStat(fs.statSync(path21), path21, options);
10129
+ function sync(path22, options) {
10130
+ return checkStat(fs.statSync(path22), path22, options);
10131
10131
  }
10132
10132
  }
10133
10133
  });
@@ -10138,13 +10138,13 @@ var require_mode = __commonJS({
10138
10138
  module2.exports = isexe;
10139
10139
  isexe.sync = sync;
10140
10140
  var fs = require("fs");
10141
- function isexe(path21, options, cb) {
10142
- fs.stat(path21, function(er, stat2) {
10141
+ function isexe(path22, options, cb) {
10142
+ fs.stat(path22, function(er, stat2) {
10143
10143
  cb(er, er ? false : checkStat(stat2, options));
10144
10144
  });
10145
10145
  }
10146
- function sync(path21, options) {
10147
- return checkStat(fs.statSync(path21), options);
10146
+ function sync(path22, options) {
10147
+ return checkStat(fs.statSync(path22), options);
10148
10148
  }
10149
10149
  function checkStat(stat2, options) {
10150
10150
  return stat2.isFile() && checkMode(stat2, options);
@@ -10177,7 +10177,7 @@ var require_isexe = __commonJS({
10177
10177
  }
10178
10178
  module2.exports = isexe;
10179
10179
  isexe.sync = sync;
10180
- function isexe(path21, options, cb) {
10180
+ function isexe(path22, options, cb) {
10181
10181
  if (typeof options === "function") {
10182
10182
  cb = options;
10183
10183
  options = {};
@@ -10187,7 +10187,7 @@ var require_isexe = __commonJS({
10187
10187
  throw new TypeError("callback not provided");
10188
10188
  }
10189
10189
  return new Promise(function(resolve, reject) {
10190
- isexe(path21, options || {}, function(er, is) {
10190
+ isexe(path22, options || {}, function(er, is) {
10191
10191
  if (er) {
10192
10192
  reject(er);
10193
10193
  } else {
@@ -10196,7 +10196,7 @@ var require_isexe = __commonJS({
10196
10196
  });
10197
10197
  });
10198
10198
  }
10199
- core(path21, options || {}, function(er, is) {
10199
+ core(path22, options || {}, function(er, is) {
10200
10200
  if (er) {
10201
10201
  if (er.code === "EACCES" || options && options.ignoreErrors) {
10202
10202
  er = null;
@@ -10206,9 +10206,9 @@ var require_isexe = __commonJS({
10206
10206
  cb(er, is);
10207
10207
  });
10208
10208
  }
10209
- function sync(path21, options) {
10209
+ function sync(path22, options) {
10210
10210
  try {
10211
- return core.sync(path21, options || {});
10211
+ return core.sync(path22, options || {});
10212
10212
  } catch (er) {
10213
10213
  if (options && options.ignoreErrors || er.code === "EACCES") {
10214
10214
  return false;
@@ -10224,7 +10224,7 @@ var require_isexe = __commonJS({
10224
10224
  var require_which = __commonJS({
10225
10225
  "../../node_modules/which/which.js"(exports2, module2) {
10226
10226
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
10227
- var path21 = require("path");
10227
+ var path22 = require("path");
10228
10228
  var COLON = isWindows ? ";" : ":";
10229
10229
  var isexe = require_isexe();
10230
10230
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -10262,7 +10262,7 @@ var require_which = __commonJS({
10262
10262
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
10263
10263
  const ppRaw = pathEnv[i];
10264
10264
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
10265
- const pCmd = path21.join(pathPart, cmd);
10265
+ const pCmd = path22.join(pathPart, cmd);
10266
10266
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
10267
10267
  resolve(subStep(p, i, 0));
10268
10268
  });
@@ -10289,7 +10289,7 @@ var require_which = __commonJS({
10289
10289
  for (let i = 0; i < pathEnv.length; i++) {
10290
10290
  const ppRaw = pathEnv[i];
10291
10291
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
10292
- const pCmd = path21.join(pathPart, cmd);
10292
+ const pCmd = path22.join(pathPart, cmd);
10293
10293
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
10294
10294
  for (let j = 0; j < pathExt.length; j++) {
10295
10295
  const cur = p + pathExt[j];
@@ -10337,7 +10337,7 @@ var require_path_key = __commonJS({
10337
10337
  var require_resolveCommand = __commonJS({
10338
10338
  "../../node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
10339
10339
  "use strict";
10340
- var path21 = require("path");
10340
+ var path22 = require("path");
10341
10341
  var which = require_which();
10342
10342
  var getPathKey = require_path_key();
10343
10343
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -10355,7 +10355,7 @@ var require_resolveCommand = __commonJS({
10355
10355
  try {
10356
10356
  resolved = which.sync(parsed.command, {
10357
10357
  path: env[getPathKey({ env })],
10358
- pathExt: withoutPathExt ? path21.delimiter : void 0
10358
+ pathExt: withoutPathExt ? path22.delimiter : void 0
10359
10359
  });
10360
10360
  } catch (e) {
10361
10361
  } finally {
@@ -10364,7 +10364,7 @@ var require_resolveCommand = __commonJS({
10364
10364
  }
10365
10365
  }
10366
10366
  if (resolved) {
10367
- resolved = path21.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
10367
+ resolved = path22.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
10368
10368
  }
10369
10369
  return resolved;
10370
10370
  }
@@ -10418,8 +10418,8 @@ var require_shebang_command = __commonJS({
10418
10418
  if (!match) {
10419
10419
  return null;
10420
10420
  }
10421
- const [path21, argument] = match[0].replace(/#! ?/, "").split(" ");
10422
- const binary = path21.split("/").pop();
10421
+ const [path22, argument] = match[0].replace(/#! ?/, "").split(" ");
10422
+ const binary = path22.split("/").pop();
10423
10423
  if (binary === "env") {
10424
10424
  return argument;
10425
10425
  }
@@ -10454,7 +10454,7 @@ var require_readShebang = __commonJS({
10454
10454
  var require_parse = __commonJS({
10455
10455
  "../../node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
10456
10456
  "use strict";
10457
- var path21 = require("path");
10457
+ var path22 = require("path");
10458
10458
  var resolveCommand = require_resolveCommand();
10459
10459
  var escape2 = require_escape();
10460
10460
  var readShebang = require_readShebang();
@@ -10479,7 +10479,7 @@ var require_parse = __commonJS({
10479
10479
  const needsShell = !isExecutableRegExp.test(commandFile);
10480
10480
  if (parsed.options.forceShell || needsShell) {
10481
10481
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
10482
- parsed.command = path21.normalize(parsed.command);
10482
+ parsed.command = path22.normalize(parsed.command);
10483
10483
  parsed.command = escape2.command(parsed.command);
10484
10484
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
10485
10485
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -10612,7 +10612,7 @@ var {
10612
10612
  // package.json
10613
10613
  var package_default = {
10614
10614
  name: "@kimbho/kimbho-cli",
10615
- version: "0.1.14",
10615
+ version: "0.1.16",
10616
10616
  description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
10617
10617
  type: "module",
10618
10618
  engines: {
@@ -11406,8 +11406,8 @@ function getErrorMap() {
11406
11406
 
11407
11407
  // ../../node_modules/zod/v3/helpers/parseUtil.js
11408
11408
  var makeIssue = (params) => {
11409
- const { data, path: path21, errorMaps, issueData } = params;
11410
- const fullPath = [...path21, ...issueData.path || []];
11409
+ const { data, path: path22, errorMaps, issueData } = params;
11410
+ const fullPath = [...path22, ...issueData.path || []];
11411
11411
  const fullIssue = {
11412
11412
  ...issueData,
11413
11413
  path: fullPath
@@ -11523,11 +11523,11 @@ var errorUtil;
11523
11523
 
11524
11524
  // ../../node_modules/zod/v3/types.js
11525
11525
  var ParseInputLazyPath = class {
11526
- constructor(parent, value, path21, key) {
11526
+ constructor(parent, value, path22, key) {
11527
11527
  this._cachedPath = [];
11528
11528
  this.parent = parent;
11529
11529
  this.data = value;
11530
- this._path = path21;
11530
+ this._path = path22;
11531
11531
  this._key = key;
11532
11532
  }
11533
11533
  get path() {
@@ -15026,7 +15026,10 @@ var PlanTaskSchema = external_exports.object({
15026
15026
  filesLikelyTouched: external_exports.array(external_exports.string()).default([]),
15027
15027
  riskLevel: RiskLevelSchema.default("medium"),
15028
15028
  originTaskId: external_exports.string().min(1).optional(),
15029
- swarmDepth: external_exports.number().int().nonnegative().optional()
15029
+ swarmDepth: external_exports.number().int().nonnegative().optional(),
15030
+ customAgentId: external_exports.string().min(1).optional(),
15031
+ teamId: external_exports.string().min(1).optional(),
15032
+ teamMemberIds: external_exports.array(external_exports.string()).optional()
15030
15033
  });
15031
15034
  var PlanMilestoneSchema = external_exports.object({
15032
15035
  id: external_exports.string().min(1),
@@ -15437,6 +15440,7 @@ function summarizeStructuredOutputError(context, error2) {
15437
15440
 
15438
15441
  // ../core/dist/config/config.js
15439
15442
  var import_promises = require("node:fs/promises");
15443
+ var import_node_os = __toESM(require("node:os"), 1);
15440
15444
  var import_node_path = __toESM(require("node:path"), 1);
15441
15445
  var KIMBHO_DIR_NAME = ".kimbho";
15442
15446
  function resolveKimbhoDir(cwd = process.cwd()) {
@@ -15445,6 +15449,9 @@ function resolveKimbhoDir(cwd = process.cwd()) {
15445
15449
  function resolveConfigPath(cwd = process.cwd()) {
15446
15450
  return import_node_path.default.join(resolveKimbhoDir(cwd), "config.json");
15447
15451
  }
15452
+ function resolveGlobalConfigPath() {
15453
+ return import_node_path.default.join(import_node_os.default.homedir(), ".kimbho", "config.json");
15454
+ }
15448
15455
  var LegacyProviderNameSchema = external_exports.enum([
15449
15456
  "openai",
15450
15457
  "anthropic",
@@ -15599,8 +15606,7 @@ async function ensureKimbhoDir(cwd = process.cwd()) {
15599
15606
  await (0, import_promises.mkdir)(import_node_path.default.join(root, "teams"), { recursive: true });
15600
15607
  return root;
15601
15608
  }
15602
- async function loadConfig(cwd = process.cwd()) {
15603
- const configPath = resolveConfigPath(cwd);
15609
+ async function loadConfigFile(configPath) {
15604
15610
  try {
15605
15611
  const raw = await (0, import_promises.readFile)(configPath, "utf8");
15606
15612
  return KimbhoConfigSchema.parse(normalizeConfigInput(JSON.parse(raw)));
@@ -15612,6 +15618,77 @@ async function loadConfig(cwd = process.cwd()) {
15612
15618
  throw error2;
15613
15619
  }
15614
15620
  }
15621
+ async function loadProjectConfig(cwd = process.cwd()) {
15622
+ return loadConfigFile(resolveConfigPath(cwd));
15623
+ }
15624
+ async function loadUserConfig() {
15625
+ return loadConfigFile(resolveGlobalConfigPath());
15626
+ }
15627
+ function mergeConfigCatalog(lower, upper) {
15628
+ const merged = /* @__PURE__ */ new Map();
15629
+ for (const provider of lower) {
15630
+ merged.set(provider.id, provider);
15631
+ }
15632
+ for (const provider of upper) {
15633
+ merged.set(provider.id, provider);
15634
+ }
15635
+ return Array.from(merged.values());
15636
+ }
15637
+ function mergeConfigLayers(base, override) {
15638
+ return KimbhoConfigSchema.parse({
15639
+ providers: mergeConfigCatalog(base.providers, override.providers),
15640
+ brains: {
15641
+ planner: {
15642
+ ...base.brains.planner,
15643
+ ...override.brains.planner
15644
+ },
15645
+ coder: {
15646
+ ...base.brains.coder,
15647
+ ...override.brains.coder
15648
+ },
15649
+ reviewer: {
15650
+ ...base.brains.reviewer,
15651
+ ...override.brains.reviewer
15652
+ },
15653
+ fast: {
15654
+ ...base.brains.fast,
15655
+ ...override.brains.fast
15656
+ }
15657
+ },
15658
+ approvalMode: override.approvalMode ?? base.approvalMode,
15659
+ sandboxMode: override.sandboxMode ?? base.sandboxMode,
15660
+ stackPresets: Array.from(/* @__PURE__ */ new Set([
15661
+ ...base.stackPresets,
15662
+ ...override.stackPresets
15663
+ ])),
15664
+ trustedDirectories: Array.from(/* @__PURE__ */ new Set([
15665
+ ...base.trustedDirectories,
15666
+ ...override.trustedDirectories
15667
+ ]))
15668
+ });
15669
+ }
15670
+ async function loadConfigLayers(cwd = process.cwd()) {
15671
+ const [user, project] = await Promise.all([
15672
+ loadUserConfig(),
15673
+ loadProjectConfig(cwd)
15674
+ ]);
15675
+ if (!user && !project) {
15676
+ return {
15677
+ user: null,
15678
+ project: null,
15679
+ resolved: null
15680
+ };
15681
+ }
15682
+ return {
15683
+ user,
15684
+ project,
15685
+ resolved: user && project ? mergeConfigLayers(user, project) : project ?? user
15686
+ };
15687
+ }
15688
+ async function loadConfig(cwd = process.cwd()) {
15689
+ const layers = await loadConfigLayers(cwd);
15690
+ return layers.resolved;
15691
+ }
15615
15692
  async function saveConfig(config2, cwd = process.cwd()) {
15616
15693
  await ensureKimbhoDir(cwd);
15617
15694
  const configPath = resolveConfigPath(cwd);
@@ -15622,6 +15699,16 @@ async function saveConfig(config2, cwd = process.cwd()) {
15622
15699
  await (0, import_promises.rename)(tempPath, configPath);
15623
15700
  return configPath;
15624
15701
  }
15702
+ async function saveUserConfig(config2) {
15703
+ const configPath = resolveGlobalConfigPath();
15704
+ await (0, import_promises.mkdir)(import_node_path.default.dirname(configPath), { recursive: true });
15705
+ const normalized = KimbhoConfigSchema.parse(config2);
15706
+ const tempPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
15707
+ await (0, import_promises.writeFile)(tempPath, `${JSON.stringify(normalized, null, 2)}
15708
+ `, "utf8");
15709
+ await (0, import_promises.rename)(tempPath, configPath);
15710
+ return configPath;
15711
+ }
15625
15712
  function findProviderById(config2, providerId) {
15626
15713
  return config2.providers.find((provider) => provider.id === providerId);
15627
15714
  }
@@ -15740,10 +15827,10 @@ async function loadLatestSession(cwd = process.cwd()) {
15740
15827
 
15741
15828
  // ../core/dist/memory.js
15742
15829
  var import_promises3 = require("node:fs/promises");
15743
- var import_node_os = __toESM(require("node:os"), 1);
15830
+ var import_node_os2 = __toESM(require("node:os"), 1);
15744
15831
  var import_node_path3 = __toESM(require("node:path"), 1);
15745
15832
  function resolveGlobalKimbhoHome() {
15746
- return import_node_path3.default.join(import_node_os.default.homedir(), ".kimbho");
15833
+ return import_node_path3.default.join(import_node_os2.default.homedir(), ".kimbho");
15747
15834
  }
15748
15835
  function resolveUserMemoryPath() {
15749
15836
  return import_node_path3.default.join(resolveGlobalKimbhoHome(), "memory.md");
@@ -15807,6 +15894,54 @@ ${block}` : `# ${import_node_path3.default.basename(filePath, import_node_path3.
15807
15894
 
15808
15895
  ${block}`);
15809
15896
  }
15897
+ function uniqueOrdered(values) {
15898
+ return Array.from(new Set(values));
15899
+ }
15900
+ function candidateMemoryFilesForDirectory(directory) {
15901
+ return [
15902
+ import_node_path3.default.join(directory, "KIMBHO.md"),
15903
+ import_node_path3.default.join(directory, "KIMBHO.local.md"),
15904
+ import_node_path3.default.join(directory, "CLAUDE.md"),
15905
+ import_node_path3.default.join(directory, "CLAUDE.local.md")
15906
+ ];
15907
+ }
15908
+ function discoverMarkdownMemoryPaths(cwd = process.cwd()) {
15909
+ const discovered = [];
15910
+ let current = import_node_path3.default.resolve(cwd);
15911
+ const root = import_node_path3.default.parse(current).root;
15912
+ while (true) {
15913
+ discovered.push(...candidateMemoryFilesForDirectory(current));
15914
+ if (current === root) {
15915
+ break;
15916
+ }
15917
+ current = import_node_path3.default.dirname(current);
15918
+ }
15919
+ discovered.push(import_node_path3.default.join(cwd, "kimbho_init.md"));
15920
+ discovered.push(resolveProjectMemoryPath(cwd));
15921
+ discovered.push(resolveUserMemoryPath());
15922
+ return uniqueOrdered(discovered);
15923
+ }
15924
+ async function loadMarkdownMemoryContext(cwd = process.cwd(), options = {}) {
15925
+ const candidates = discoverMarkdownMemoryPaths(cwd);
15926
+ const records = [];
15927
+ const maxFiles = options.maxFiles ?? 12;
15928
+ const maxCharsPerFile = options.maxCharsPerFile ?? 6e3;
15929
+ for (const filePath of candidates) {
15930
+ if (records.length >= maxFiles) {
15931
+ break;
15932
+ }
15933
+ const content = await readMarkdownIfExists(filePath);
15934
+ if (!content || content.trim().length === 0) {
15935
+ continue;
15936
+ }
15937
+ records.push({
15938
+ filePath,
15939
+ content: content.length > maxCharsPerFile ? `${content.slice(0, maxCharsPerFile)}
15940
+ ... [truncated]` : content
15941
+ });
15942
+ }
15943
+ return records;
15944
+ }
15810
15945
 
15811
15946
  // ../core/dist/mcp.js
15812
15947
  var import_promises4 = require("node:fs/promises");
@@ -15996,6 +16131,8 @@ async function loadAgentTeams(cwd) {
15996
16131
  id,
15997
16132
  label: typeof attributes.label === "string" ? attributes.label : id,
15998
16133
  agents: asStringArray(attributes.agents),
16134
+ match: asStringArray(attributes.match),
16135
+ strategy: typeof attributes.strategy === "string" && attributes.strategy === "parallel" ? "parallel" : "sequential",
15999
16136
  filePath,
16000
16137
  ...body.trim() ? {
16001
16138
  description: body.trim()
@@ -16044,8 +16181,11 @@ async function createAgentTeamFile(cwd, id, agentIds, label) {
16044
16181
  "---",
16045
16182
  `id: ${id}`,
16046
16183
  `label: ${label ?? id}`,
16184
+ "match:",
16185
+ " - feature",
16047
16186
  "agents:",
16048
16187
  ...agentIds.map((agentId) => ` - ${agentId}`),
16188
+ "strategy: sequential",
16049
16189
  "---",
16050
16190
  "",
16051
16191
  `# ${label ?? id}`,
@@ -16072,11 +16212,46 @@ function scoreCustomAgentMatch(definition, task, request) {
16072
16212
  }
16073
16213
  return definition.match.reduce((score, keyword) => haystack.includes(keyword.toLowerCase()) ? score + 1 : score, 0);
16074
16214
  }
16215
+ function scoreAgentTeamMatch(definition, task, request) {
16216
+ const haystack = [
16217
+ request.goal,
16218
+ task.title,
16219
+ task.description,
16220
+ ...task.acceptanceCriteria
16221
+ ].join(" ").toLowerCase();
16222
+ if (definition.match.length === 0) {
16223
+ return definition.agents.length > 0 ? 1 : -1;
16224
+ }
16225
+ return definition.match.reduce((score, keyword) => haystack.includes(keyword.toLowerCase()) ? score + 1 : score, 0);
16226
+ }
16227
+ async function selectAgentTeam(cwd, task, request) {
16228
+ const [teams, agents] = await Promise.all([
16229
+ loadAgentTeams(cwd),
16230
+ loadCustomAgents(cwd)
16231
+ ]);
16232
+ const selected = teams.map((team) => ({
16233
+ team,
16234
+ score: scoreAgentTeamMatch(team, task, request)
16235
+ })).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.team.id.localeCompare(right.team.id))[0]?.team;
16236
+ if (!selected) {
16237
+ return null;
16238
+ }
16239
+ const members = selected.agents.map((agentId) => agents.find((candidate) => candidate.id === agentId)).filter((candidate) => Boolean(candidate));
16240
+ return {
16241
+ team: selected,
16242
+ members
16243
+ };
16244
+ }
16075
16245
  async function selectCustomAgentOverlay(cwd, task, request, builtInTools) {
16076
- const definitions = await loadCustomAgents(cwd);
16246
+ const selectedTeam = task.teamId ? await selectAgentTeam(cwd, task, request) : await selectAgentTeam(cwd, task, request);
16247
+ const allDefinitions = await loadCustomAgents(cwd);
16248
+ const preferredDefinition = task.customAgentId ? allDefinitions.find((definition) => definition.id === task.customAgentId) : null;
16249
+ const definitions = preferredDefinition ? [
16250
+ preferredDefinition
16251
+ ] : selectedTeam?.members.length ? selectedTeam.members : allDefinitions;
16077
16252
  const ranked = definitions.map((definition) => ({
16078
16253
  definition,
16079
- score: scoreCustomAgentMatch(definition, task, request)
16254
+ score: preferredDefinition && definition.id === preferredDefinition.id ? Number.MAX_SAFE_INTEGER : scoreCustomAgentMatch(definition, task, request)
16080
16255
  })).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.definition.id.localeCompare(right.definition.id));
16081
16256
  const selected = ranked[0]?.definition;
16082
16257
  if (!selected) {
@@ -16090,6 +16265,12 @@ async function selectCustomAgentOverlay(cwd, task, request, builtInTools) {
16090
16265
  purpose: selected.purpose ?? baseProfile.purpose,
16091
16266
  brainRole: selected.brainRole ?? baseProfile.brainRole,
16092
16267
  promptPreamble: [
16268
+ ...selectedTeam ? [
16269
+ `Agent team active: ${selectedTeam.team.label} (${selectedTeam.team.id}).`,
16270
+ `Team strategy: ${selectedTeam.team.strategy}.`,
16271
+ `Team roster: ${selectedTeam.members.map((member) => member.id).join(", ") || "(none)"}.`,
16272
+ selectedTeam.team.description?.trim() ?? ""
16273
+ ].filter((line) => line.length > 0) : [],
16093
16274
  `Custom agent overlay: ${selected.label} (${selected.id}).`,
16094
16275
  `Base role: ${selected.baseRole}.`,
16095
16276
  selected.instructions.trim()
@@ -17311,7 +17492,7 @@ var import_promises11 = require("node:fs/promises");
17311
17492
  var import_node_path11 = __toESM(require("node:path"), 1);
17312
17493
  var import_node_process2 = __toESM(require("node:process"), 1);
17313
17494
  var import_node_child_process2 = require("node:child_process");
17314
- var import_node_os2 = require("node:os");
17495
+ var import_node_os3 = require("node:os");
17315
17496
 
17316
17497
  // ../tools/dist/browser-manager.js
17317
17498
  var import_node_crypto2 = require("node:crypto");
@@ -18109,6 +18290,47 @@ var KNOWN_SCAFFOLD_PRESETS = [
18109
18290
  ];
18110
18291
  function looksLikeStaticLandingGoal(value) {
18111
18292
  const normalized = value.toLowerCase();
18293
+ const backendSignals = [
18294
+ "api",
18295
+ "database",
18296
+ "backend",
18297
+ "server",
18298
+ "migration",
18299
+ "schema",
18300
+ "prisma",
18301
+ "postgres",
18302
+ "sqlite",
18303
+ "auth",
18304
+ "billing",
18305
+ "webhook"
18306
+ ];
18307
+ const pageSignals = [
18308
+ "page",
18309
+ "landing",
18310
+ "lnding",
18311
+ "homepage",
18312
+ "home page",
18313
+ "site",
18314
+ "website",
18315
+ "ui",
18316
+ "hero",
18317
+ "layout",
18318
+ "screen"
18319
+ ];
18320
+ const polishSignals = [
18321
+ "animate",
18322
+ "animated",
18323
+ "animation",
18324
+ "motion",
18325
+ "rich",
18326
+ "richer",
18327
+ "polish",
18328
+ "styled",
18329
+ "styling",
18330
+ "visual",
18331
+ "redesign",
18332
+ "revamp"
18333
+ ];
18112
18334
  if (normalized.includes("landing page") || normalized.includes("landing") || normalized.includes("lnding") || normalized.includes("homepage") || normalized.includes("home page") || normalized.includes("marketing page") || normalized.includes("storefront") || normalized.includes("static")) {
18113
18335
  return true;
18114
18336
  }
@@ -18118,6 +18340,12 @@ function looksLikeStaticLandingGoal(value) {
18118
18340
  if (normalized.includes("restaurant") && (normalized.includes("page") || normalized.includes("site") || normalized.includes("website") || normalized.includes("landing") || normalized.includes("lnding"))) {
18119
18341
  return true;
18120
18342
  }
18343
+ if (pageSignals.some((signal) => normalized.includes(signal)) && polishSignals.some((signal) => normalized.includes(signal)) && !backendSignals.some((signal) => normalized.includes(signal))) {
18344
+ return true;
18345
+ }
18346
+ if (normalized.includes("blog") && (normalized.includes("make") || normalized.includes("improve") || normalized.includes("enhance") || normalized.includes("redesign")) && !backendSignals.some((signal) => normalized.includes(signal))) {
18347
+ return true;
18348
+ }
18121
18349
  return false;
18122
18350
  }
18123
18351
  function sanitizeName(value) {
@@ -19019,7 +19247,7 @@ async function createUnifiedDiffPreview(cwd, targetPath, beforeContent, afterCon
19019
19247
  if (beforeContent === afterContent) {
19020
19248
  return void 0;
19021
19249
  }
19022
- const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os2.tmpdir)(), "kimbho-diff-"));
19250
+ const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os3.tmpdir)(), "kimbho-diff-"));
19023
19251
  const beforePath = import_node_path11.default.join(tempDir, "before");
19024
19252
  const afterPath = import_node_path11.default.join(tempDir, "after");
19025
19253
  const relativeTarget = import_node_path11.default.relative(cwd, targetPath) || import_node_path11.default.basename(targetPath);
@@ -19051,10 +19279,17 @@ async function createUnifiedDiffPreview(cwd, targetPath, beforeContent, afterCon
19051
19279
  await (0, import_promises11.rm)(tempDir, { recursive: true, force: true });
19052
19280
  }
19053
19281
  }
19054
- function resolveWorkspacePath(cwd, filePath) {
19282
+ function isTrustedWorkspacePath(resolved, trustedDirectories = []) {
19283
+ return trustedDirectories.some((trustedDirectory) => {
19284
+ const trustedRoot = import_node_path11.default.resolve(trustedDirectory);
19285
+ const relative = import_node_path11.default.relative(trustedRoot, resolved);
19286
+ return relative === "" || !relative.startsWith("..") && !import_node_path11.default.isAbsolute(relative);
19287
+ });
19288
+ }
19289
+ function resolveWorkspacePath(cwd, filePath, trustedDirectories = []) {
19055
19290
  const resolved = import_node_path11.default.resolve(cwd, filePath);
19056
19291
  const relative = import_node_path11.default.relative(cwd, resolved);
19057
- if (relative.startsWith("..") || import_node_path11.default.isAbsolute(relative)) {
19292
+ if ((relative.startsWith("..") || import_node_path11.default.isAbsolute(relative)) && !isTrustedWorkspacePath(resolved, trustedDirectories)) {
19058
19293
  throw new Error(`Path "${filePath}" escapes the workspace.`);
19059
19294
  }
19060
19295
  return resolved;
@@ -19137,10 +19372,10 @@ function normalizeArtifactRelativePath(cwd, absolutePath) {
19137
19372
  function isProtectedWritePath(relativePath) {
19138
19373
  return PROTECTED_WRITE_PREFIXES.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}/`));
19139
19374
  }
19140
- function extractProspectiveWriteTargets(toolId, input, cwd) {
19375
+ function extractProspectiveWriteTargets(toolId, input, cwd, trustedDirectories = []) {
19141
19376
  if (toolId === "file.write" && typeof input.path === "string") {
19142
19377
  return [
19143
- normalizeArtifactRelativePath(cwd, resolveWorkspacePath(cwd, input.path))
19378
+ normalizeArtifactRelativePath(cwd, resolveWorkspacePath(cwd, input.path, trustedDirectories))
19144
19379
  ];
19145
19380
  }
19146
19381
  if (toolId === "file.patch" && typeof input.patch === "string") {
@@ -19212,7 +19447,7 @@ function enforceToolPolicy(toolId, input, descriptor, context) {
19212
19447
  return null;
19213
19448
  }
19214
19449
  if ((toolId === "file.write" || toolId === "file.patch") && sandboxMode === "workspace-write") {
19215
- const targets = extractProspectiveWriteTargets(toolId, input, context.cwd);
19450
+ const targets = extractProspectiveWriteTargets(toolId, input, context.cwd, context.trustedDirectories);
19216
19451
  const protectedTargets = targets.filter((target) => isProtectedWritePath(target));
19217
19452
  if (protectedTargets.length > 0) {
19218
19453
  return createPolicyFailure(toolId, `Blocked write to protected workspace paths: ${protectedTargets.join(", ")}`);
@@ -19319,7 +19554,7 @@ async function executeFileRead(input, context) {
19319
19554
  if (!rawPath) {
19320
19555
  throw new Error("file.read requires a string path.");
19321
19556
  }
19322
- const targetPath = resolveWorkspacePath(context.cwd, rawPath);
19557
+ const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
19323
19558
  const contents = await (0, import_promises11.readFile)(targetPath, "utf8");
19324
19559
  return ToolResultSchema.parse({
19325
19560
  toolId: "file.read",
@@ -19340,7 +19575,7 @@ async function executeFileList(input, context, timeoutMs) {
19340
19575
  const root = typeof input.path === "string" && input.path.trim().length > 0 ? input.path : ".";
19341
19576
  const pattern = typeof input.pattern === "string" && input.pattern.trim().length > 0 ? input.pattern : null;
19342
19577
  const limit = parseLimitValue(input.limit, 200);
19343
- const searchRoot = resolveWorkspacePath(context.cwd, root);
19578
+ const searchRoot = resolveWorkspacePath(context.cwd, root, context.trustedDirectories);
19344
19579
  const relativeRoot = import_node_path11.default.relative(context.cwd, searchRoot) || ".";
19345
19580
  const rootArg = relativeRoot === "." ? "." : relativeRoot;
19346
19581
  let result;
@@ -19388,7 +19623,7 @@ async function executeFileSearch(input, context, timeoutMs) {
19388
19623
  if (!pattern) {
19389
19624
  throw new Error("file.search requires a non-empty pattern.");
19390
19625
  }
19391
- const searchRoot = resolveWorkspacePath(context.cwd, root);
19626
+ const searchRoot = resolveWorkspacePath(context.cwd, root, context.trustedDirectories);
19392
19627
  const relativeRoot = import_node_path11.default.relative(context.cwd, searchRoot) || ".";
19393
19628
  const rootArg = relativeRoot === "." ? "." : relativeRoot;
19394
19629
  let result;
@@ -19444,7 +19679,7 @@ async function executeFileWrite(input, context) {
19444
19679
  if (content === null) {
19445
19680
  throw new Error("file.write requires a string content field.");
19446
19681
  }
19447
- const targetPath = resolveWorkspacePath(context.cwd, rawPath);
19682
+ const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
19448
19683
  let existed = true;
19449
19684
  let previousContent = "";
19450
19685
  try {
@@ -19522,7 +19757,7 @@ async function executeGitDiff(input, context, timeoutMs) {
19522
19757
  "--unified=3"
19523
19758
  ];
19524
19759
  if (rawPath) {
19525
- const targetPath = resolveWorkspacePath(context.cwd, rawPath);
19760
+ const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
19526
19761
  const relativeTarget = import_node_path11.default.relative(context.cwd, targetPath) || ".";
19527
19762
  args.push("--", relativeTarget);
19528
19763
  }
@@ -19859,7 +20094,7 @@ async function executeProcessStart(input, context) {
19859
20094
  if (!command) {
19860
20095
  throw new Error("process.start requires a command string.");
19861
20096
  }
19862
- const processCwd = rawPath ? resolveWorkspacePath(context.cwd, rawPath) : context.cwd;
20097
+ const processCwd = rawPath ? resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories) : context.cwd;
19863
20098
  const record2 = await startManagedProcess(processCwd, command, name, context.signal);
19864
20099
  return ToolResultSchema.parse({
19865
20100
  toolId: "process.start",
@@ -19957,7 +20192,7 @@ async function executeFilePatch(input, context, timeoutMs) {
19957
20192
  if (!patch) {
19958
20193
  throw new Error("file.patch requires a unified diff in the patch field.");
19959
20194
  }
19960
- const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os2.tmpdir)(), "kimbho-patch-"));
20195
+ const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os3.tmpdir)(), "kimbho-patch-"));
19961
20196
  const patchPath = import_node_path11.default.join(tempDir, "change.patch");
19962
20197
  try {
19963
20198
  await (0, import_promises11.writeFile)(patchPath, patch, "utf8");
@@ -20310,10 +20545,10 @@ function assignProp(target, prop, value) {
20310
20545
  configurable: true
20311
20546
  });
20312
20547
  }
20313
- function getElementAtPath(obj, path21) {
20314
- if (!path21)
20548
+ function getElementAtPath(obj, path22) {
20549
+ if (!path22)
20315
20550
  return obj;
20316
- return path21.reduce((acc, key) => acc?.[key], obj);
20551
+ return path22.reduce((acc, key) => acc?.[key], obj);
20317
20552
  }
20318
20553
  function promiseAllObject(promisesObj) {
20319
20554
  const keys = Object.keys(promisesObj);
@@ -20633,11 +20868,11 @@ function aborted(x, startIndex = 0) {
20633
20868
  }
20634
20869
  return false;
20635
20870
  }
20636
- function prefixIssues(path21, issues) {
20871
+ function prefixIssues(path22, issues) {
20637
20872
  return issues.map((iss) => {
20638
20873
  var _a;
20639
20874
  (_a = iss).path ?? (_a.path = []);
20640
- iss.path.unshift(path21);
20875
+ iss.path.unshift(path22);
20641
20876
  return iss;
20642
20877
  });
20643
20878
  }
@@ -27385,6 +27620,7 @@ var MCP_ALLOWED_ROLES = [
27385
27620
  "reviewer",
27386
27621
  "integrator"
27387
27622
  ];
27623
+ var MCP_INVENTORY_TIMEOUT_MS = 8e3;
27388
27624
  function toMcpToolId(serverName, toolName) {
27389
27625
  return `mcp.${serverName}.${toolName}`;
27390
27626
  }
@@ -27427,7 +27663,7 @@ function renderMcpContent(result) {
27427
27663
  }
27428
27664
  return sections.length > 0 ? sections.join("\n\n") : void 0;
27429
27665
  }
27430
- async function withMcpClient(cwd, name, server, handler) {
27666
+ async function withMcpClient(cwd, name, server, handler, options = {}) {
27431
27667
  const transport = new StdioClientTransport({
27432
27668
  command: server.command,
27433
27669
  args: server.args,
@@ -27446,10 +27682,31 @@ async function withMcpClient(cwd, name, server, handler) {
27446
27682
  name: "kimbho",
27447
27683
  version: "0.1.13"
27448
27684
  });
27449
- await client.connect(transport);
27685
+ const run = async () => {
27686
+ await client.connect(transport);
27687
+ return handler(client);
27688
+ };
27450
27689
  try {
27451
- return await handler(client);
27690
+ if (typeof options.timeoutMs === "number" && options.timeoutMs > 0) {
27691
+ let timer;
27692
+ try {
27693
+ return await Promise.race([
27694
+ run(),
27695
+ new Promise((_, reject) => {
27696
+ timer = setTimeout(() => {
27697
+ reject(new Error(`${options.timeoutLabel ?? `MCP call for ${name}`} timed out after ${options.timeoutMs}ms.`));
27698
+ }, options.timeoutMs);
27699
+ })
27700
+ ]);
27701
+ } finally {
27702
+ if (timer) {
27703
+ clearTimeout(timer);
27704
+ }
27705
+ }
27706
+ }
27707
+ return await run();
27452
27708
  } finally {
27709
+ await client.close().catch(() => void 0);
27453
27710
  await transport.close().catch(() => void 0);
27454
27711
  }
27455
27712
  }
@@ -27458,31 +27715,65 @@ async function loadMcpServerInventory(cwd) {
27458
27715
  const servers = Object.entries(config2.mcpServers).filter(([, server]) => server.enabled !== false).sort(([left], [right]) => left.localeCompare(right));
27459
27716
  const inventory = [];
27460
27717
  for (const [name, server] of servers) {
27461
- const record2 = await withMcpClient(cwd, name, server, async (client) => {
27462
- const [toolsResponse, promptsResponse, resourcesResponse] = await Promise.all([
27463
- client.listTools().catch(() => ({ tools: [] })),
27464
- client.listPrompts().catch(() => ({ prompts: [] })),
27465
- client.listResources().catch(() => ({ resources: [] }))
27466
- ]);
27467
- const instructions = client.getInstructions();
27468
- return {
27718
+ try {
27719
+ const record2 = await withMcpClient(cwd, name, server, async (client) => {
27720
+ const [toolsResponse, promptsResponse, resourcesResponse, resourceTemplatesResponse] = await Promise.all([
27721
+ client.listTools().catch(() => ({ tools: [] })),
27722
+ client.listPrompts().catch(() => ({ prompts: [] })),
27723
+ client.listResources().catch(() => ({ resources: [] })),
27724
+ client.listResourceTemplates().catch(() => ({ resourceTemplates: [] }))
27725
+ ]);
27726
+ const instructions = client.getInstructions();
27727
+ return {
27728
+ name,
27729
+ config: server,
27730
+ tools: (toolsResponse.tools ?? []).map((tool) => ({
27731
+ serverName: name,
27732
+ toolName: tool.name,
27733
+ toolId: toMcpToolId(name, tool.name),
27734
+ description: tool.description ?? `MCP tool ${tool.name} from ${name}.`,
27735
+ permission: permissionFromAnnotations(tool.annotations)
27736
+ })),
27737
+ prompts: (promptsResponse.prompts ?? []).map((prompt) => ({
27738
+ serverName: name,
27739
+ name: prompt.name,
27740
+ description: prompt.description ?? `MCP prompt ${prompt.name} from ${name}.`,
27741
+ arguments: prompt.arguments ?? []
27742
+ })),
27743
+ resources: (resourcesResponse.resources ?? []).map((resource) => ({
27744
+ serverName: name,
27745
+ uri: resource.uri,
27746
+ name: resource.name,
27747
+ description: resource.description,
27748
+ mimeType: resource.mimeType
27749
+ })),
27750
+ resourceTemplates: (resourceTemplatesResponse.resourceTemplates ?? []).map((resourceTemplate) => ({
27751
+ serverName: name,
27752
+ uriTemplate: resourceTemplate.uriTemplate,
27753
+ name: resourceTemplate.name,
27754
+ description: resourceTemplate.description,
27755
+ mimeType: resourceTemplate.mimeType
27756
+ })),
27757
+ ...instructions ? {
27758
+ instructions
27759
+ } : {}
27760
+ };
27761
+ }, {
27762
+ timeoutMs: MCP_INVENTORY_TIMEOUT_MS,
27763
+ timeoutLabel: `MCP inventory for ${name}`
27764
+ });
27765
+ inventory.push(record2);
27766
+ } catch (error2) {
27767
+ inventory.push({
27469
27768
  name,
27470
27769
  config: server,
27471
- tools: (toolsResponse.tools ?? []).map((tool) => ({
27472
- serverName: name,
27473
- toolName: tool.name,
27474
- toolId: toMcpToolId(name, tool.name),
27475
- description: tool.description ?? `MCP tool ${tool.name} from ${name}.`,
27476
- permission: permissionFromAnnotations(tool.annotations)
27477
- })),
27478
- prompts: (promptsResponse.prompts ?? []).map((prompt) => prompt.name),
27479
- resources: (resourcesResponse.resources ?? []).map((resource) => resource.name ?? resource.uri),
27480
- ...instructions ? {
27481
- instructions
27482
- } : {}
27483
- };
27484
- });
27485
- inventory.push(record2);
27770
+ tools: [],
27771
+ prompts: [],
27772
+ resources: [],
27773
+ resourceTemplates: [],
27774
+ warning: error2 instanceof Error ? error2.message : String(error2)
27775
+ });
27776
+ }
27486
27777
  }
27487
27778
  return inventory;
27488
27779
  }
@@ -27513,6 +27804,59 @@ async function callMcpTool(cwd, serverName, toolName, input) {
27513
27804
  artifacts: []
27514
27805
  };
27515
27806
  }
27807
+ function renderPromptContent(result) {
27808
+ return result.messages.map((message) => {
27809
+ if (message.content.type === "text") {
27810
+ return `[${message.role}] ${message.content.text}`;
27811
+ }
27812
+ if (message.content.type === "resource") {
27813
+ const resource = message.content.resource;
27814
+ return `[${message.role}] resource ${resource.uri}
27815
+ ${"text" in resource ? resource.text : `[binary ${resource.mimeType ?? "resource"}]`}`;
27816
+ }
27817
+ if (message.content.type === "resource_link") {
27818
+ return `[${message.role}] resource-link ${message.content.uri}`;
27819
+ }
27820
+ return `[${message.role}] ${JSON.stringify(message.content, null, 2)}`;
27821
+ }).join("\n\n");
27822
+ }
27823
+ async function invokeMcpPrompt(cwd, serverName, promptName, args) {
27824
+ const config2 = await loadMcpConfig(cwd);
27825
+ const server = config2.mcpServers[serverName];
27826
+ if (!server || server.enabled === false) {
27827
+ throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
27828
+ }
27829
+ const result = await withMcpClient(cwd, serverName, server, async (client) => client.getPrompt({
27830
+ name: promptName,
27831
+ arguments: args
27832
+ }));
27833
+ return {
27834
+ serverName,
27835
+ promptName,
27836
+ ...result.description ? {
27837
+ description: result.description
27838
+ } : {},
27839
+ content: renderPromptContent(result)
27840
+ };
27841
+ }
27842
+ async function readMcpResource(cwd, serverName, uri) {
27843
+ const config2 = await loadMcpConfig(cwd);
27844
+ const server = config2.mcpServers[serverName];
27845
+ if (!server || server.enabled === false) {
27846
+ throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
27847
+ }
27848
+ const result = await withMcpClient(cwd, serverName, server, async (client) => client.readResource({
27849
+ uri
27850
+ }));
27851
+ const content = result.contents.map((item) => "text" in item ? `resource: ${item.uri}
27852
+ ${item.text}` : `resource: ${item.uri}
27853
+ [binary ${item.mimeType ?? "resource"}]`).join("\n\n");
27854
+ return {
27855
+ serverName,
27856
+ uri,
27857
+ content
27858
+ };
27859
+ }
27516
27860
  async function registerMcpToolsForWorkspace(cwd, registry2, runtime) {
27517
27861
  const tools = await discoverMcpTools(cwd);
27518
27862
  for (const tool of tools) {
@@ -27536,7 +27880,7 @@ async function registerMcpToolsForWorkspace(cwd, registry2, runtime) {
27536
27880
 
27537
27881
  // ../agent-runtime/dist/autonomous.js
27538
27882
  var MAX_TOOL_OUTPUT_CHARS = 4e3;
27539
- var MAX_PARSE_RETRIES = 2;
27883
+ var MAX_PARSE_RETRIES = 1;
27540
27884
  var DEFAULT_MAX_REPAIR_ATTEMPTS = 2;
27541
27885
  var CONTEXT_FILE_READ_LIMIT = 3;
27542
27886
  var SESSION_NOTE_LIMIT = 4;
@@ -27552,6 +27896,14 @@ var BOOTSTRAP_CONTEXT_FILES = [
27552
27896
  "README.md",
27553
27897
  "package.json"
27554
27898
  ];
27899
+ async function fileExists(cwd, filePath) {
27900
+ try {
27901
+ await (0, import_promises12.access)(import_node_path12.default.join(cwd, filePath));
27902
+ return true;
27903
+ } catch {
27904
+ return false;
27905
+ }
27906
+ }
27555
27907
  function createAgentActionSchema(allowedTools) {
27556
27908
  return external_exports.discriminatedUnion("type", [
27557
27909
  external_exports.object({
@@ -28165,6 +28517,7 @@ var AutonomousTaskExecutor = class {
28165
28517
  signal: options.signal,
28166
28518
  approvalMode: this.config.approvalMode,
28167
28519
  sandboxMode: this.config.sandboxMode,
28520
+ trustedDirectories: this.config.trustedDirectories,
28168
28521
  agentRole: task.agentRole,
28169
28522
  sessionId,
28170
28523
  taskId: task.id,
@@ -28174,6 +28527,7 @@ var AutonomousTaskExecutor = class {
28174
28527
  cwd: request.cwd,
28175
28528
  approvalMode: this.config.approvalMode,
28176
28529
  sandboxMode: this.config.sandboxMode,
28530
+ trustedDirectories: this.config.trustedDirectories,
28177
28531
  agentRole: task.agentRole,
28178
28532
  sessionId,
28179
28533
  taskId: task.id,
@@ -28244,7 +28598,13 @@ var AutonomousTaskExecutor = class {
28244
28598
  ...BOOTSTRAP_CONTEXT_FILES,
28245
28599
  ...filesToRead
28246
28600
  ] : BOOTSTRAP_CONTEXT_FILES)).slice(0, CONTEXT_FILE_READ_LIMIT + 4);
28601
+ const existingContextFiles = [];
28247
28602
  for (const filePath of fallbackFiles) {
28603
+ if (await fileExists(request.cwd, filePath)) {
28604
+ existingContextFiles.push(filePath);
28605
+ }
28606
+ }
28607
+ for (const filePath of existingContextFiles) {
28248
28608
  const readResult = await runPreflightTool("file.read", {
28249
28609
  path: filePath
28250
28610
  }, "Load an existing repo-defining file before the first model action.");
@@ -28256,12 +28616,23 @@ var AutonomousTaskExecutor = class {
28256
28616
  const contextRootCwd = options.contextRootCwd ?? request.cwd;
28257
28617
  const savedSession = await loadLatestSession(contextRootCwd).catch(() => null);
28258
28618
  const savedSessionSummary = summarizeSavedSessionContext(sessionId, savedSession);
28619
+ const recursiveMemory = await loadMarkdownMemoryContext(request.cwd, {
28620
+ maxFiles: 10,
28621
+ maxCharsPerFile: 4e3
28622
+ });
28259
28623
  const userMemory = await readMarkdownIfExists(resolveUserMemoryPath());
28260
28624
  const projectMemory = await readMarkdownIfExists(resolveProjectMemoryPath(request.cwd));
28261
28625
  const customAgentMemory = customOverlay ? await readMarkdownIfExists(import_node_path12.default.join(resolveAgentMemoryDir(request.cwd), `${customOverlay.definition.id}.md`)) : null;
28262
28626
  if (savedSessionSummary.length > 0) {
28263
28627
  contextMessages.unshift(savedSessionSummary.join("\n"));
28264
28628
  }
28629
+ if (recursiveMemory.length > 0) {
28630
+ contextMessages.unshift([
28631
+ "Recursive markdown memory context:",
28632
+ ...recursiveMemory.map((record2) => `File: ${record2.filePath}
28633
+ ${truncateForModel(record2.content)}`)
28634
+ ].join("\n\n"));
28635
+ }
28265
28636
  if (userMemory) {
28266
28637
  contextMessages.unshift(`Global user memory:
28267
28638
  ${truncateForModel(userMemory)}`);
@@ -28306,6 +28677,7 @@ ${truncateForModel(customAgentMemory)}`);
28306
28677
  signal: options.signal,
28307
28678
  approvalMode: this.config.approvalMode,
28308
28679
  sandboxMode: this.config.sandboxMode,
28680
+ trustedDirectories: this.config.trustedDirectories,
28309
28681
  agentRole: task.agentRole,
28310
28682
  operatorApproved,
28311
28683
  sessionId,
@@ -28318,6 +28690,7 @@ ${truncateForModel(customAgentMemory)}`);
28318
28690
  cwd: request.cwd,
28319
28691
  approvalMode: this.config.approvalMode,
28320
28692
  sandboxMode: this.config.sandboxMode,
28693
+ trustedDirectories: this.config.trustedDirectories,
28321
28694
  agentRole: task.agentRole,
28322
28695
  operatorApproved,
28323
28696
  sessionId,
@@ -28516,14 +28889,16 @@ ${truncateForModel(customAgentMemory)}`);
28516
28889
  let responseText = "";
28517
28890
  let parsedAction = null;
28518
28891
  for (let attempt = 0; attempt <= MAX_PARSE_RETRIES; attempt += 1) {
28519
- await emitProgress({
28520
- type: "task-note",
28521
- sessionId,
28522
- taskId: task.id,
28523
- agentRole: task.agentRole,
28524
- step,
28525
- message: attempt === 0 ? "Thinking through the next safe action." : `Re-asking the model for a stricter machine-readable action (${attempt + 1}/${MAX_PARSE_RETRIES + 1}).`
28526
- });
28892
+ if (attempt === 0) {
28893
+ await emitProgress({
28894
+ type: "task-note",
28895
+ sessionId,
28896
+ taskId: task.id,
28897
+ agentRole: task.agentRole,
28898
+ step,
28899
+ message: "Thinking through the next safe action."
28900
+ });
28901
+ }
28527
28902
  let response;
28528
28903
  try {
28529
28904
  response = await brain.client.generateText({
@@ -28591,20 +28966,20 @@ ${truncateForModel(customAgentMemory)}`);
28591
28966
  response: response.text,
28592
28967
  runtimeNote: `${parseSummary} ${error2 instanceof Error ? error2.message : String(error2)}`
28593
28968
  });
28969
+ const inferredFallbackAction = inferFallbackActionFromResponse(response.text, task, request, allowedTools);
28970
+ if (inferredFallbackAction) {
28971
+ parsedAction = inferredFallbackAction;
28972
+ await emitProgress({
28973
+ type: "task-note",
28974
+ sessionId,
28975
+ taskId: task.id,
28976
+ agentRole: task.agentRole,
28977
+ step,
28978
+ message: `Interpreted the model draft as ${inferredFallbackAction.tool}.`
28979
+ });
28980
+ break;
28981
+ }
28594
28982
  if (attempt === MAX_PARSE_RETRIES) {
28595
- const inferredFallbackAction = inferFallbackActionFromResponse(response.text, task, request, allowedTools);
28596
- if (inferredFallbackAction) {
28597
- parsedAction = inferredFallbackAction;
28598
- await emitProgress({
28599
- type: "task-note",
28600
- sessionId,
28601
- taskId: task.id,
28602
- agentRole: task.agentRole,
28603
- step,
28604
- message: `Model stayed out of structured mode; inferred ${inferredFallbackAction.tool} from the response.`
28605
- });
28606
- break;
28607
- }
28608
28983
  const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
28609
28984
  artifacts.add(transcriptPath2);
28610
28985
  await emitProgress({
@@ -28629,7 +29004,7 @@ ${truncateForModel(customAgentMemory)}`);
28629
29004
  taskId: task.id,
28630
29005
  agentRole: task.agentRole,
28631
29006
  step,
28632
- message: `Reformatting model output to match Kimbho's action schema (${attempt + 1}/${MAX_PARSE_RETRIES + 1}).`
29007
+ message: "Normalizing the model draft into one safe machine-readable action."
28633
29008
  });
28634
29009
  messages.push({
28635
29010
  role: "assistant",
@@ -28795,6 +29170,47 @@ function normalizeGoal(goal) {
28795
29170
  }
28796
29171
  function looksLikeStaticSiteGoal(goal) {
28797
29172
  const lower = goal.toLowerCase();
29173
+ const backendSignals = [
29174
+ "api",
29175
+ "database",
29176
+ "backend",
29177
+ "server",
29178
+ "migration",
29179
+ "schema",
29180
+ "prisma",
29181
+ "postgres",
29182
+ "sqlite",
29183
+ "auth",
29184
+ "billing",
29185
+ "webhook"
29186
+ ];
29187
+ const pageSignals = [
29188
+ "page",
29189
+ "landing",
29190
+ "lnding",
29191
+ "homepage",
29192
+ "home page",
29193
+ "site",
29194
+ "website",
29195
+ "ui",
29196
+ "hero",
29197
+ "layout",
29198
+ "screen"
29199
+ ];
29200
+ const polishSignals = [
29201
+ "animate",
29202
+ "animated",
29203
+ "animation",
29204
+ "motion",
29205
+ "rich",
29206
+ "richer",
29207
+ "polish",
29208
+ "styled",
29209
+ "styling",
29210
+ "visual",
29211
+ "redesign",
29212
+ "revamp"
29213
+ ];
28798
29214
  if (lower.includes("landing page") || lower.includes("landing") || lower.includes("lnding") || lower.includes("homepage") || lower.includes("home page") || lower.includes("marketing page") || lower.includes("storefront") || lower.includes("restaurant website") || lower.includes("restaurant site") || lower.includes("static site")) {
28799
29215
  return true;
28800
29216
  }
@@ -28804,6 +29220,12 @@ function looksLikeStaticSiteGoal(goal) {
28804
29220
  if (lower.includes("restaurant") && (lower.includes("page") || lower.includes("site") || lower.includes("website") || lower.includes("landing") || lower.includes("lnding"))) {
28805
29221
  return true;
28806
29222
  }
29223
+ if (pageSignals.some((signal) => lower.includes(signal)) && polishSignals.some((signal) => lower.includes(signal)) && !backendSignals.some((signal) => lower.includes(signal))) {
29224
+ return true;
29225
+ }
29226
+ if (lower.includes("blog") && (lower.includes("make") || lower.includes("improve") || lower.includes("enhance") || lower.includes("redesign")) && !backendSignals.some((signal) => lower.includes(signal))) {
29227
+ return true;
29228
+ }
28807
29229
  return false;
28808
29230
  }
28809
29231
  function inferProjectShape(goal) {
@@ -29577,7 +29999,7 @@ function terminalTaskIds(tasks) {
29577
29999
  const terminalIds = tasks.filter((task) => !referencedIds.has(task.id)).map((task) => task.id);
29578
30000
  return terminalIds.length > 0 ? terminalIds : tasks.map((task) => task.id);
29579
30001
  }
29580
- function integrateExpandedTasks(plan, taskId, expandedTasks, note) {
30002
+ function integratePlanTaskExpansion(plan, taskId, expandedTasks, note) {
29581
30003
  const terminalIds = terminalTaskIds(expandedTasks);
29582
30004
  const milestones = plan.milestones.map((milestone) => ({
29583
30005
  ...milestone,
@@ -29687,7 +30109,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
29687
30109
  const normalizedTasks = normalizeExpandedTasks(plan, task, payload, "model");
29688
30110
  const note = `Planner expanded ${task.id} into ${normalizedTasks.length} subtasks via ${invocation.modelLabel ?? "planner model"}: ${normalizedTasks.map((candidate) => candidate.id).join(", ")}.`;
29689
30111
  return {
29690
- plan: integrateExpandedTasks(plan, task.id, normalizedTasks, note),
30112
+ plan: integratePlanTaskExpansion(plan, task.id, normalizedTasks, note),
29691
30113
  source: "model",
29692
30114
  expandedTaskId: task.id,
29693
30115
  createdTaskIds: normalizedTasks.map((candidate) => candidate.id),
@@ -29716,7 +30138,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
29716
30138
  }
29717
30139
  const note = `Planner fell back to deterministic subtask expansion for ${task.id}: ${fallbackTasks.map((candidate) => candidate.id).join(", ")}.`;
29718
30140
  return {
29719
- plan: integrateExpandedTasks(plan, task.id, fallbackTasks, note),
30141
+ plan: integratePlanTaskExpansion(plan, task.id, fallbackTasks, note),
29720
30142
  source: "fallback",
29721
30143
  expandedTaskId: task.id,
29722
30144
  createdTaskIds: fallbackTasks.map((candidate) => candidate.id),
@@ -29761,7 +30183,7 @@ function expandPlanTaskFallback(plan, taskId) {
29761
30183
  }
29762
30184
  const note = `Planner deterministically expanded ${task.id} into ${fallbackTasks.length} subtasks: ${fallbackTasks.map((candidate) => candidate.id).join(", ")}.`;
29763
30185
  return {
29764
- plan: integrateExpandedTasks(plan, task.id, fallbackTasks, note),
30186
+ plan: integratePlanTaskExpansion(plan, task.id, fallbackTasks, note),
29765
30187
  source: "fallback",
29766
30188
  expandedTaskId: task.id,
29767
30189
  createdTaskIds: fallbackTasks.map((candidate) => candidate.id),
@@ -30695,6 +31117,19 @@ function maybeAppendNote(notes, note) {
30695
31117
  note
30696
31118
  ];
30697
31119
  }
31120
+ function slugify2(value) {
31121
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
31122
+ }
31123
+ function createUniqueTaskId2(baseId, existingIds) {
31124
+ let candidate = baseId;
31125
+ let index = 2;
31126
+ while (existingIds.has(candidate)) {
31127
+ candidate = `${baseId}-${index}`;
31128
+ index += 1;
31129
+ }
31130
+ existingIds.add(candidate);
31131
+ return candidate;
31132
+ }
30698
31133
  function mergeUnique(items) {
30699
31134
  return Array.from(new Set(items.filter((item) => item.trim().length > 0)));
30700
31135
  }
@@ -31778,6 +32213,53 @@ var ExecutionOrchestrator = class {
31778
32213
  plan
31779
32214
  };
31780
32215
  }
32216
+ const matchedTeam = await selectAgentTeam(request.cwd, candidate, request);
32217
+ if (matchedTeam && matchedTeam.members.length > 1) {
32218
+ const existingIds = new Set(flattenPlanTasks(plan).filter((task) => task.id !== candidate.id).map((task) => task.id));
32219
+ const teamMemberIds = matchedTeam.members.map((member) => member.id);
32220
+ const generatedTaskIds = matchedTeam.members.map((member, index) => createUniqueTaskId2(`${candidate.id}-${slugify2(member.id) || `member-${index + 1}`}`, existingIds));
32221
+ const teamTasks = matchedTeam.members.map((member, index) => {
32222
+ const taskId = generatedTaskIds[index];
32223
+ const dependsOn = matchedTeam.team.strategy === "parallel" ? [
32224
+ ...candidate.dependsOn
32225
+ ] : index === 0 ? [
32226
+ ...candidate.dependsOn
32227
+ ] : [
32228
+ generatedTaskIds[index - 1] ?? candidate.dependsOn[0] ?? ""
32229
+ ].filter((value) => value.length > 0);
32230
+ return {
32231
+ ...candidate,
32232
+ id: taskId,
32233
+ title: `${candidate.title} (${member.label})`,
32234
+ description: [
32235
+ candidate.description,
32236
+ `Assigned team member: ${member.label} (${member.id}).`,
32237
+ member.purpose ? `Purpose: ${member.purpose}` : "",
32238
+ matchedTeam.team.description ? `Team context: ${matchedTeam.team.description}` : ""
32239
+ ].filter((part) => part.length > 0).join("\n\n"),
32240
+ agentRole: member.baseRole,
32241
+ dependsOn,
32242
+ acceptanceCriteria: index === matchedTeam.members.length - 1 ? candidate.acceptanceCriteria : [
32243
+ `Advance ${candidate.title.toLowerCase()} from the ${member.label} perspective.`,
32244
+ "Leave clear handoff notes and concrete changed-file intent for the next team member."
32245
+ ],
32246
+ outputs: index === matchedTeam.members.length - 1 ? candidate.outputs : [
32247
+ `Team handoff from ${member.id}`
32248
+ ],
32249
+ originTaskId: candidate.id,
32250
+ swarmDepth: (candidate.swarmDepth ?? 0) + 1,
32251
+ customAgentId: member.id,
32252
+ teamId: matchedTeam.team.id,
32253
+ teamMemberIds
32254
+ };
32255
+ });
32256
+ const note = `Agent team ${matchedTeam.team.label} expanded ${candidate.id} into ${teamTasks.length} ${matchedTeam.team.strategy} subtasks: ${teamTasks.map((task) => task.id).join(", ")}.`;
32257
+ return {
32258
+ plan: integratePlanTaskExpansion(plan, candidate.id, teamTasks, note),
32259
+ note,
32260
+ expandedTaskId: candidate.id
32261
+ };
32262
+ }
31781
32263
  const repoContext = latestRepoAnalysisContext(events);
31782
32264
  const config2 = await loadConfig(request.cwd);
31783
32265
  if (!config2) {
@@ -32486,21 +32968,24 @@ async function listMemoryPaths(cwd) {
32486
32968
  function createConfigCommand() {
32487
32969
  return new Command("config").description("Show the current config, memory, and MCP file locations.").action(async () => {
32488
32970
  const cwd = import_node_process5.default.cwd();
32489
- const config2 = await loadConfig(cwd);
32971
+ const layers = await loadConfigLayers(cwd);
32490
32972
  const memory = await listMemoryPaths(cwd);
32491
- console.log(`config: ${resolveConfigPath(cwd)}`);
32973
+ console.log(`config.user: ${resolveGlobalConfigPath()}`);
32974
+ console.log(`config.project: ${resolveConfigPath(cwd)}`);
32492
32975
  console.log(`mcp: ${resolveMcpConfigPath(cwd)}`);
32493
32976
  console.log(`memory.project: ${memory.projectMemoryPath}`);
32494
32977
  console.log(`memory.user: ${memory.userMemoryPath}`);
32495
32978
  console.log(`memory.agents: ${memory.agentMemoryDir}`);
32496
- if (!config2) {
32979
+ if (!layers.resolved) {
32497
32980
  console.log("No config found.");
32498
32981
  return;
32499
32982
  }
32500
- console.log(`providers: ${config2.providers.length}`);
32501
- console.log(`approval: ${config2.approvalMode}`);
32502
- console.log(`sandbox: ${config2.sandboxMode}`);
32503
- console.log(`trusted directories: ${config2.trustedDirectories.length}`);
32983
+ console.log(`layers.user: ${layers.user ? "present" : "missing"}`);
32984
+ console.log(`layers.project: ${layers.project ? "present" : "missing"}`);
32985
+ console.log(`providers: ${layers.resolved.providers.length}`);
32986
+ console.log(`approval: ${layers.resolved.approvalMode}`);
32987
+ console.log(`sandbox: ${layers.resolved.sandboxMode}`);
32988
+ console.log(`trusted directories: ${layers.resolved.trustedDirectories.length}`);
32504
32989
  });
32505
32990
  }
32506
32991
 
@@ -32734,14 +33219,90 @@ async function renderMcpInventory(cwd, name) {
32734
33219
  }
32735
33220
  return filtered.flatMap((server) => [
32736
33221
  `${server.name}`,
33222
+ ...server.warning ? [
33223
+ ` warning: ${server.warning}`
33224
+ ] : [],
32737
33225
  ` tools: ${server.tools.length}`,
32738
33226
  ...server.tools.slice(0, 20).map((tool) => ` - ${tool.toolName} (${tool.permission})`),
32739
33227
  ` prompts: ${server.prompts.length}`,
32740
- ...server.prompts.slice(0, 10).map((prompt) => ` - ${prompt}`),
33228
+ ...server.prompts.slice(0, 10).flatMap((prompt) => [
33229
+ ` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
33230
+ ` command: /mcp__${server.name}__${prompt.name}`
33231
+ ]),
32741
33232
  ` resources: ${server.resources.length}`,
32742
- ...server.resources.slice(0, 10).map((resource) => ` - ${resource}`)
33233
+ ...server.resources.slice(0, 10).flatMap((resource) => [
33234
+ ` - ${resource.name} -> ${resource.uri}`,
33235
+ ` attach: @${server.name}:${resource.uri}`
33236
+ ]),
33237
+ ` resource templates: ${server.resourceTemplates.length}`,
33238
+ ...server.resourceTemplates.slice(0, 10).map((resourceTemplate) => ` - ${resourceTemplate.name} -> ${resourceTemplate.uriTemplate}`)
33239
+ ]);
33240
+ }
33241
+ async function renderMcpPromptList(cwd, name) {
33242
+ const inventory = await loadMcpServerInventory(cwd);
33243
+ const filtered = name ? inventory.filter((server) => server.name === name) : inventory;
33244
+ if (filtered.length === 0) {
33245
+ return [
33246
+ name ? `No MCP prompt inventory found for ${name}.` : "No MCP prompt inventory available."
33247
+ ];
33248
+ }
33249
+ return filtered.flatMap((server) => [
33250
+ `${server.name}`,
33251
+ ...server.warning ? [
33252
+ ` warning: ${server.warning}`
33253
+ ] : [],
33254
+ ...server.prompts.length > 0 ? server.prompts.flatMap((prompt) => [
33255
+ ` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
33256
+ ` command: /mcp__${server.name}__${prompt.name}`
33257
+ ]) : [
33258
+ " - no prompts"
33259
+ ]
32743
33260
  ]);
32744
33261
  }
33262
+ async function renderMcpResourceList(cwd, name) {
33263
+ const inventory = await loadMcpServerInventory(cwd);
33264
+ const filtered = name ? inventory.filter((server) => server.name === name) : inventory;
33265
+ if (filtered.length === 0) {
33266
+ return [
33267
+ name ? `No MCP resource inventory found for ${name}.` : "No MCP resource inventory available."
33268
+ ];
33269
+ }
33270
+ return filtered.flatMap((server) => [
33271
+ `${server.name}`,
33272
+ ...server.warning ? [
33273
+ ` warning: ${server.warning}`
33274
+ ] : [],
33275
+ ...server.resources.length > 0 ? server.resources.flatMap((resource) => [
33276
+ ` - ${resource.name} -> ${resource.uri}`,
33277
+ ` attach: @${server.name}:${resource.uri}`
33278
+ ]) : [
33279
+ " - no resources"
33280
+ ],
33281
+ ...server.resourceTemplates.length > 0 ? [
33282
+ " templates:",
33283
+ ...server.resourceTemplates.map((resourceTemplate) => ` - ${resourceTemplate.name} -> ${resourceTemplate.uriTemplate}`)
33284
+ ] : []
33285
+ ]);
33286
+ }
33287
+ async function renderMcpPromptInvocation(cwd, serverName, promptName, args) {
33288
+ const result = await invokeMcpPrompt(cwd, serverName, promptName, args);
33289
+ return [
33290
+ `${result.serverName}/${result.promptName}`,
33291
+ ...result.description ? [
33292
+ `description: ${result.description}`
33293
+ ] : [],
33294
+ "",
33295
+ result.content
33296
+ ];
33297
+ }
33298
+ async function renderMcpResourceRead(cwd, serverName, uri) {
33299
+ const result = await readMcpResource(cwd, serverName, uri);
33300
+ return [
33301
+ `${result.serverName} ${result.uri}`,
33302
+ "",
33303
+ result.content
33304
+ ];
33305
+ }
32745
33306
 
32746
33307
  // src/commands/mcp.ts
32747
33308
  function collectValues(value, previous = []) {
@@ -32791,6 +33352,34 @@ function createMcpCommand() {
32791
33352
  console.log(line);
32792
33353
  }
32793
33354
  });
33355
+ command.command("discover").description("Show MCP tools, prompts-as-commands, and resource attachment syntax.").argument("[name]", "Optional server name").action(async (name) => {
33356
+ for (const line of await renderMcpInventory(import_node_process8.default.cwd(), name)) {
33357
+ console.log(line);
33358
+ }
33359
+ });
33360
+ command.command("prompts").description("List MCP prompts.").argument("[name]", "Optional server name").action(async (name) => {
33361
+ for (const line of await renderMcpPromptList(import_node_process8.default.cwd(), name)) {
33362
+ console.log(line);
33363
+ }
33364
+ });
33365
+ command.command("resources").description("List MCP resources and resource templates.").argument("[name]", "Optional server name").action(async (name) => {
33366
+ for (const line of await renderMcpResourceList(import_node_process8.default.cwd(), name)) {
33367
+ console.log(line);
33368
+ }
33369
+ });
33370
+ command.command("prompt").description("Invoke an MCP prompt and print the rendered content.").argument("<server>", "Server name").argument("<prompt>", "Prompt name").argument("[args...]", "Prompt args in key=value form").action(async (server, prompt, rawArgs = []) => {
33371
+ const args = Object.fromEntries(
33372
+ rawArgs.map((entry) => entry.split("=", 2)).filter((parts) => parts.length === 2 && parts[0].length > 0)
33373
+ );
33374
+ for (const line of await renderMcpPromptInvocation(import_node_process8.default.cwd(), server, prompt, args)) {
33375
+ console.log(line);
33376
+ }
33377
+ });
33378
+ command.command("read").description("Read one MCP resource by URI.").argument("<server>", "Server name").argument("<uri>", "Resource URI").action(async (server, uri) => {
33379
+ for (const line of await renderMcpResourceRead(import_node_process8.default.cwd(), server, uri)) {
33380
+ console.log(line);
33381
+ }
33382
+ });
32794
33383
  return command;
32795
33384
  }
32796
33385
 
@@ -33252,7 +33841,7 @@ function createModelsCommand() {
33252
33841
  const exists = models.some((item) => item.id === model) || provider.models.includes(model);
33253
33842
  if (!exists) {
33254
33843
  throw new Error(
33255
- `Model "${model}" was not found for provider "${provider.id}". Run \`kimbho /models ${provider.id} --search ${model}\` or re-run with --force.`
33844
+ `Model "${model}" was not found for provider "${provider.id}". Run \`kimbho model find ${model}\` or re-run with --force.`
33256
33845
  );
33257
33846
  }
33258
33847
  } catch (error2) {
@@ -33595,27 +34184,49 @@ function createPlanCommand() {
33595
34184
 
33596
34185
  // src/commands/permissions.ts
33597
34186
  var import_node_process13 = __toESM(require("node:process"), 1);
34187
+ var import_node_path19 = __toESM(require("node:path"), 1);
34188
+ function getScope(options) {
34189
+ return options.scope === "user" ? "user" : "project";
34190
+ }
34191
+ async function loadScopedConfig(scope, cwd) {
34192
+ const direct = scope === "user" ? await loadUserConfig() : await loadProjectConfig(cwd);
34193
+ const resolved = await loadConfig(cwd);
34194
+ return {
34195
+ direct,
34196
+ resolved
34197
+ };
34198
+ }
34199
+ async function saveScopedConfig(scope, cwd, config2) {
34200
+ return scope === "user" ? saveUserConfig(config2) : saveConfig(config2, cwd);
34201
+ }
33598
34202
  function createPermissionsCommand() {
33599
34203
  const command = new Command("permissions").description("Manage approval mode, sandbox mode, and trusted directories.");
33600
- command.command("status").description("Show permission settings.").action(async () => {
33601
- const config2 = await loadConfig(import_node_process13.default.cwd());
33602
- if (!config2) {
34204
+ command.command("status").description("Show permission settings.").option("--scope <scope>", "project or user", "project").action(async (options) => {
34205
+ const scope = getScope(options);
34206
+ const { direct, resolved } = await loadScopedConfig(scope, import_node_process13.default.cwd());
34207
+ console.log(`scope: ${scope}`);
34208
+ console.log(`direct config: ${direct ? "present" : "missing"}`);
34209
+ if (!resolved) {
33603
34210
  throw new Error("No config found. Run `kimbho init` first.");
33604
34211
  }
33605
- console.log(`approval: ${config2.approvalMode}`);
33606
- console.log(`sandbox: ${config2.sandboxMode}`);
33607
- console.log(`trusted directories: ${config2.trustedDirectories.length}`);
33608
- for (const directory of config2.trustedDirectories) {
34212
+ const display = direct ?? resolved;
34213
+ console.log(`approval: ${display.approvalMode}`);
34214
+ console.log(`sandbox: ${display.sandboxMode}`);
34215
+ console.log(`trusted directories: ${display.trustedDirectories.length}`);
34216
+ for (const directory of display.trustedDirectories) {
33609
34217
  console.log(` - ${directory}`);
33610
34218
  }
33611
34219
  });
33612
- command.command("set").description("Set approval mode or sandbox mode.").option("--approval <mode>", "manual or auto").option("--sandbox <mode>", "read-only, workspace-write, or full").action(async (options) => {
33613
- const config2 = await loadConfig(import_node_process13.default.cwd());
33614
- if (!config2) {
34220
+ command.command("set").description("Set approval mode or sandbox mode.").option("--scope <scope>", "project or user", "project").option("--approval <mode>", "manual or auto").option("--sandbox <mode>", "read-only, workspace-write, or full").action(async (options) => {
34221
+ const scope = getScope(options);
34222
+ const cwd = import_node_process13.default.cwd();
34223
+ const { direct, resolved } = await loadScopedConfig(scope, cwd);
34224
+ if (!resolved && !direct) {
33615
34225
  throw new Error("No config found. Run `kimbho init` first.");
33616
34226
  }
34227
+ const base = direct ?? resolved ?? createDefaultConfig();
33617
34228
  const next = {
33618
- ...config2,
34229
+ ...base,
33619
34230
  ...options.approval ? {
33620
34231
  approvalMode: options.approval
33621
34232
  } : {},
@@ -33623,32 +34234,40 @@ function createPermissionsCommand() {
33623
34234
  sandboxMode: options.sandbox
33624
34235
  } : {}
33625
34236
  };
33626
- const outputPath = await saveConfig(next, import_node_process13.default.cwd());
34237
+ const outputPath = await saveScopedConfig(scope, cwd, next);
33627
34238
  console.log(`Updated ${outputPath}`);
33628
34239
  });
33629
- command.command("trust").description("Add a trusted directory.").argument("<path>", "Directory to trust").action(async (trustedPath) => {
33630
- const config2 = await loadConfig(import_node_process13.default.cwd());
33631
- if (!config2) {
34240
+ command.command("trust").description("Add a trusted directory.").argument("<path>", "Directory to trust").option("--scope <scope>", "project or user", "project").action(async (trustedPath, options) => {
34241
+ const scope = getScope(options);
34242
+ const cwd = import_node_process13.default.cwd();
34243
+ const { direct, resolved } = await loadScopedConfig(scope, cwd);
34244
+ if (!resolved && !direct) {
33632
34245
  throw new Error("No config found. Run `kimbho init` first.");
33633
34246
  }
33634
- const outputPath = await saveConfig({
33635
- ...config2,
34247
+ const base = direct ?? resolved ?? createDefaultConfig();
34248
+ const normalizedPath = import_node_path19.default.resolve(cwd, trustedPath);
34249
+ const outputPath = await saveScopedConfig(scope, cwd, {
34250
+ ...base,
33636
34251
  trustedDirectories: Array.from(/* @__PURE__ */ new Set([
33637
- ...config2.trustedDirectories,
33638
- trustedPath
34252
+ ...base.trustedDirectories,
34253
+ normalizedPath
33639
34254
  ]))
33640
- }, import_node_process13.default.cwd());
34255
+ });
33641
34256
  console.log(`Updated ${outputPath}`);
33642
34257
  });
33643
- command.command("untrust").description("Remove a trusted directory.").argument("<path>", "Directory to remove").action(async (trustedPath) => {
33644
- const config2 = await loadConfig(import_node_process13.default.cwd());
33645
- if (!config2) {
34258
+ command.command("untrust").description("Remove a trusted directory.").argument("<path>", "Directory to remove").option("--scope <scope>", "project or user", "project").action(async (trustedPath, options) => {
34259
+ const scope = getScope(options);
34260
+ const cwd = import_node_process13.default.cwd();
34261
+ const { direct, resolved } = await loadScopedConfig(scope, cwd);
34262
+ if (!resolved && !direct) {
33646
34263
  throw new Error("No config found. Run `kimbho init` first.");
33647
34264
  }
33648
- const outputPath = await saveConfig({
33649
- ...config2,
33650
- trustedDirectories: config2.trustedDirectories.filter((directory) => directory !== trustedPath)
33651
- }, import_node_process13.default.cwd());
34265
+ const base = direct ?? resolved ?? createDefaultConfig();
34266
+ const normalizedPath = import_node_path19.default.resolve(cwd, trustedPath);
34267
+ const outputPath = await saveScopedConfig(scope, cwd, {
34268
+ ...base,
34269
+ trustedDirectories: base.trustedDirectories.filter((directory) => directory !== trustedPath && directory !== normalizedPath)
34270
+ });
33652
34271
  console.log(`Updated ${outputPath}`);
33653
34272
  });
33654
34273
  return command;
@@ -33762,7 +34381,7 @@ function createProvidersCommand() {
33762
34381
 
33763
34382
  // src/commands/review.ts
33764
34383
  var import_promises17 = require("node:fs/promises");
33765
- var import_node_path19 = __toESM(require("node:path"), 1);
34384
+ var import_node_path20 = __toESM(require("node:path"), 1);
33766
34385
  var import_node_process15 = __toESM(require("node:process"), 1);
33767
34386
  function summarizeChangedFiles2(diff) {
33768
34387
  const files = /* @__PURE__ */ new Set();
@@ -33846,7 +34465,7 @@ ${diff.stdout}`
33846
34465
  }
33847
34466
  }
33848
34467
  await ensureKimbhoDir(cwd);
33849
- const artifactPath = import_node_path19.default.join(resolveKimbhoDir(cwd), "logs", `review-${Date.now()}.md`);
34468
+ const artifactPath = import_node_path20.default.join(resolveKimbhoDir(cwd), "logs", `review-${Date.now()}.md`);
33850
34469
  await (0, import_promises17.writeFile)(artifactPath, [
33851
34470
  `# Review`,
33852
34471
  ``,
@@ -34080,6 +34699,12 @@ function normalizeCliTokens(tokens) {
34080
34699
  }
34081
34700
  function createProgram(onOpenShell) {
34082
34701
  const program2 = new Command();
34702
+ const legacyProviders = createProvidersCommand();
34703
+ const legacyModels = createModelsCommand();
34704
+ const legacyBrains = createBrainsCommand();
34705
+ legacyProviders._hidden = true;
34706
+ legacyModels._hidden = true;
34707
+ legacyBrains._hidden = true;
34083
34708
  program2.name("kimbho").description(KIMBHO_DESCRIPTION).version(KIMBHO_VERSION);
34084
34709
  program2.addCommand(createInitCommand());
34085
34710
  program2.addCommand(createPlanCommand());
@@ -34090,9 +34715,9 @@ function createProgram(onOpenShell) {
34090
34715
  program2.addCommand(createMcpCommand());
34091
34716
  program2.addCommand(createModelCommand());
34092
34717
  program2.addCommand(createAgentsCommand());
34093
- program2.addCommand(createProvidersCommand());
34094
- program2.addCommand(createModelsCommand());
34095
- program2.addCommand(createBrainsCommand());
34718
+ program2.addCommand(legacyProviders);
34719
+ program2.addCommand(legacyModels);
34720
+ program2.addCommand(legacyBrains);
34096
34721
  program2.addCommand(createRunCommand());
34097
34722
  program2.addCommand(createResumeCommand());
34098
34723
  program2.addCommand(createFixCommand());
@@ -34108,7 +34733,7 @@ function createProgram(onOpenShell) {
34108
34733
  // src/shell.ts
34109
34734
  var import_node_readline = require("node:readline");
34110
34735
  var import_promises18 = require("node:readline/promises");
34111
- var import_node_path20 = __toESM(require("node:path"), 1);
34736
+ var import_node_path21 = __toESM(require("node:path"), 1);
34112
34737
  var import_node_process18 = __toESM(require("node:process"), 1);
34113
34738
 
34114
34739
  // src/agent-management.ts
@@ -34162,6 +34787,7 @@ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
34162
34787
  "help",
34163
34788
  "init",
34164
34789
  "mcp",
34790
+ "mcp__",
34165
34791
  "memory",
34166
34792
  "model",
34167
34793
  "models",
@@ -34266,6 +34892,61 @@ var PLAN_PREFIXES = [
34266
34892
  function color(code, value) {
34267
34893
  return `${code}${value}${RESET}`;
34268
34894
  }
34895
+ function extractMcpResourceReferences(prompt) {
34896
+ const matches = prompt.match(/@[A-Za-z0-9_-]+:[^\s]+/g) ?? [];
34897
+ return matches.flatMap((token) => {
34898
+ const withoutAt = token.slice(1);
34899
+ const separatorIndex = withoutAt.indexOf(":");
34900
+ if (separatorIndex === -1) {
34901
+ return [];
34902
+ }
34903
+ return [
34904
+ {
34905
+ token,
34906
+ serverName: withoutAt.slice(0, separatorIndex),
34907
+ uri: withoutAt.slice(separatorIndex + 1)
34908
+ }
34909
+ ];
34910
+ });
34911
+ }
34912
+ async function hydratePromptWithMcpResources(cwd, prompt) {
34913
+ const references = extractMcpResourceReferences(prompt);
34914
+ if (references.length === 0) {
34915
+ return {
34916
+ prompt,
34917
+ notes: []
34918
+ };
34919
+ }
34920
+ const blocks = [];
34921
+ const notes = [];
34922
+ for (const reference of references) {
34923
+ try {
34924
+ const rendered = await renderMcpResourceRead(cwd, reference.serverName, reference.uri);
34925
+ notes.push(`attached MCP resource ${reference.token}`);
34926
+ blocks.push([
34927
+ `Attached MCP resource ${reference.token}:`,
34928
+ ...rendered
34929
+ ].join("\n"));
34930
+ } catch (error2) {
34931
+ const message = error2 instanceof Error ? error2.message : String(error2);
34932
+ notes.push(`failed to attach ${reference.token}: ${message}`);
34933
+ }
34934
+ }
34935
+ if (blocks.length === 0) {
34936
+ return {
34937
+ prompt,
34938
+ notes
34939
+ };
34940
+ }
34941
+ return {
34942
+ prompt: [
34943
+ prompt,
34944
+ "",
34945
+ ...blocks
34946
+ ].join("\n"),
34947
+ notes
34948
+ };
34949
+ }
34269
34950
  function createExecutionTelemetry() {
34270
34951
  return {
34271
34952
  toolCalls: 0,
@@ -34535,6 +35216,11 @@ function renderHelp() {
34535
35216
  "/mcp add <name> --command <cmd> [--arg <value>] [--env KEY=VALUE]",
34536
35217
  " Add an MCP stdio server to .mcp.json.",
34537
35218
  "/mcp tools [server] Discover tools/prompts/resources from MCP servers.",
35219
+ "/mcp prompts [server] List MCP prompts.",
35220
+ "/mcp resources [server] List MCP resources and templates.",
35221
+ "/mcp prompt <server> <prompt> [key=value ...]",
35222
+ " Render an MCP prompt.",
35223
+ "/mcp read <server> <uri> Read one MCP resource.",
34538
35224
  "/agents List custom agents and teams.",
34539
35225
  "/agents create <id> --base <role> Create a markdown custom agent.",
34540
35226
  "/agents team create <id> <agent...> Create a markdown team file.",
@@ -34571,7 +35257,7 @@ function renderStartupCard(cwd, state) {
34571
35257
  renderCardLine("shortcuts", "/ask /run /model /permissions /memory /mcp /quit")
34572
35258
  ];
34573
35259
  if (!state.configured) {
34574
- cardLines.push("setup: run /init or /providers add <template> to create .kimbho/config.json");
35260
+ cardLines.push("setup: run /init or /model add <template> to create .kimbho/config.json");
34575
35261
  }
34576
35262
  return renderBox(cardLines);
34577
35263
  }
@@ -34863,6 +35549,12 @@ function hasRenderableDiff(toolId, output) {
34863
35549
  }
34864
35550
  return false;
34865
35551
  }
35552
+ function isMutationTool(toolId) {
35553
+ return toolId === "file.write" || toolId === "file.patch" || toolId === "scaffold.generate";
35554
+ }
35555
+ function renderMutationArtifacts(artifacts, label = "changed", maxItems = 4) {
35556
+ return artifacts.slice(0, maxItems).map((artifact) => ` ${color(DIM, `${label}: ${shortenMiddle(artifact, 120)}`)}`);
35557
+ }
34866
35558
  function colorDiffLine(line) {
34867
35559
  if (line.startsWith("diff --git ")) {
34868
35560
  const match = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
@@ -34921,14 +35613,26 @@ function renderShellSessionSummary(snapshot, planPath, sessionPath) {
34921
35613
  lines.push(color(BOLD, "Activity"));
34922
35614
  for (const event of snapshot.events.slice(-8)) {
34923
35615
  lines.push(`- ${renderEventType(event.type)} | ${event.agentRole ?? "session"} | ${event.message}`);
35616
+ let landedMutation = false;
34924
35617
  for (const toolResult of event.toolResults.slice(-4)) {
34925
35618
  lines.push(` ${toolResult.success ? "ok" : "fail"} ${toolResult.toolId}: ${toolResult.summary}`);
34926
35619
  if (toolResult.stdout && hasRenderableDiff(toolResult.toolId, toolResult.stdout)) {
35620
+ landedMutation = landedMutation || isMutationTool(toolResult.toolId);
34927
35621
  lines.push(...renderDiffPreview(toolResult.stdout, 6));
34928
35622
  }
34929
35623
  if ((toolResult.toolId.startsWith("process.") || toolResult.toolId === "http.fetch" || toolResult.toolId.startsWith("browser.") || toolResult.toolId.startsWith("repo.")) && toolResult.stdout) {
34930
35624
  lines.push(...renderTextPreview(toolResult.stdout, 6));
34931
35625
  }
35626
+ if (toolResult.success && isMutationTool(toolResult.toolId) && toolResult.artifacts.length > 0) {
35627
+ landedMutation = true;
35628
+ lines.push(...renderMutationArtifacts(
35629
+ toolResult.artifacts,
35630
+ toolResult.toolId === "scaffold.generate" ? "created" : "changed"
35631
+ ));
35632
+ }
35633
+ }
35634
+ if (event.type === "task-blocked" && !landedMutation) {
35635
+ lines.push(` ${color(DIM, "no safe edit landed before this task blocked.")}`);
34932
35636
  }
34933
35637
  for (const artifact of event.artifacts.slice(-3)) {
34934
35638
  lines.push(` artifact: ${artifact}`);
@@ -35041,7 +35745,11 @@ function renderLiveExecutionEvent(event) {
35041
35745
  case "tool-finished":
35042
35746
  return [
35043
35747
  ` ${event.toolResult.success ? color(CYAN, "ok") : color(AMBER, "fail")} ${event.toolResult.toolId} ${event.toolResult.summary}`,
35044
- ...event.toolResult.stdout && hasRenderableDiff(event.toolResult.toolId, event.toolResult.stdout) ? renderDiffPreview(event.toolResult.stdout, 8) : (event.toolResult.toolId.startsWith("process.") || event.toolResult.toolId === "http.fetch" || event.toolResult.toolId.startsWith("browser.") || event.toolResult.toolId.startsWith("repo.")) && event.toolResult.stdout ? renderTextPreview(event.toolResult.stdout, 8) : []
35748
+ ...event.toolResult.stdout && hasRenderableDiff(event.toolResult.toolId, event.toolResult.stdout) ? renderDiffPreview(event.toolResult.stdout, 8) : (event.toolResult.toolId.startsWith("process.") || event.toolResult.toolId === "http.fetch" || event.toolResult.toolId.startsWith("browser.") || event.toolResult.toolId.startsWith("repo.")) && event.toolResult.stdout ? renderTextPreview(event.toolResult.stdout, 8) : [],
35749
+ ...event.toolResult.success && isMutationTool(event.toolResult.toolId) && event.toolResult.artifacts.length > 0 ? renderMutationArtifacts(
35750
+ event.toolResult.artifacts,
35751
+ event.toolResult.toolId === "scaffold.generate" ? "created" : "changed"
35752
+ ) : []
35045
35753
  ];
35046
35754
  default:
35047
35755
  return [];
@@ -35190,13 +35898,18 @@ function clearAllConversations(runtime) {
35190
35898
  }
35191
35899
  }
35192
35900
  async function handleChatPrompt(cwd, prompt, runtime) {
35901
+ const hydrated = await hydratePromptWithMcpResources(cwd, prompt);
35193
35902
  const config2 = await loadConfig(cwd);
35194
35903
  if (!config2) {
35195
- throw new Error("No config found. Run /init or /providers add <template> first.");
35904
+ throw new Error("No config found. Run /init or /model add <template> first.");
35196
35905
  }
35197
35906
  const registry2 = createDefaultBrainProviderRegistry(cwd);
35198
35907
  const resolver = new BrainResolver(config2, registry2);
35199
35908
  const brain = await resolver.resolve(runtime.focusRole);
35909
+ const memoryContext = await loadMarkdownMemoryContext(cwd, {
35910
+ maxFiles: 8,
35911
+ maxCharsPerFile: 3e3
35912
+ });
35200
35913
  const compactionPath = await compactConversationIfNeeded(cwd, runtime, runtime.focusRole);
35201
35914
  if (compactionPath) {
35202
35915
  console.log(color(DIM, `[compacting] condensed ${runtime.focusRole} chat context -> ${compactionPath}`));
@@ -35206,7 +35919,7 @@ async function handleChatPrompt(cwd, prompt, runtime) {
35206
35919
  ...history,
35207
35920
  {
35208
35921
  role: "user",
35209
- content: prompt
35922
+ content: hydrated.prompt
35210
35923
  }
35211
35924
  ]);
35212
35925
  let result;
@@ -35219,6 +35932,11 @@ async function handleChatPrompt(cwd, prompt, runtime) {
35219
35932
  ...brain.settings.promptPreamble || runtime.conversationSummaries[runtime.focusRole] ? {
35220
35933
  systemPrompt: [
35221
35934
  brain.settings.promptPreamble,
35935
+ memoryContext.length > 0 ? [
35936
+ "Workspace memory:",
35937
+ ...memoryContext.map((record2) => `File: ${record2.filePath}
35938
+ ${record2.content}`)
35939
+ ].join("\n\n") : null,
35222
35940
  runtime.conversationSummaries[runtime.focusRole] ? `Compacted conversation summary:
35223
35941
  ${runtime.conversationSummaries[runtime.focusRole]}` : null
35224
35942
  ].filter((value) => Boolean(value)).join("\n\n")
@@ -35245,16 +35963,20 @@ ${runtime.conversationSummaries[runtime.focusRole]}` : null
35245
35963
  ]);
35246
35964
  runtime.conversations[runtime.focusRole] = nextConversation;
35247
35965
  console.log(color(DIM, `[${brain.role}] ${brain.provider.id}/${brain.model}`));
35966
+ for (const note of hydrated.notes) {
35967
+ console.log(color(DIM, note));
35968
+ }
35248
35969
  if (result.usage) {
35249
35970
  console.log(color(DIM, `tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`));
35250
35971
  }
35251
35972
  console.log(renderTerminalMarkdown(result.text));
35252
35973
  }
35253
35974
  async function runGoalExecution(cwd, goal, runtime) {
35975
+ const hydrated = await hydratePromptWithMcpResources(cwd, goal);
35254
35976
  const orchestrator = new ExecutionOrchestrator();
35255
- const workspace = await resolveExecutionWorkspace(cwd, goal);
35977
+ const workspace = await resolveExecutionWorkspace(cwd, hydrated.prompt);
35256
35978
  const request = {
35257
- goal,
35979
+ goal: hydrated.prompt,
35258
35980
  mode: "run",
35259
35981
  cwd: workspace.cwd,
35260
35982
  workspaceState: workspace.workspaceState,
@@ -35269,6 +35991,9 @@ async function runGoalExecution(cwd, goal, runtime) {
35269
35991
  };
35270
35992
  const planningSpinner = new ShellActivityIndicator("planning");
35271
35993
  console.log(color(DIM, `Working on: ${goal}`));
35994
+ for (const note of hydrated.notes) {
35995
+ console.log(color(DIM, note));
35996
+ }
35272
35997
  for (const note of workspace.notes) {
35273
35998
  console.log(color(DIM, note));
35274
35999
  }
@@ -35529,7 +36254,7 @@ async function printLatestPlanSummary(cwd) {
35529
36254
  async function handleApprovalModeCommand(cwd, tokens) {
35530
36255
  const config2 = await loadConfig(cwd);
35531
36256
  if (!config2) {
35532
- throw new Error("No config found. Run /init or /providers add <template> first.");
36257
+ throw new Error("No config found. Run /init or /model add <template> first.");
35533
36258
  }
35534
36259
  const subcommand = tokens[1]?.trim().toLowerCase();
35535
36260
  if (!subcommand || subcommand === "status") {
@@ -35549,44 +36274,55 @@ async function handleApprovalModeCommand(cwd, tokens) {
35549
36274
  console.log(`approval mode: ${subcommand}`);
35550
36275
  }
35551
36276
  async function printConfigSummary(cwd) {
35552
- const config2 = await loadConfig(cwd);
36277
+ const layers = await loadConfigLayers(cwd);
35553
36278
  const paths = await listMemoryPaths(cwd);
35554
- console.log(`config: ${resolveConfigPath(cwd)}`);
36279
+ console.log(`config.project: ${resolveConfigPath(cwd)}`);
36280
+ console.log(`config.user: ${resolveGlobalConfigPath()}`);
35555
36281
  console.log(`mcp: ${resolveMcpConfigPath(cwd)}`);
35556
36282
  console.log(`memory.project: ${paths.projectMemoryPath}`);
35557
36283
  console.log(`memory.user: ${paths.userMemoryPath}`);
35558
36284
  console.log(`memory.agents: ${paths.agentMemoryDir}`);
35559
- if (!config2) {
36285
+ if (!layers.resolved) {
35560
36286
  console.log("No config found. Run /init first.");
35561
36287
  return;
35562
36288
  }
36289
+ console.log(`layers: user=${layers.user ? "yes" : "no"} project=${layers.project ? "yes" : "no"}`);
36290
+ const config2 = layers.resolved;
35563
36291
  console.log(`providers: ${config2.providers.length}`);
35564
36292
  console.log(`approval: ${config2.approvalMode}`);
35565
36293
  console.log(`sandbox: ${config2.sandboxMode}`);
35566
36294
  console.log(`trusted directories: ${config2.trustedDirectories.length}`);
35567
36295
  }
35568
36296
  async function handlePermissionsCommand(cwd, tokens) {
35569
- const config2 = await loadConfig(cwd);
35570
- if (!config2) {
36297
+ const subcommand = tokens[1]?.trim().toLowerCase();
36298
+ const scopeToken = tokens.find((token, index) => index > 0 && (token === "user" || token === "project"));
36299
+ const scope = scopeToken === "user" ? "user" : "project";
36300
+ const direct = scope === "user" ? await loadUserConfig() : await loadProjectConfig(cwd);
36301
+ const resolved = await loadConfig(cwd);
36302
+ if (!resolved && !direct) {
35571
36303
  throw new Error("No config found. Run /init or /model add <template> first.");
35572
36304
  }
35573
- const subcommand = tokens[1]?.trim().toLowerCase();
36305
+ const effective = direct ?? resolved ?? createDefaultConfig();
36306
+ const saveScoped = async (next) => scope === "user" ? saveUserConfig(next) : saveConfig(next, cwd);
35574
36307
  if (!subcommand || subcommand === "show" || subcommand === "status") {
35575
- console.log(`approval: ${config2.approvalMode}`);
35576
- console.log(`sandbox: ${config2.sandboxMode}`);
35577
- console.log(`trusted directories: ${config2.trustedDirectories.length}`);
35578
- for (const directory of config2.trustedDirectories) {
36308
+ console.log(`scope: ${scope}`);
36309
+ console.log(`direct config: ${direct ? "present" : "missing"}`);
36310
+ console.log(`approval: ${effective.approvalMode}`);
36311
+ console.log(`sandbox: ${effective.sandboxMode}`);
36312
+ console.log(`trusted directories: ${effective.trustedDirectories.length}`);
36313
+ for (const directory of effective.trustedDirectories) {
35579
36314
  console.log(` - ${directory}`);
35580
36315
  }
35581
36316
  return;
35582
36317
  }
35583
36318
  if (subcommand === "auto" || subcommand === "manual") {
35584
- const outputPath = await saveConfig({
35585
- ...config2,
36319
+ const outputPath = await saveScoped({
36320
+ ...effective,
35586
36321
  approvalMode: subcommand
35587
- }, cwd);
36322
+ });
35588
36323
  console.log(`Updated ${outputPath}`);
35589
36324
  console.log(`approval mode: ${subcommand}`);
36325
+ console.log(`scope: ${scope}`);
35590
36326
  return;
35591
36327
  }
35592
36328
  if (subcommand === "sandbox") {
@@ -35598,12 +36334,13 @@ async function handlePermissionsCommand(cwd, tokens) {
35598
36334
  ].includes(mode)) {
35599
36335
  throw new Error("Usage: /permissions sandbox <read-only|workspace-write|full>");
35600
36336
  }
35601
- const outputPath = await saveConfig({
35602
- ...config2,
36337
+ const outputPath = await saveScoped({
36338
+ ...effective,
35603
36339
  sandboxMode: mode
35604
- }, cwd);
36340
+ });
35605
36341
  console.log(`Updated ${outputPath}`);
35606
36342
  console.log(`sandbox: ${mode}`);
36343
+ console.log(`scope: ${scope}`);
35607
36344
  return;
35608
36345
  }
35609
36346
  if (subcommand === "trust" || subcommand === "untrust") {
@@ -35611,19 +36348,21 @@ async function handlePermissionsCommand(cwd, tokens) {
35611
36348
  if (!target) {
35612
36349
  throw new Error(`Usage: /permissions ${subcommand} <path>`);
35613
36350
  }
36351
+ const normalizedTarget = import_node_path21.default.resolve(cwd, target);
35614
36352
  const trustedDirectories = subcommand === "trust" ? Array.from(/* @__PURE__ */ new Set([
35615
- ...config2.trustedDirectories,
35616
- target
35617
- ])) : config2.trustedDirectories.filter((directory) => directory !== target);
35618
- const outputPath = await saveConfig({
35619
- ...config2,
36353
+ ...effective.trustedDirectories,
36354
+ normalizedTarget
36355
+ ])) : effective.trustedDirectories.filter((directory) => directory !== target && directory !== normalizedTarget);
36356
+ const outputPath = await saveScoped({
36357
+ ...effective,
35620
36358
  trustedDirectories
35621
- }, cwd);
36359
+ });
35622
36360
  console.log(`Updated ${outputPath}`);
35623
- console.log(`${subcommand === "trust" ? "Trusted" : "Removed"} ${target}`);
36361
+ console.log(`${subcommand === "trust" ? "Trusted" : "Removed"} ${normalizedTarget}`);
36362
+ console.log(`scope: ${scope}`);
35624
36363
  return;
35625
36364
  }
35626
- throw new Error("Usage: /permissions [show|auto|manual|sandbox <mode>|trust <path>|untrust <path>]");
36365
+ throw new Error("Usage: /permissions [show|auto|manual|sandbox <mode>|trust <path>|untrust <path>] [project|user]");
35627
36366
  }
35628
36367
  async function handleMemoryCommand(cwd, tokens) {
35629
36368
  const subcommand = tokens[1]?.trim().toLowerCase();
@@ -35650,7 +36389,7 @@ async function handleMemoryCommand(cwd, tokens) {
35650
36389
  }
35651
36390
  let filePath;
35652
36391
  if (scope === "init") {
35653
- filePath = import_node_path20.default.join(cwd, "kimbho_init.md");
36392
+ filePath = import_node_path21.default.join(cwd, "kimbho_init.md");
35654
36393
  } else if (scope === "project") {
35655
36394
  filePath = resolveProjectMemoryPath(cwd);
35656
36395
  } else if (scope === "user") {
@@ -35660,7 +36399,7 @@ async function handleMemoryCommand(cwd, tokens) {
35660
36399
  if (!agentId) {
35661
36400
  throw new Error("Usage: /memory show agent <agent-id>");
35662
36401
  }
35663
- filePath = import_node_path20.default.join(resolveAgentMemoryDir(cwd), `${agentId}.md`);
36402
+ filePath = import_node_path21.default.join(resolveAgentMemoryDir(cwd), `${agentId}.md`);
35664
36403
  } else {
35665
36404
  throw new Error("Usage: /memory show <init|project|user|agent> [id]");
35666
36405
  }
@@ -35728,6 +36467,45 @@ async function handleMcpCommand(cwd, tokens) {
35728
36467
  }
35729
36468
  return;
35730
36469
  }
36470
+ if (subcommand === "prompts") {
36471
+ const name = tokens[2]?.trim();
36472
+ for (const line of await renderMcpPromptList(cwd, name)) {
36473
+ console.log(line);
36474
+ }
36475
+ return;
36476
+ }
36477
+ if (subcommand === "resources") {
36478
+ const name = tokens[2]?.trim();
36479
+ for (const line of await renderMcpResourceList(cwd, name)) {
36480
+ console.log(line);
36481
+ }
36482
+ return;
36483
+ }
36484
+ if (subcommand === "prompt") {
36485
+ const serverName = tokens[2]?.trim();
36486
+ const promptName = tokens[3]?.trim();
36487
+ if (!serverName || !promptName) {
36488
+ throw new Error("Usage: /mcp prompt <server> <prompt> [key=value ...]");
36489
+ }
36490
+ const args = Object.fromEntries(
36491
+ tokens.slice(4).map((entry) => entry.split("=", 2)).filter((parts) => parts.length === 2 && parts[0].length > 0)
36492
+ );
36493
+ for (const line of await renderMcpPromptInvocation(cwd, serverName, promptName, args)) {
36494
+ console.log(line);
36495
+ }
36496
+ return;
36497
+ }
36498
+ if (subcommand === "read") {
36499
+ const serverName = tokens[2]?.trim();
36500
+ const uri = tokens.slice(3).join(" ").trim();
36501
+ if (!serverName || !uri) {
36502
+ throw new Error("Usage: /mcp read <server> <uri>");
36503
+ }
36504
+ for (const line of await renderMcpResourceRead(cwd, serverName, uri)) {
36505
+ console.log(line);
36506
+ }
36507
+ return;
36508
+ }
35731
36509
  if (subcommand === "remove") {
35732
36510
  const name = tokens[2]?.trim();
35733
36511
  if (!name) {
@@ -35791,7 +36569,7 @@ async function handleMcpCommand(cwd, tokens) {
35791
36569
  console.log(`Added MCP server ${name}`);
35792
36570
  return;
35793
36571
  }
35794
- throw new Error("Usage: /mcp [list|tools [server]|inspect <server>|add <name> --command <cmd>|remove <name>|enable <name>|disable <name>]");
36572
+ throw new Error("Usage: /mcp [list|tools [server]|prompts [server]|resources [server]|prompt <server> <prompt>|read <server> <uri>|add <name> --command <cmd>|remove <name>|enable <name>|disable <name>]");
35795
36573
  }
35796
36574
  async function handleAgentsCommand(cwd, tokens) {
35797
36575
  const subcommand = tokens[1]?.trim().toLowerCase();
@@ -35922,11 +36700,12 @@ async function handleModelSurfaceCommand(cwd, tokens, runtime) {
35922
36700
  throw new Error("Usage: /model [show|providers|templates|add <tpl>|use <provider> [model]|find [search]|select <n>|focus <role>|check]");
35923
36701
  }
35924
36702
  async function createPlanOnly(cwd, goal) {
36703
+ const hydrated = await hydratePromptWithMcpResources(cwd, goal);
35925
36704
  const request = {
35926
- goal,
36705
+ goal: hydrated.prompt,
35927
36706
  mode: "plan",
35928
36707
  cwd,
35929
- workspaceState: await inferPlanningWorkspaceState(cwd, goal),
36708
+ workspaceState: await inferPlanningWorkspaceState(cwd, hydrated.prompt),
35930
36709
  constraints: []
35931
36710
  };
35932
36711
  const activity = new ShellActivityIndicator("planning");
@@ -35939,6 +36718,9 @@ async function createPlanOnly(cwd, goal) {
35939
36718
  }
35940
36719
  const plan = planResult.plan;
35941
36720
  const planPath = await savePlan(plan, cwd);
36721
+ for (const note of hydrated.notes) {
36722
+ console.log(color(DIM, note));
36723
+ }
35942
36724
  for (const line of renderPlanGenerationNotes(planResult)) {
35943
36725
  console.log(line);
35944
36726
  }
@@ -36111,7 +36893,7 @@ function renderModelLine2(model) {
36111
36893
  async function printProviderList(cwd, focusRole) {
36112
36894
  const config2 = await loadConfig(cwd);
36113
36895
  if (!config2) {
36114
- console.log("No config found. Run /init or /providers add <template> first.");
36896
+ console.log("No config found. Run /init or /model add <template> first.");
36115
36897
  return;
36116
36898
  }
36117
36899
  const activeProviderId = config2.brains[focusRole].providerId;
@@ -36163,7 +36945,7 @@ async function addProviderFromTemplate(cwd, options) {
36163
36945
  async function printProviderHealth(cwd) {
36164
36946
  const config2 = await loadConfig(cwd);
36165
36947
  if (!config2) {
36166
- console.log("No config found. Run /init or /providers add <template> first.");
36948
+ console.log("No config found. Run /init or /model add <template> first.");
36167
36949
  return;
36168
36950
  }
36169
36951
  const registry2 = createDefaultBrainProviderRegistry(cwd);
@@ -36180,7 +36962,7 @@ async function printProviderHealth(cwd) {
36180
36962
  async function printBrainAssignments(cwd) {
36181
36963
  const config2 = await loadConfig(cwd);
36182
36964
  if (!config2) {
36183
- console.log("No config found. Run /init or /providers add <template> first.");
36965
+ console.log("No config found. Run /init or /model add <template> first.");
36184
36966
  return;
36185
36967
  }
36186
36968
  for (const role of BRAIN_ROLES) {
@@ -36218,7 +37000,7 @@ async function handleProvidersCommand(cwd, tokens, runtime) {
36218
37000
  if (options.baseUrl) {
36219
37001
  console.log(`Base URL ${options.baseUrl}`);
36220
37002
  }
36221
- console.log(`Next: /providers use ${resolvedProviderId}`);
37003
+ console.log(`Next: /model use ${resolvedProviderId}`);
36222
37004
  return;
36223
37005
  }
36224
37006
  if (subcommand === "use") {
@@ -36258,7 +37040,7 @@ async function handleBrainCommand(cwd, tokens, runtime) {
36258
37040
  async function handleModelsCommand(cwd, tokens, runtime) {
36259
37041
  const config2 = await loadConfig(cwd);
36260
37042
  if (!config2) {
36261
- throw new Error("No config found. Run /init or /providers add <template> first.");
37043
+ throw new Error("No config found. Run /init or /model add <template> first.");
36262
37044
  }
36263
37045
  const providerId = config2.brains[runtime.focusRole].providerId;
36264
37046
  const provider = findProviderById(config2, providerId);
@@ -36301,12 +37083,12 @@ async function handleModelsCommand(cwd, tokens, runtime) {
36301
37083
  console.log(` ${index + 1}. ${renderModelLine2(model)}`);
36302
37084
  }
36303
37085
  console.log(``);
36304
- console.log("Use /select <number> or /use-model <model-id> to assign one to all roles.");
37086
+ console.log("Use /model select <number> or /model use <provider-id> <model-id> to assign one to all roles.");
36305
37087
  }
36306
37088
  async function handleModelSelection(cwd, modelId, runtime) {
36307
37089
  const config2 = await loadConfig(cwd);
36308
37090
  if (!config2) {
36309
- throw new Error("No config found. Run /init or /providers add <template> first.");
37091
+ throw new Error("No config found. Run /init or /model add <template> first.");
36310
37092
  }
36311
37093
  const providerId = config2.brains[runtime.focusRole].providerId;
36312
37094
  const provider = findProviderById(config2, providerId);
@@ -36415,6 +37197,23 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
36415
37197
  if (!head) {
36416
37198
  return cwd;
36417
37199
  }
37200
+ if (head.startsWith("mcp__")) {
37201
+ const parts = head.split("__").filter((part) => part.length > 0);
37202
+ if (parts.length < 3) {
37203
+ throw new Error("Usage: /mcp__<server>__<prompt> [key=value ...]");
37204
+ }
37205
+ const serverName = parts[1];
37206
+ const promptName = parts[2];
37207
+ if (!serverName || !promptName) {
37208
+ throw new Error("Usage: /mcp__<server>__<prompt> [key=value ...]");
37209
+ }
37210
+ const args = Object.fromEntries(
37211
+ tokens.slice(1).map((entry) => entry.split("=", 2)).filter((pair) => pair.length === 2 && pair[0].length > 0)
37212
+ );
37213
+ const rendered = await renderMcpPromptInvocation(cwd, serverName, promptName, args);
37214
+ await handleChatPrompt(cwd, rendered.join("\n"), runtime);
37215
+ return cwd;
37216
+ }
36418
37217
  if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
36419
37218
  const intent = inferPromptIntent(trimmed);
36420
37219
  if (intent === "run") {