@kimbho/kimbho-cli 0.1.14 → 0.1.18
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 +1345 -237
- package/dist/index.cjs.map +3 -3
- package/package.json +1 -1
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
|
|
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 =
|
|
2150
|
+
const localBin = path22.resolve(baseDir, baseName);
|
|
2151
2151
|
if (fs.existsSync(localBin)) return localBin;
|
|
2152
|
-
if (sourceExt.includes(
|
|
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 =
|
|
2171
|
-
|
|
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 =
|
|
2178
|
+
const legacyName = path22.basename(
|
|
2179
2179
|
this._scriptPath,
|
|
2180
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
3053
|
-
if (
|
|
3054
|
-
this._executableDir =
|
|
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(
|
|
6520
|
-
let input =
|
|
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 [
|
|
6720
|
-
wsComponent.path =
|
|
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(
|
|
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 &&
|
|
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,
|
|
10118
|
+
function checkStat(stat2, path22, options) {
|
|
10119
10119
|
if (!stat2.isSymbolicLink() && !stat2.isFile()) {
|
|
10120
10120
|
return false;
|
|
10121
10121
|
}
|
|
10122
|
-
return checkPathExt(
|
|
10122
|
+
return checkPathExt(path22, options);
|
|
10123
10123
|
}
|
|
10124
|
-
function isexe(
|
|
10125
|
-
fs.stat(
|
|
10126
|
-
cb(er, er ? false : checkStat(stat2,
|
|
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(
|
|
10130
|
-
return checkStat(fs.statSync(
|
|
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(
|
|
10142
|
-
fs.stat(
|
|
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(
|
|
10147
|
-
return checkStat(fs.statSync(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
10209
|
+
function sync(path22, options) {
|
|
10210
10210
|
try {
|
|
10211
|
-
return core.sync(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ?
|
|
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 =
|
|
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 [
|
|
10422
|
-
const binary =
|
|
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
|
|
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 =
|
|
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.
|
|
10615
|
+
version: "0.1.18",
|
|
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:
|
|
11410
|
-
const fullPath = [...
|
|
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,
|
|
11526
|
+
constructor(parent, value, path22, key) {
|
|
11527
11527
|
this._cachedPath = [];
|
|
11528
11528
|
this.parent = parent;
|
|
11529
11529
|
this.data = value;
|
|
11530
|
-
this._path =
|
|
11530
|
+
this._path = path22;
|
|
11531
11531
|
this._key = key;
|
|
11532
11532
|
}
|
|
11533
11533
|
get path() {
|
|
@@ -15026,7 +15026,12 @@ 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(),
|
|
15033
|
+
subagentLabel: external_exports.string().min(1).optional(),
|
|
15034
|
+
subagentInstructions: external_exports.string().min(1).optional()
|
|
15030
15035
|
});
|
|
15031
15036
|
var PlanMilestoneSchema = external_exports.object({
|
|
15032
15037
|
id: external_exports.string().min(1),
|
|
@@ -15437,6 +15442,7 @@ function summarizeStructuredOutputError(context, error2) {
|
|
|
15437
15442
|
|
|
15438
15443
|
// ../core/dist/config/config.js
|
|
15439
15444
|
var import_promises = require("node:fs/promises");
|
|
15445
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
15440
15446
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
15441
15447
|
var KIMBHO_DIR_NAME = ".kimbho";
|
|
15442
15448
|
function resolveKimbhoDir(cwd = process.cwd()) {
|
|
@@ -15445,6 +15451,9 @@ function resolveKimbhoDir(cwd = process.cwd()) {
|
|
|
15445
15451
|
function resolveConfigPath(cwd = process.cwd()) {
|
|
15446
15452
|
return import_node_path.default.join(resolveKimbhoDir(cwd), "config.json");
|
|
15447
15453
|
}
|
|
15454
|
+
function resolveGlobalConfigPath() {
|
|
15455
|
+
return import_node_path.default.join(import_node_os.default.homedir(), ".kimbho", "config.json");
|
|
15456
|
+
}
|
|
15448
15457
|
var LegacyProviderNameSchema = external_exports.enum([
|
|
15449
15458
|
"openai",
|
|
15450
15459
|
"anthropic",
|
|
@@ -15599,8 +15608,7 @@ async function ensureKimbhoDir(cwd = process.cwd()) {
|
|
|
15599
15608
|
await (0, import_promises.mkdir)(import_node_path.default.join(root, "teams"), { recursive: true });
|
|
15600
15609
|
return root;
|
|
15601
15610
|
}
|
|
15602
|
-
async function
|
|
15603
|
-
const configPath = resolveConfigPath(cwd);
|
|
15611
|
+
async function loadConfigFile(configPath) {
|
|
15604
15612
|
try {
|
|
15605
15613
|
const raw = await (0, import_promises.readFile)(configPath, "utf8");
|
|
15606
15614
|
return KimbhoConfigSchema.parse(normalizeConfigInput(JSON.parse(raw)));
|
|
@@ -15612,6 +15620,77 @@ async function loadConfig(cwd = process.cwd()) {
|
|
|
15612
15620
|
throw error2;
|
|
15613
15621
|
}
|
|
15614
15622
|
}
|
|
15623
|
+
async function loadProjectConfig(cwd = process.cwd()) {
|
|
15624
|
+
return loadConfigFile(resolveConfigPath(cwd));
|
|
15625
|
+
}
|
|
15626
|
+
async function loadUserConfig() {
|
|
15627
|
+
return loadConfigFile(resolveGlobalConfigPath());
|
|
15628
|
+
}
|
|
15629
|
+
function mergeConfigCatalog(lower, upper) {
|
|
15630
|
+
const merged = /* @__PURE__ */ new Map();
|
|
15631
|
+
for (const provider of lower) {
|
|
15632
|
+
merged.set(provider.id, provider);
|
|
15633
|
+
}
|
|
15634
|
+
for (const provider of upper) {
|
|
15635
|
+
merged.set(provider.id, provider);
|
|
15636
|
+
}
|
|
15637
|
+
return Array.from(merged.values());
|
|
15638
|
+
}
|
|
15639
|
+
function mergeConfigLayers(base, override) {
|
|
15640
|
+
return KimbhoConfigSchema.parse({
|
|
15641
|
+
providers: mergeConfigCatalog(base.providers, override.providers),
|
|
15642
|
+
brains: {
|
|
15643
|
+
planner: {
|
|
15644
|
+
...base.brains.planner,
|
|
15645
|
+
...override.brains.planner
|
|
15646
|
+
},
|
|
15647
|
+
coder: {
|
|
15648
|
+
...base.brains.coder,
|
|
15649
|
+
...override.brains.coder
|
|
15650
|
+
},
|
|
15651
|
+
reviewer: {
|
|
15652
|
+
...base.brains.reviewer,
|
|
15653
|
+
...override.brains.reviewer
|
|
15654
|
+
},
|
|
15655
|
+
fast: {
|
|
15656
|
+
...base.brains.fast,
|
|
15657
|
+
...override.brains.fast
|
|
15658
|
+
}
|
|
15659
|
+
},
|
|
15660
|
+
approvalMode: override.approvalMode ?? base.approvalMode,
|
|
15661
|
+
sandboxMode: override.sandboxMode ?? base.sandboxMode,
|
|
15662
|
+
stackPresets: Array.from(/* @__PURE__ */ new Set([
|
|
15663
|
+
...base.stackPresets,
|
|
15664
|
+
...override.stackPresets
|
|
15665
|
+
])),
|
|
15666
|
+
trustedDirectories: Array.from(/* @__PURE__ */ new Set([
|
|
15667
|
+
...base.trustedDirectories,
|
|
15668
|
+
...override.trustedDirectories
|
|
15669
|
+
]))
|
|
15670
|
+
});
|
|
15671
|
+
}
|
|
15672
|
+
async function loadConfigLayers(cwd = process.cwd()) {
|
|
15673
|
+
const [user, project] = await Promise.all([
|
|
15674
|
+
loadUserConfig(),
|
|
15675
|
+
loadProjectConfig(cwd)
|
|
15676
|
+
]);
|
|
15677
|
+
if (!user && !project) {
|
|
15678
|
+
return {
|
|
15679
|
+
user: null,
|
|
15680
|
+
project: null,
|
|
15681
|
+
resolved: null
|
|
15682
|
+
};
|
|
15683
|
+
}
|
|
15684
|
+
return {
|
|
15685
|
+
user,
|
|
15686
|
+
project,
|
|
15687
|
+
resolved: user && project ? mergeConfigLayers(user, project) : project ?? user
|
|
15688
|
+
};
|
|
15689
|
+
}
|
|
15690
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
15691
|
+
const layers = await loadConfigLayers(cwd);
|
|
15692
|
+
return layers.resolved;
|
|
15693
|
+
}
|
|
15615
15694
|
async function saveConfig(config2, cwd = process.cwd()) {
|
|
15616
15695
|
await ensureKimbhoDir(cwd);
|
|
15617
15696
|
const configPath = resolveConfigPath(cwd);
|
|
@@ -15622,6 +15701,16 @@ async function saveConfig(config2, cwd = process.cwd()) {
|
|
|
15622
15701
|
await (0, import_promises.rename)(tempPath, configPath);
|
|
15623
15702
|
return configPath;
|
|
15624
15703
|
}
|
|
15704
|
+
async function saveUserConfig(config2) {
|
|
15705
|
+
const configPath = resolveGlobalConfigPath();
|
|
15706
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(configPath), { recursive: true });
|
|
15707
|
+
const normalized = KimbhoConfigSchema.parse(config2);
|
|
15708
|
+
const tempPath = `${configPath}.tmp-${process.pid}-${Date.now()}`;
|
|
15709
|
+
await (0, import_promises.writeFile)(tempPath, `${JSON.stringify(normalized, null, 2)}
|
|
15710
|
+
`, "utf8");
|
|
15711
|
+
await (0, import_promises.rename)(tempPath, configPath);
|
|
15712
|
+
return configPath;
|
|
15713
|
+
}
|
|
15625
15714
|
function findProviderById(config2, providerId) {
|
|
15626
15715
|
return config2.providers.find((provider) => provider.id === providerId);
|
|
15627
15716
|
}
|
|
@@ -15740,10 +15829,10 @@ async function loadLatestSession(cwd = process.cwd()) {
|
|
|
15740
15829
|
|
|
15741
15830
|
// ../core/dist/memory.js
|
|
15742
15831
|
var import_promises3 = require("node:fs/promises");
|
|
15743
|
-
var
|
|
15832
|
+
var import_node_os2 = __toESM(require("node:os"), 1);
|
|
15744
15833
|
var import_node_path3 = __toESM(require("node:path"), 1);
|
|
15745
15834
|
function resolveGlobalKimbhoHome() {
|
|
15746
|
-
return import_node_path3.default.join(
|
|
15835
|
+
return import_node_path3.default.join(import_node_os2.default.homedir(), ".kimbho");
|
|
15747
15836
|
}
|
|
15748
15837
|
function resolveUserMemoryPath() {
|
|
15749
15838
|
return import_node_path3.default.join(resolveGlobalKimbhoHome(), "memory.md");
|
|
@@ -15807,6 +15896,54 @@ ${block}` : `# ${import_node_path3.default.basename(filePath, import_node_path3.
|
|
|
15807
15896
|
|
|
15808
15897
|
${block}`);
|
|
15809
15898
|
}
|
|
15899
|
+
function uniqueOrdered(values) {
|
|
15900
|
+
return Array.from(new Set(values));
|
|
15901
|
+
}
|
|
15902
|
+
function candidateMemoryFilesForDirectory(directory) {
|
|
15903
|
+
return [
|
|
15904
|
+
import_node_path3.default.join(directory, "KIMBHO.md"),
|
|
15905
|
+
import_node_path3.default.join(directory, "KIMBHO.local.md"),
|
|
15906
|
+
import_node_path3.default.join(directory, "CLAUDE.md"),
|
|
15907
|
+
import_node_path3.default.join(directory, "CLAUDE.local.md")
|
|
15908
|
+
];
|
|
15909
|
+
}
|
|
15910
|
+
function discoverMarkdownMemoryPaths(cwd = process.cwd()) {
|
|
15911
|
+
const discovered = [];
|
|
15912
|
+
let current = import_node_path3.default.resolve(cwd);
|
|
15913
|
+
const root = import_node_path3.default.parse(current).root;
|
|
15914
|
+
while (true) {
|
|
15915
|
+
discovered.push(...candidateMemoryFilesForDirectory(current));
|
|
15916
|
+
if (current === root) {
|
|
15917
|
+
break;
|
|
15918
|
+
}
|
|
15919
|
+
current = import_node_path3.default.dirname(current);
|
|
15920
|
+
}
|
|
15921
|
+
discovered.push(import_node_path3.default.join(cwd, "kimbho_init.md"));
|
|
15922
|
+
discovered.push(resolveProjectMemoryPath(cwd));
|
|
15923
|
+
discovered.push(resolveUserMemoryPath());
|
|
15924
|
+
return uniqueOrdered(discovered);
|
|
15925
|
+
}
|
|
15926
|
+
async function loadMarkdownMemoryContext(cwd = process.cwd(), options = {}) {
|
|
15927
|
+
const candidates = discoverMarkdownMemoryPaths(cwd);
|
|
15928
|
+
const records = [];
|
|
15929
|
+
const maxFiles = options.maxFiles ?? 12;
|
|
15930
|
+
const maxCharsPerFile = options.maxCharsPerFile ?? 6e3;
|
|
15931
|
+
for (const filePath of candidates) {
|
|
15932
|
+
if (records.length >= maxFiles) {
|
|
15933
|
+
break;
|
|
15934
|
+
}
|
|
15935
|
+
const content = await readMarkdownIfExists(filePath);
|
|
15936
|
+
if (!content || content.trim().length === 0) {
|
|
15937
|
+
continue;
|
|
15938
|
+
}
|
|
15939
|
+
records.push({
|
|
15940
|
+
filePath,
|
|
15941
|
+
content: content.length > maxCharsPerFile ? `${content.slice(0, maxCharsPerFile)}
|
|
15942
|
+
... [truncated]` : content
|
|
15943
|
+
});
|
|
15944
|
+
}
|
|
15945
|
+
return records;
|
|
15946
|
+
}
|
|
15810
15947
|
|
|
15811
15948
|
// ../core/dist/mcp.js
|
|
15812
15949
|
var import_promises4 = require("node:fs/promises");
|
|
@@ -15996,6 +16133,8 @@ async function loadAgentTeams(cwd) {
|
|
|
15996
16133
|
id,
|
|
15997
16134
|
label: typeof attributes.label === "string" ? attributes.label : id,
|
|
15998
16135
|
agents: asStringArray(attributes.agents),
|
|
16136
|
+
match: asStringArray(attributes.match),
|
|
16137
|
+
strategy: typeof attributes.strategy === "string" && attributes.strategy === "parallel" ? "parallel" : "sequential",
|
|
15999
16138
|
filePath,
|
|
16000
16139
|
...body.trim() ? {
|
|
16001
16140
|
description: body.trim()
|
|
@@ -16044,8 +16183,11 @@ async function createAgentTeamFile(cwd, id, agentIds, label) {
|
|
|
16044
16183
|
"---",
|
|
16045
16184
|
`id: ${id}`,
|
|
16046
16185
|
`label: ${label ?? id}`,
|
|
16186
|
+
"match:",
|
|
16187
|
+
" - feature",
|
|
16047
16188
|
"agents:",
|
|
16048
16189
|
...agentIds.map((agentId) => ` - ${agentId}`),
|
|
16190
|
+
"strategy: sequential",
|
|
16049
16191
|
"---",
|
|
16050
16192
|
"",
|
|
16051
16193
|
`# ${label ?? id}`,
|
|
@@ -16072,11 +16214,46 @@ function scoreCustomAgentMatch(definition, task, request) {
|
|
|
16072
16214
|
}
|
|
16073
16215
|
return definition.match.reduce((score, keyword) => haystack.includes(keyword.toLowerCase()) ? score + 1 : score, 0);
|
|
16074
16216
|
}
|
|
16217
|
+
function scoreAgentTeamMatch(definition, task, request) {
|
|
16218
|
+
const haystack = [
|
|
16219
|
+
request.goal,
|
|
16220
|
+
task.title,
|
|
16221
|
+
task.description,
|
|
16222
|
+
...task.acceptanceCriteria
|
|
16223
|
+
].join(" ").toLowerCase();
|
|
16224
|
+
if (definition.match.length === 0) {
|
|
16225
|
+
return definition.agents.length > 0 ? 1 : -1;
|
|
16226
|
+
}
|
|
16227
|
+
return definition.match.reduce((score, keyword) => haystack.includes(keyword.toLowerCase()) ? score + 1 : score, 0);
|
|
16228
|
+
}
|
|
16229
|
+
async function selectAgentTeam(cwd, task, request) {
|
|
16230
|
+
const [teams, agents] = await Promise.all([
|
|
16231
|
+
loadAgentTeams(cwd),
|
|
16232
|
+
loadCustomAgents(cwd)
|
|
16233
|
+
]);
|
|
16234
|
+
const selected = teams.map((team) => ({
|
|
16235
|
+
team,
|
|
16236
|
+
score: scoreAgentTeamMatch(team, task, request)
|
|
16237
|
+
})).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.team.id.localeCompare(right.team.id))[0]?.team;
|
|
16238
|
+
if (!selected) {
|
|
16239
|
+
return null;
|
|
16240
|
+
}
|
|
16241
|
+
const members = selected.agents.map((agentId) => agents.find((candidate) => candidate.id === agentId)).filter((candidate) => Boolean(candidate));
|
|
16242
|
+
return {
|
|
16243
|
+
team: selected,
|
|
16244
|
+
members
|
|
16245
|
+
};
|
|
16246
|
+
}
|
|
16075
16247
|
async function selectCustomAgentOverlay(cwd, task, request, builtInTools) {
|
|
16076
|
-
const
|
|
16248
|
+
const selectedTeam = task.teamId ? await selectAgentTeam(cwd, task, request) : await selectAgentTeam(cwd, task, request);
|
|
16249
|
+
const allDefinitions = await loadCustomAgents(cwd);
|
|
16250
|
+
const preferredDefinition = task.customAgentId ? allDefinitions.find((definition) => definition.id === task.customAgentId) : null;
|
|
16251
|
+
const definitions = preferredDefinition ? [
|
|
16252
|
+
preferredDefinition
|
|
16253
|
+
] : selectedTeam?.members.length ? selectedTeam.members : allDefinitions;
|
|
16077
16254
|
const ranked = definitions.map((definition) => ({
|
|
16078
16255
|
definition,
|
|
16079
|
-
score: scoreCustomAgentMatch(definition, task, request)
|
|
16256
|
+
score: preferredDefinition && definition.id === preferredDefinition.id ? Number.MAX_SAFE_INTEGER : scoreCustomAgentMatch(definition, task, request)
|
|
16080
16257
|
})).filter((candidate) => candidate.score > 0).sort((left, right) => right.score - left.score || left.definition.id.localeCompare(right.definition.id));
|
|
16081
16258
|
const selected = ranked[0]?.definition;
|
|
16082
16259
|
if (!selected) {
|
|
@@ -16090,6 +16267,12 @@ async function selectCustomAgentOverlay(cwd, task, request, builtInTools) {
|
|
|
16090
16267
|
purpose: selected.purpose ?? baseProfile.purpose,
|
|
16091
16268
|
brainRole: selected.brainRole ?? baseProfile.brainRole,
|
|
16092
16269
|
promptPreamble: [
|
|
16270
|
+
...selectedTeam ? [
|
|
16271
|
+
`Agent team active: ${selectedTeam.team.label} (${selectedTeam.team.id}).`,
|
|
16272
|
+
`Team strategy: ${selectedTeam.team.strategy}.`,
|
|
16273
|
+
`Team roster: ${selectedTeam.members.map((member) => member.id).join(", ") || "(none)"}.`,
|
|
16274
|
+
selectedTeam.team.description?.trim() ?? ""
|
|
16275
|
+
].filter((line) => line.length > 0) : [],
|
|
16093
16276
|
`Custom agent overlay: ${selected.label} (${selected.id}).`,
|
|
16094
16277
|
`Base role: ${selected.baseRole}.`,
|
|
16095
16278
|
selected.instructions.trim()
|
|
@@ -17311,7 +17494,7 @@ var import_promises11 = require("node:fs/promises");
|
|
|
17311
17494
|
var import_node_path11 = __toESM(require("node:path"), 1);
|
|
17312
17495
|
var import_node_process2 = __toESM(require("node:process"), 1);
|
|
17313
17496
|
var import_node_child_process2 = require("node:child_process");
|
|
17314
|
-
var
|
|
17497
|
+
var import_node_os3 = require("node:os");
|
|
17315
17498
|
|
|
17316
17499
|
// ../tools/dist/browser-manager.js
|
|
17317
17500
|
var import_node_crypto2 = require("node:crypto");
|
|
@@ -18109,6 +18292,47 @@ var KNOWN_SCAFFOLD_PRESETS = [
|
|
|
18109
18292
|
];
|
|
18110
18293
|
function looksLikeStaticLandingGoal(value) {
|
|
18111
18294
|
const normalized = value.toLowerCase();
|
|
18295
|
+
const backendSignals = [
|
|
18296
|
+
"api",
|
|
18297
|
+
"database",
|
|
18298
|
+
"backend",
|
|
18299
|
+
"server",
|
|
18300
|
+
"migration",
|
|
18301
|
+
"schema",
|
|
18302
|
+
"prisma",
|
|
18303
|
+
"postgres",
|
|
18304
|
+
"sqlite",
|
|
18305
|
+
"auth",
|
|
18306
|
+
"billing",
|
|
18307
|
+
"webhook"
|
|
18308
|
+
];
|
|
18309
|
+
const pageSignals = [
|
|
18310
|
+
"page",
|
|
18311
|
+
"landing",
|
|
18312
|
+
"lnding",
|
|
18313
|
+
"homepage",
|
|
18314
|
+
"home page",
|
|
18315
|
+
"site",
|
|
18316
|
+
"website",
|
|
18317
|
+
"ui",
|
|
18318
|
+
"hero",
|
|
18319
|
+
"layout",
|
|
18320
|
+
"screen"
|
|
18321
|
+
];
|
|
18322
|
+
const polishSignals = [
|
|
18323
|
+
"animate",
|
|
18324
|
+
"animated",
|
|
18325
|
+
"animation",
|
|
18326
|
+
"motion",
|
|
18327
|
+
"rich",
|
|
18328
|
+
"richer",
|
|
18329
|
+
"polish",
|
|
18330
|
+
"styled",
|
|
18331
|
+
"styling",
|
|
18332
|
+
"visual",
|
|
18333
|
+
"redesign",
|
|
18334
|
+
"revamp"
|
|
18335
|
+
];
|
|
18112
18336
|
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
18337
|
return true;
|
|
18114
18338
|
}
|
|
@@ -18118,6 +18342,12 @@ function looksLikeStaticLandingGoal(value) {
|
|
|
18118
18342
|
if (normalized.includes("restaurant") && (normalized.includes("page") || normalized.includes("site") || normalized.includes("website") || normalized.includes("landing") || normalized.includes("lnding"))) {
|
|
18119
18343
|
return true;
|
|
18120
18344
|
}
|
|
18345
|
+
if (pageSignals.some((signal) => normalized.includes(signal)) && polishSignals.some((signal) => normalized.includes(signal)) && !backendSignals.some((signal) => normalized.includes(signal))) {
|
|
18346
|
+
return true;
|
|
18347
|
+
}
|
|
18348
|
+
if (normalized.includes("blog") && (normalized.includes("make") || normalized.includes("improve") || normalized.includes("enhance") || normalized.includes("redesign")) && !backendSignals.some((signal) => normalized.includes(signal))) {
|
|
18349
|
+
return true;
|
|
18350
|
+
}
|
|
18121
18351
|
return false;
|
|
18122
18352
|
}
|
|
18123
18353
|
function sanitizeName(value) {
|
|
@@ -19019,7 +19249,7 @@ async function createUnifiedDiffPreview(cwd, targetPath, beforeContent, afterCon
|
|
|
19019
19249
|
if (beforeContent === afterContent) {
|
|
19020
19250
|
return void 0;
|
|
19021
19251
|
}
|
|
19022
|
-
const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0,
|
|
19252
|
+
const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os3.tmpdir)(), "kimbho-diff-"));
|
|
19023
19253
|
const beforePath = import_node_path11.default.join(tempDir, "before");
|
|
19024
19254
|
const afterPath = import_node_path11.default.join(tempDir, "after");
|
|
19025
19255
|
const relativeTarget = import_node_path11.default.relative(cwd, targetPath) || import_node_path11.default.basename(targetPath);
|
|
@@ -19051,10 +19281,17 @@ async function createUnifiedDiffPreview(cwd, targetPath, beforeContent, afterCon
|
|
|
19051
19281
|
await (0, import_promises11.rm)(tempDir, { recursive: true, force: true });
|
|
19052
19282
|
}
|
|
19053
19283
|
}
|
|
19054
|
-
function
|
|
19284
|
+
function isTrustedWorkspacePath(resolved, trustedDirectories = []) {
|
|
19285
|
+
return trustedDirectories.some((trustedDirectory) => {
|
|
19286
|
+
const trustedRoot = import_node_path11.default.resolve(trustedDirectory);
|
|
19287
|
+
const relative = import_node_path11.default.relative(trustedRoot, resolved);
|
|
19288
|
+
return relative === "" || !relative.startsWith("..") && !import_node_path11.default.isAbsolute(relative);
|
|
19289
|
+
});
|
|
19290
|
+
}
|
|
19291
|
+
function resolveWorkspacePath(cwd, filePath, trustedDirectories = []) {
|
|
19055
19292
|
const resolved = import_node_path11.default.resolve(cwd, filePath);
|
|
19056
19293
|
const relative = import_node_path11.default.relative(cwd, resolved);
|
|
19057
|
-
if (relative.startsWith("..") || import_node_path11.default.isAbsolute(relative)) {
|
|
19294
|
+
if ((relative.startsWith("..") || import_node_path11.default.isAbsolute(relative)) && !isTrustedWorkspacePath(resolved, trustedDirectories)) {
|
|
19058
19295
|
throw new Error(`Path "${filePath}" escapes the workspace.`);
|
|
19059
19296
|
}
|
|
19060
19297
|
return resolved;
|
|
@@ -19137,10 +19374,10 @@ function normalizeArtifactRelativePath(cwd, absolutePath) {
|
|
|
19137
19374
|
function isProtectedWritePath(relativePath) {
|
|
19138
19375
|
return PROTECTED_WRITE_PREFIXES.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}/`));
|
|
19139
19376
|
}
|
|
19140
|
-
function extractProspectiveWriteTargets(toolId, input, cwd) {
|
|
19377
|
+
function extractProspectiveWriteTargets(toolId, input, cwd, trustedDirectories = []) {
|
|
19141
19378
|
if (toolId === "file.write" && typeof input.path === "string") {
|
|
19142
19379
|
return [
|
|
19143
|
-
normalizeArtifactRelativePath(cwd, resolveWorkspacePath(cwd, input.path))
|
|
19380
|
+
normalizeArtifactRelativePath(cwd, resolveWorkspacePath(cwd, input.path, trustedDirectories))
|
|
19144
19381
|
];
|
|
19145
19382
|
}
|
|
19146
19383
|
if (toolId === "file.patch" && typeof input.patch === "string") {
|
|
@@ -19212,7 +19449,7 @@ function enforceToolPolicy(toolId, input, descriptor, context) {
|
|
|
19212
19449
|
return null;
|
|
19213
19450
|
}
|
|
19214
19451
|
if ((toolId === "file.write" || toolId === "file.patch") && sandboxMode === "workspace-write") {
|
|
19215
|
-
const targets = extractProspectiveWriteTargets(toolId, input, context.cwd);
|
|
19452
|
+
const targets = extractProspectiveWriteTargets(toolId, input, context.cwd, context.trustedDirectories);
|
|
19216
19453
|
const protectedTargets = targets.filter((target) => isProtectedWritePath(target));
|
|
19217
19454
|
if (protectedTargets.length > 0) {
|
|
19218
19455
|
return createPolicyFailure(toolId, `Blocked write to protected workspace paths: ${protectedTargets.join(", ")}`);
|
|
@@ -19319,7 +19556,7 @@ async function executeFileRead(input, context) {
|
|
|
19319
19556
|
if (!rawPath) {
|
|
19320
19557
|
throw new Error("file.read requires a string path.");
|
|
19321
19558
|
}
|
|
19322
|
-
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
19559
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
|
|
19323
19560
|
const contents = await (0, import_promises11.readFile)(targetPath, "utf8");
|
|
19324
19561
|
return ToolResultSchema.parse({
|
|
19325
19562
|
toolId: "file.read",
|
|
@@ -19340,7 +19577,7 @@ async function executeFileList(input, context, timeoutMs) {
|
|
|
19340
19577
|
const root = typeof input.path === "string" && input.path.trim().length > 0 ? input.path : ".";
|
|
19341
19578
|
const pattern = typeof input.pattern === "string" && input.pattern.trim().length > 0 ? input.pattern : null;
|
|
19342
19579
|
const limit = parseLimitValue(input.limit, 200);
|
|
19343
|
-
const searchRoot = resolveWorkspacePath(context.cwd, root);
|
|
19580
|
+
const searchRoot = resolveWorkspacePath(context.cwd, root, context.trustedDirectories);
|
|
19344
19581
|
const relativeRoot = import_node_path11.default.relative(context.cwd, searchRoot) || ".";
|
|
19345
19582
|
const rootArg = relativeRoot === "." ? "." : relativeRoot;
|
|
19346
19583
|
let result;
|
|
@@ -19388,7 +19625,7 @@ async function executeFileSearch(input, context, timeoutMs) {
|
|
|
19388
19625
|
if (!pattern) {
|
|
19389
19626
|
throw new Error("file.search requires a non-empty pattern.");
|
|
19390
19627
|
}
|
|
19391
|
-
const searchRoot = resolveWorkspacePath(context.cwd, root);
|
|
19628
|
+
const searchRoot = resolveWorkspacePath(context.cwd, root, context.trustedDirectories);
|
|
19392
19629
|
const relativeRoot = import_node_path11.default.relative(context.cwd, searchRoot) || ".";
|
|
19393
19630
|
const rootArg = relativeRoot === "." ? "." : relativeRoot;
|
|
19394
19631
|
let result;
|
|
@@ -19444,7 +19681,7 @@ async function executeFileWrite(input, context) {
|
|
|
19444
19681
|
if (content === null) {
|
|
19445
19682
|
throw new Error("file.write requires a string content field.");
|
|
19446
19683
|
}
|
|
19447
|
-
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
19684
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
|
|
19448
19685
|
let existed = true;
|
|
19449
19686
|
let previousContent = "";
|
|
19450
19687
|
try {
|
|
@@ -19522,7 +19759,7 @@ async function executeGitDiff(input, context, timeoutMs) {
|
|
|
19522
19759
|
"--unified=3"
|
|
19523
19760
|
];
|
|
19524
19761
|
if (rawPath) {
|
|
19525
|
-
const targetPath = resolveWorkspacePath(context.cwd, rawPath);
|
|
19762
|
+
const targetPath = resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories);
|
|
19526
19763
|
const relativeTarget = import_node_path11.default.relative(context.cwd, targetPath) || ".";
|
|
19527
19764
|
args.push("--", relativeTarget);
|
|
19528
19765
|
}
|
|
@@ -19859,7 +20096,7 @@ async function executeProcessStart(input, context) {
|
|
|
19859
20096
|
if (!command) {
|
|
19860
20097
|
throw new Error("process.start requires a command string.");
|
|
19861
20098
|
}
|
|
19862
|
-
const processCwd = rawPath ? resolveWorkspacePath(context.cwd, rawPath) : context.cwd;
|
|
20099
|
+
const processCwd = rawPath ? resolveWorkspacePath(context.cwd, rawPath, context.trustedDirectories) : context.cwd;
|
|
19863
20100
|
const record2 = await startManagedProcess(processCwd, command, name, context.signal);
|
|
19864
20101
|
return ToolResultSchema.parse({
|
|
19865
20102
|
toolId: "process.start",
|
|
@@ -19957,7 +20194,7 @@ async function executeFilePatch(input, context, timeoutMs) {
|
|
|
19957
20194
|
if (!patch) {
|
|
19958
20195
|
throw new Error("file.patch requires a unified diff in the patch field.");
|
|
19959
20196
|
}
|
|
19960
|
-
const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0,
|
|
20197
|
+
const tempDir = await (0, import_promises11.mkdtemp)(import_node_path11.default.join((0, import_node_os3.tmpdir)(), "kimbho-patch-"));
|
|
19961
20198
|
const patchPath = import_node_path11.default.join(tempDir, "change.patch");
|
|
19962
20199
|
try {
|
|
19963
20200
|
await (0, import_promises11.writeFile)(patchPath, patch, "utf8");
|
|
@@ -20310,10 +20547,10 @@ function assignProp(target, prop, value) {
|
|
|
20310
20547
|
configurable: true
|
|
20311
20548
|
});
|
|
20312
20549
|
}
|
|
20313
|
-
function getElementAtPath(obj,
|
|
20314
|
-
if (!
|
|
20550
|
+
function getElementAtPath(obj, path22) {
|
|
20551
|
+
if (!path22)
|
|
20315
20552
|
return obj;
|
|
20316
|
-
return
|
|
20553
|
+
return path22.reduce((acc, key) => acc?.[key], obj);
|
|
20317
20554
|
}
|
|
20318
20555
|
function promiseAllObject(promisesObj) {
|
|
20319
20556
|
const keys = Object.keys(promisesObj);
|
|
@@ -20633,11 +20870,11 @@ function aborted(x, startIndex = 0) {
|
|
|
20633
20870
|
}
|
|
20634
20871
|
return false;
|
|
20635
20872
|
}
|
|
20636
|
-
function prefixIssues(
|
|
20873
|
+
function prefixIssues(path22, issues) {
|
|
20637
20874
|
return issues.map((iss) => {
|
|
20638
20875
|
var _a;
|
|
20639
20876
|
(_a = iss).path ?? (_a.path = []);
|
|
20640
|
-
iss.path.unshift(
|
|
20877
|
+
iss.path.unshift(path22);
|
|
20641
20878
|
return iss;
|
|
20642
20879
|
});
|
|
20643
20880
|
}
|
|
@@ -27385,6 +27622,7 @@ var MCP_ALLOWED_ROLES = [
|
|
|
27385
27622
|
"reviewer",
|
|
27386
27623
|
"integrator"
|
|
27387
27624
|
];
|
|
27625
|
+
var MCP_INVENTORY_TIMEOUT_MS = 8e3;
|
|
27388
27626
|
function toMcpToolId(serverName, toolName) {
|
|
27389
27627
|
return `mcp.${serverName}.${toolName}`;
|
|
27390
27628
|
}
|
|
@@ -27427,7 +27665,7 @@ function renderMcpContent(result) {
|
|
|
27427
27665
|
}
|
|
27428
27666
|
return sections.length > 0 ? sections.join("\n\n") : void 0;
|
|
27429
27667
|
}
|
|
27430
|
-
async function withMcpClient(cwd, name, server, handler) {
|
|
27668
|
+
async function withMcpClient(cwd, name, server, handler, options = {}) {
|
|
27431
27669
|
const transport = new StdioClientTransport({
|
|
27432
27670
|
command: server.command,
|
|
27433
27671
|
args: server.args,
|
|
@@ -27446,10 +27684,31 @@ async function withMcpClient(cwd, name, server, handler) {
|
|
|
27446
27684
|
name: "kimbho",
|
|
27447
27685
|
version: "0.1.13"
|
|
27448
27686
|
});
|
|
27449
|
-
|
|
27687
|
+
const run = async () => {
|
|
27688
|
+
await client.connect(transport);
|
|
27689
|
+
return handler(client);
|
|
27690
|
+
};
|
|
27450
27691
|
try {
|
|
27451
|
-
|
|
27692
|
+
if (typeof options.timeoutMs === "number" && options.timeoutMs > 0) {
|
|
27693
|
+
let timer;
|
|
27694
|
+
try {
|
|
27695
|
+
return await Promise.race([
|
|
27696
|
+
run(),
|
|
27697
|
+
new Promise((_, reject) => {
|
|
27698
|
+
timer = setTimeout(() => {
|
|
27699
|
+
reject(new Error(`${options.timeoutLabel ?? `MCP call for ${name}`} timed out after ${options.timeoutMs}ms.`));
|
|
27700
|
+
}, options.timeoutMs);
|
|
27701
|
+
})
|
|
27702
|
+
]);
|
|
27703
|
+
} finally {
|
|
27704
|
+
if (timer) {
|
|
27705
|
+
clearTimeout(timer);
|
|
27706
|
+
}
|
|
27707
|
+
}
|
|
27708
|
+
}
|
|
27709
|
+
return await run();
|
|
27452
27710
|
} finally {
|
|
27711
|
+
await client.close().catch(() => void 0);
|
|
27453
27712
|
await transport.close().catch(() => void 0);
|
|
27454
27713
|
}
|
|
27455
27714
|
}
|
|
@@ -27458,31 +27717,65 @@ async function loadMcpServerInventory(cwd) {
|
|
|
27458
27717
|
const servers = Object.entries(config2.mcpServers).filter(([, server]) => server.enabled !== false).sort(([left], [right]) => left.localeCompare(right));
|
|
27459
27718
|
const inventory = [];
|
|
27460
27719
|
for (const [name, server] of servers) {
|
|
27461
|
-
|
|
27462
|
-
const
|
|
27463
|
-
|
|
27464
|
-
|
|
27465
|
-
|
|
27466
|
-
|
|
27467
|
-
|
|
27468
|
-
|
|
27720
|
+
try {
|
|
27721
|
+
const record2 = await withMcpClient(cwd, name, server, async (client) => {
|
|
27722
|
+
const [toolsResponse, promptsResponse, resourcesResponse, resourceTemplatesResponse] = await Promise.all([
|
|
27723
|
+
client.listTools().catch(() => ({ tools: [] })),
|
|
27724
|
+
client.listPrompts().catch(() => ({ prompts: [] })),
|
|
27725
|
+
client.listResources().catch(() => ({ resources: [] })),
|
|
27726
|
+
client.listResourceTemplates().catch(() => ({ resourceTemplates: [] }))
|
|
27727
|
+
]);
|
|
27728
|
+
const instructions = client.getInstructions();
|
|
27729
|
+
return {
|
|
27730
|
+
name,
|
|
27731
|
+
config: server,
|
|
27732
|
+
tools: (toolsResponse.tools ?? []).map((tool) => ({
|
|
27733
|
+
serverName: name,
|
|
27734
|
+
toolName: tool.name,
|
|
27735
|
+
toolId: toMcpToolId(name, tool.name),
|
|
27736
|
+
description: tool.description ?? `MCP tool ${tool.name} from ${name}.`,
|
|
27737
|
+
permission: permissionFromAnnotations(tool.annotations)
|
|
27738
|
+
})),
|
|
27739
|
+
prompts: (promptsResponse.prompts ?? []).map((prompt) => ({
|
|
27740
|
+
serverName: name,
|
|
27741
|
+
name: prompt.name,
|
|
27742
|
+
description: prompt.description ?? `MCP prompt ${prompt.name} from ${name}.`,
|
|
27743
|
+
arguments: prompt.arguments ?? []
|
|
27744
|
+
})),
|
|
27745
|
+
resources: (resourcesResponse.resources ?? []).map((resource) => ({
|
|
27746
|
+
serverName: name,
|
|
27747
|
+
uri: resource.uri,
|
|
27748
|
+
name: resource.name,
|
|
27749
|
+
description: resource.description,
|
|
27750
|
+
mimeType: resource.mimeType
|
|
27751
|
+
})),
|
|
27752
|
+
resourceTemplates: (resourceTemplatesResponse.resourceTemplates ?? []).map((resourceTemplate) => ({
|
|
27753
|
+
serverName: name,
|
|
27754
|
+
uriTemplate: resourceTemplate.uriTemplate,
|
|
27755
|
+
name: resourceTemplate.name,
|
|
27756
|
+
description: resourceTemplate.description,
|
|
27757
|
+
mimeType: resourceTemplate.mimeType
|
|
27758
|
+
})),
|
|
27759
|
+
...instructions ? {
|
|
27760
|
+
instructions
|
|
27761
|
+
} : {}
|
|
27762
|
+
};
|
|
27763
|
+
}, {
|
|
27764
|
+
timeoutMs: MCP_INVENTORY_TIMEOUT_MS,
|
|
27765
|
+
timeoutLabel: `MCP inventory for ${name}`
|
|
27766
|
+
});
|
|
27767
|
+
inventory.push(record2);
|
|
27768
|
+
} catch (error2) {
|
|
27769
|
+
inventory.push({
|
|
27469
27770
|
name,
|
|
27470
27771
|
config: server,
|
|
27471
|
-
tools:
|
|
27472
|
-
|
|
27473
|
-
|
|
27474
|
-
|
|
27475
|
-
|
|
27476
|
-
|
|
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);
|
|
27772
|
+
tools: [],
|
|
27773
|
+
prompts: [],
|
|
27774
|
+
resources: [],
|
|
27775
|
+
resourceTemplates: [],
|
|
27776
|
+
warning: error2 instanceof Error ? error2.message : String(error2)
|
|
27777
|
+
});
|
|
27778
|
+
}
|
|
27486
27779
|
}
|
|
27487
27780
|
return inventory;
|
|
27488
27781
|
}
|
|
@@ -27513,6 +27806,59 @@ async function callMcpTool(cwd, serverName, toolName, input) {
|
|
|
27513
27806
|
artifacts: []
|
|
27514
27807
|
};
|
|
27515
27808
|
}
|
|
27809
|
+
function renderPromptContent(result) {
|
|
27810
|
+
return result.messages.map((message) => {
|
|
27811
|
+
if (message.content.type === "text") {
|
|
27812
|
+
return `[${message.role}] ${message.content.text}`;
|
|
27813
|
+
}
|
|
27814
|
+
if (message.content.type === "resource") {
|
|
27815
|
+
const resource = message.content.resource;
|
|
27816
|
+
return `[${message.role}] resource ${resource.uri}
|
|
27817
|
+
${"text" in resource ? resource.text : `[binary ${resource.mimeType ?? "resource"}]`}`;
|
|
27818
|
+
}
|
|
27819
|
+
if (message.content.type === "resource_link") {
|
|
27820
|
+
return `[${message.role}] resource-link ${message.content.uri}`;
|
|
27821
|
+
}
|
|
27822
|
+
return `[${message.role}] ${JSON.stringify(message.content, null, 2)}`;
|
|
27823
|
+
}).join("\n\n");
|
|
27824
|
+
}
|
|
27825
|
+
async function invokeMcpPrompt(cwd, serverName, promptName, args) {
|
|
27826
|
+
const config2 = await loadMcpConfig(cwd);
|
|
27827
|
+
const server = config2.mcpServers[serverName];
|
|
27828
|
+
if (!server || server.enabled === false) {
|
|
27829
|
+
throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
|
|
27830
|
+
}
|
|
27831
|
+
const result = await withMcpClient(cwd, serverName, server, async (client) => client.getPrompt({
|
|
27832
|
+
name: promptName,
|
|
27833
|
+
arguments: args
|
|
27834
|
+
}));
|
|
27835
|
+
return {
|
|
27836
|
+
serverName,
|
|
27837
|
+
promptName,
|
|
27838
|
+
...result.description ? {
|
|
27839
|
+
description: result.description
|
|
27840
|
+
} : {},
|
|
27841
|
+
content: renderPromptContent(result)
|
|
27842
|
+
};
|
|
27843
|
+
}
|
|
27844
|
+
async function readMcpResource(cwd, serverName, uri) {
|
|
27845
|
+
const config2 = await loadMcpConfig(cwd);
|
|
27846
|
+
const server = config2.mcpServers[serverName];
|
|
27847
|
+
if (!server || server.enabled === false) {
|
|
27848
|
+
throw new Error(`MCP server ${serverName} is not configured or is disabled.`);
|
|
27849
|
+
}
|
|
27850
|
+
const result = await withMcpClient(cwd, serverName, server, async (client) => client.readResource({
|
|
27851
|
+
uri
|
|
27852
|
+
}));
|
|
27853
|
+
const content = result.contents.map((item) => "text" in item ? `resource: ${item.uri}
|
|
27854
|
+
${item.text}` : `resource: ${item.uri}
|
|
27855
|
+
[binary ${item.mimeType ?? "resource"}]`).join("\n\n");
|
|
27856
|
+
return {
|
|
27857
|
+
serverName,
|
|
27858
|
+
uri,
|
|
27859
|
+
content
|
|
27860
|
+
};
|
|
27861
|
+
}
|
|
27516
27862
|
async function registerMcpToolsForWorkspace(cwd, registry2, runtime) {
|
|
27517
27863
|
const tools = await discoverMcpTools(cwd);
|
|
27518
27864
|
for (const tool of tools) {
|
|
@@ -27536,7 +27882,7 @@ async function registerMcpToolsForWorkspace(cwd, registry2, runtime) {
|
|
|
27536
27882
|
|
|
27537
27883
|
// ../agent-runtime/dist/autonomous.js
|
|
27538
27884
|
var MAX_TOOL_OUTPUT_CHARS = 4e3;
|
|
27539
|
-
var MAX_PARSE_RETRIES =
|
|
27885
|
+
var MAX_PARSE_RETRIES = 1;
|
|
27540
27886
|
var DEFAULT_MAX_REPAIR_ATTEMPTS = 2;
|
|
27541
27887
|
var CONTEXT_FILE_READ_LIMIT = 3;
|
|
27542
27888
|
var SESSION_NOTE_LIMIT = 4;
|
|
@@ -27552,6 +27898,14 @@ var BOOTSTRAP_CONTEXT_FILES = [
|
|
|
27552
27898
|
"README.md",
|
|
27553
27899
|
"package.json"
|
|
27554
27900
|
];
|
|
27901
|
+
async function fileExists(cwd, filePath) {
|
|
27902
|
+
try {
|
|
27903
|
+
await (0, import_promises12.access)(import_node_path12.default.join(cwd, filePath));
|
|
27904
|
+
return true;
|
|
27905
|
+
} catch {
|
|
27906
|
+
return false;
|
|
27907
|
+
}
|
|
27908
|
+
}
|
|
27555
27909
|
function createAgentActionSchema(allowedTools) {
|
|
27556
27910
|
return external_exports.discriminatedUnion("type", [
|
|
27557
27911
|
external_exports.object({
|
|
@@ -27718,6 +28072,9 @@ function buildSystemPrompt(agent, task, request, allowedTools, plan, extraInstru
|
|
|
27718
28072
|
return [
|
|
27719
28073
|
`You are Kimbho's ${agent.role}.`,
|
|
27720
28074
|
`Purpose: ${agent.purpose}`,
|
|
28075
|
+
...task.subagentLabel ? [
|
|
28076
|
+
`Delegated worker: ${task.subagentLabel}`
|
|
28077
|
+
] : [],
|
|
27721
28078
|
`Goal: ${request.goal}`,
|
|
27722
28079
|
`Current task: ${task.id} - ${task.title}`,
|
|
27723
28080
|
`Task description: ${task.description}`,
|
|
@@ -27751,6 +28108,10 @@ function buildSystemPrompt(agent, task, request, allowedTools, plan, extraInstru
|
|
|
27751
28108
|
`- After changing code, run verification with tests.run or shell.exec when appropriate.`,
|
|
27752
28109
|
`- Do not claim success unless the task acceptance criteria are satisfied.`,
|
|
27753
28110
|
`- If the task is underspecified, make a pragmatic implementation choice and continue.`,
|
|
28111
|
+
...task.subagentInstructions ? [
|
|
28112
|
+
`Delegation instructions:`,
|
|
28113
|
+
task.subagentInstructions
|
|
28114
|
+
] : [],
|
|
27754
28115
|
...extraInstructions ? [
|
|
27755
28116
|
`Custom instructions:`,
|
|
27756
28117
|
extraInstructions
|
|
@@ -28165,6 +28526,7 @@ var AutonomousTaskExecutor = class {
|
|
|
28165
28526
|
signal: options.signal,
|
|
28166
28527
|
approvalMode: this.config.approvalMode,
|
|
28167
28528
|
sandboxMode: this.config.sandboxMode,
|
|
28529
|
+
trustedDirectories: this.config.trustedDirectories,
|
|
28168
28530
|
agentRole: task.agentRole,
|
|
28169
28531
|
sessionId,
|
|
28170
28532
|
taskId: task.id,
|
|
@@ -28174,6 +28536,7 @@ var AutonomousTaskExecutor = class {
|
|
|
28174
28536
|
cwd: request.cwd,
|
|
28175
28537
|
approvalMode: this.config.approvalMode,
|
|
28176
28538
|
sandboxMode: this.config.sandboxMode,
|
|
28539
|
+
trustedDirectories: this.config.trustedDirectories,
|
|
28177
28540
|
agentRole: task.agentRole,
|
|
28178
28541
|
sessionId,
|
|
28179
28542
|
taskId: task.id,
|
|
@@ -28244,7 +28607,13 @@ var AutonomousTaskExecutor = class {
|
|
|
28244
28607
|
...BOOTSTRAP_CONTEXT_FILES,
|
|
28245
28608
|
...filesToRead
|
|
28246
28609
|
] : BOOTSTRAP_CONTEXT_FILES)).slice(0, CONTEXT_FILE_READ_LIMIT + 4);
|
|
28610
|
+
const existingContextFiles = [];
|
|
28247
28611
|
for (const filePath of fallbackFiles) {
|
|
28612
|
+
if (await fileExists(request.cwd, filePath)) {
|
|
28613
|
+
existingContextFiles.push(filePath);
|
|
28614
|
+
}
|
|
28615
|
+
}
|
|
28616
|
+
for (const filePath of existingContextFiles) {
|
|
28248
28617
|
const readResult = await runPreflightTool("file.read", {
|
|
28249
28618
|
path: filePath
|
|
28250
28619
|
}, "Load an existing repo-defining file before the first model action.");
|
|
@@ -28256,12 +28625,23 @@ var AutonomousTaskExecutor = class {
|
|
|
28256
28625
|
const contextRootCwd = options.contextRootCwd ?? request.cwd;
|
|
28257
28626
|
const savedSession = await loadLatestSession(contextRootCwd).catch(() => null);
|
|
28258
28627
|
const savedSessionSummary = summarizeSavedSessionContext(sessionId, savedSession);
|
|
28628
|
+
const recursiveMemory = await loadMarkdownMemoryContext(request.cwd, {
|
|
28629
|
+
maxFiles: 10,
|
|
28630
|
+
maxCharsPerFile: 4e3
|
|
28631
|
+
});
|
|
28259
28632
|
const userMemory = await readMarkdownIfExists(resolveUserMemoryPath());
|
|
28260
28633
|
const projectMemory = await readMarkdownIfExists(resolveProjectMemoryPath(request.cwd));
|
|
28261
28634
|
const customAgentMemory = customOverlay ? await readMarkdownIfExists(import_node_path12.default.join(resolveAgentMemoryDir(request.cwd), `${customOverlay.definition.id}.md`)) : null;
|
|
28262
28635
|
if (savedSessionSummary.length > 0) {
|
|
28263
28636
|
contextMessages.unshift(savedSessionSummary.join("\n"));
|
|
28264
28637
|
}
|
|
28638
|
+
if (recursiveMemory.length > 0) {
|
|
28639
|
+
contextMessages.unshift([
|
|
28640
|
+
"Recursive markdown memory context:",
|
|
28641
|
+
...recursiveMemory.map((record2) => `File: ${record2.filePath}
|
|
28642
|
+
${truncateForModel(record2.content)}`)
|
|
28643
|
+
].join("\n\n"));
|
|
28644
|
+
}
|
|
28265
28645
|
if (userMemory) {
|
|
28266
28646
|
contextMessages.unshift(`Global user memory:
|
|
28267
28647
|
${truncateForModel(userMemory)}`);
|
|
@@ -28306,6 +28686,7 @@ ${truncateForModel(customAgentMemory)}`);
|
|
|
28306
28686
|
signal: options.signal,
|
|
28307
28687
|
approvalMode: this.config.approvalMode,
|
|
28308
28688
|
sandboxMode: this.config.sandboxMode,
|
|
28689
|
+
trustedDirectories: this.config.trustedDirectories,
|
|
28309
28690
|
agentRole: task.agentRole,
|
|
28310
28691
|
operatorApproved,
|
|
28311
28692
|
sessionId,
|
|
@@ -28318,6 +28699,7 @@ ${truncateForModel(customAgentMemory)}`);
|
|
|
28318
28699
|
cwd: request.cwd,
|
|
28319
28700
|
approvalMode: this.config.approvalMode,
|
|
28320
28701
|
sandboxMode: this.config.sandboxMode,
|
|
28702
|
+
trustedDirectories: this.config.trustedDirectories,
|
|
28321
28703
|
agentRole: task.agentRole,
|
|
28322
28704
|
operatorApproved,
|
|
28323
28705
|
sessionId,
|
|
@@ -28516,14 +28898,16 @@ ${truncateForModel(customAgentMemory)}`);
|
|
|
28516
28898
|
let responseText = "";
|
|
28517
28899
|
let parsedAction = null;
|
|
28518
28900
|
for (let attempt = 0; attempt <= MAX_PARSE_RETRIES; attempt += 1) {
|
|
28519
|
-
|
|
28520
|
-
|
|
28521
|
-
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
|
|
28525
|
-
|
|
28526
|
-
|
|
28901
|
+
if (attempt === 0) {
|
|
28902
|
+
await emitProgress({
|
|
28903
|
+
type: "task-note",
|
|
28904
|
+
sessionId,
|
|
28905
|
+
taskId: task.id,
|
|
28906
|
+
agentRole: task.agentRole,
|
|
28907
|
+
step,
|
|
28908
|
+
message: "Thinking through the next safe action."
|
|
28909
|
+
});
|
|
28910
|
+
}
|
|
28527
28911
|
let response;
|
|
28528
28912
|
try {
|
|
28529
28913
|
response = await brain.client.generateText({
|
|
@@ -28591,20 +28975,20 @@ ${truncateForModel(customAgentMemory)}`);
|
|
|
28591
28975
|
response: response.text,
|
|
28592
28976
|
runtimeNote: `${parseSummary} ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
28593
28977
|
});
|
|
28978
|
+
const inferredFallbackAction = inferFallbackActionFromResponse(response.text, task, request, allowedTools);
|
|
28979
|
+
if (inferredFallbackAction) {
|
|
28980
|
+
parsedAction = inferredFallbackAction;
|
|
28981
|
+
await emitProgress({
|
|
28982
|
+
type: "task-note",
|
|
28983
|
+
sessionId,
|
|
28984
|
+
taskId: task.id,
|
|
28985
|
+
agentRole: task.agentRole,
|
|
28986
|
+
step,
|
|
28987
|
+
message: `Interpreted the model draft as ${inferredFallbackAction.tool}.`
|
|
28988
|
+
});
|
|
28989
|
+
break;
|
|
28990
|
+
}
|
|
28594
28991
|
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
28992
|
const transcriptPath2 = await writeTranscriptArtifact(request.cwd, sessionId, task.id, transcript);
|
|
28609
28993
|
artifacts.add(transcriptPath2);
|
|
28610
28994
|
await emitProgress({
|
|
@@ -28629,7 +29013,7 @@ ${truncateForModel(customAgentMemory)}`);
|
|
|
28629
29013
|
taskId: task.id,
|
|
28630
29014
|
agentRole: task.agentRole,
|
|
28631
29015
|
step,
|
|
28632
|
-
message:
|
|
29016
|
+
message: "Normalizing the model draft into one safe machine-readable action."
|
|
28633
29017
|
});
|
|
28634
29018
|
messages.push({
|
|
28635
29019
|
role: "assistant",
|
|
@@ -28795,6 +29179,47 @@ function normalizeGoal(goal) {
|
|
|
28795
29179
|
}
|
|
28796
29180
|
function looksLikeStaticSiteGoal(goal) {
|
|
28797
29181
|
const lower = goal.toLowerCase();
|
|
29182
|
+
const backendSignals = [
|
|
29183
|
+
"api",
|
|
29184
|
+
"database",
|
|
29185
|
+
"backend",
|
|
29186
|
+
"server",
|
|
29187
|
+
"migration",
|
|
29188
|
+
"schema",
|
|
29189
|
+
"prisma",
|
|
29190
|
+
"postgres",
|
|
29191
|
+
"sqlite",
|
|
29192
|
+
"auth",
|
|
29193
|
+
"billing",
|
|
29194
|
+
"webhook"
|
|
29195
|
+
];
|
|
29196
|
+
const pageSignals = [
|
|
29197
|
+
"page",
|
|
29198
|
+
"landing",
|
|
29199
|
+
"lnding",
|
|
29200
|
+
"homepage",
|
|
29201
|
+
"home page",
|
|
29202
|
+
"site",
|
|
29203
|
+
"website",
|
|
29204
|
+
"ui",
|
|
29205
|
+
"hero",
|
|
29206
|
+
"layout",
|
|
29207
|
+
"screen"
|
|
29208
|
+
];
|
|
29209
|
+
const polishSignals = [
|
|
29210
|
+
"animate",
|
|
29211
|
+
"animated",
|
|
29212
|
+
"animation",
|
|
29213
|
+
"motion",
|
|
29214
|
+
"rich",
|
|
29215
|
+
"richer",
|
|
29216
|
+
"polish",
|
|
29217
|
+
"styled",
|
|
29218
|
+
"styling",
|
|
29219
|
+
"visual",
|
|
29220
|
+
"redesign",
|
|
29221
|
+
"revamp"
|
|
29222
|
+
];
|
|
28798
29223
|
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
29224
|
return true;
|
|
28800
29225
|
}
|
|
@@ -28804,6 +29229,12 @@ function looksLikeStaticSiteGoal(goal) {
|
|
|
28804
29229
|
if (lower.includes("restaurant") && (lower.includes("page") || lower.includes("site") || lower.includes("website") || lower.includes("landing") || lower.includes("lnding"))) {
|
|
28805
29230
|
return true;
|
|
28806
29231
|
}
|
|
29232
|
+
if (pageSignals.some((signal) => lower.includes(signal)) && polishSignals.some((signal) => lower.includes(signal)) && !backendSignals.some((signal) => lower.includes(signal))) {
|
|
29233
|
+
return true;
|
|
29234
|
+
}
|
|
29235
|
+
if (lower.includes("blog") && (lower.includes("make") || lower.includes("improve") || lower.includes("enhance") || lower.includes("redesign")) && !backendSignals.some((signal) => lower.includes(signal))) {
|
|
29236
|
+
return true;
|
|
29237
|
+
}
|
|
28807
29238
|
return false;
|
|
28808
29239
|
}
|
|
28809
29240
|
function inferProjectShape(goal) {
|
|
@@ -29577,7 +30008,7 @@ function terminalTaskIds(tasks) {
|
|
|
29577
30008
|
const terminalIds = tasks.filter((task) => !referencedIds.has(task.id)).map((task) => task.id);
|
|
29578
30009
|
return terminalIds.length > 0 ? terminalIds : tasks.map((task) => task.id);
|
|
29579
30010
|
}
|
|
29580
|
-
function
|
|
30011
|
+
function integratePlanTaskExpansion(plan, taskId, expandedTasks, note) {
|
|
29581
30012
|
const terminalIds = terminalTaskIds(expandedTasks);
|
|
29582
30013
|
const milestones = plan.milestones.map((milestone) => ({
|
|
29583
30014
|
...milestone,
|
|
@@ -29687,7 +30118,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
|
|
|
29687
30118
|
const normalizedTasks = normalizeExpandedTasks(plan, task, payload, "model");
|
|
29688
30119
|
const note = `Planner expanded ${task.id} into ${normalizedTasks.length} subtasks via ${invocation.modelLabel ?? "planner model"}: ${normalizedTasks.map((candidate) => candidate.id).join(", ")}.`;
|
|
29689
30120
|
return {
|
|
29690
|
-
plan:
|
|
30121
|
+
plan: integratePlanTaskExpansion(plan, task.id, normalizedTasks, note),
|
|
29691
30122
|
source: "model",
|
|
29692
30123
|
expandedTaskId: task.id,
|
|
29693
30124
|
createdTaskIds: normalizedTasks.map((candidate) => candidate.id),
|
|
@@ -29716,7 +30147,7 @@ async function generateExpandedTasks(request, plan, task, invocation, repoSummar
|
|
|
29716
30147
|
}
|
|
29717
30148
|
const note = `Planner fell back to deterministic subtask expansion for ${task.id}: ${fallbackTasks.map((candidate) => candidate.id).join(", ")}.`;
|
|
29718
30149
|
return {
|
|
29719
|
-
plan:
|
|
30150
|
+
plan: integratePlanTaskExpansion(plan, task.id, fallbackTasks, note),
|
|
29720
30151
|
source: "fallback",
|
|
29721
30152
|
expandedTaskId: task.id,
|
|
29722
30153
|
createdTaskIds: fallbackTasks.map((candidate) => candidate.id),
|
|
@@ -29761,7 +30192,7 @@ function expandPlanTaskFallback(plan, taskId) {
|
|
|
29761
30192
|
}
|
|
29762
30193
|
const note = `Planner deterministically expanded ${task.id} into ${fallbackTasks.length} subtasks: ${fallbackTasks.map((candidate) => candidate.id).join(", ")}.`;
|
|
29763
30194
|
return {
|
|
29764
|
-
plan:
|
|
30195
|
+
plan: integratePlanTaskExpansion(plan, task.id, fallbackTasks, note),
|
|
29765
30196
|
source: "fallback",
|
|
29766
30197
|
expandedTaskId: task.id,
|
|
29767
30198
|
createdTaskIds: fallbackTasks.map((candidate) => candidate.id),
|
|
@@ -30695,6 +31126,19 @@ function maybeAppendNote(notes, note) {
|
|
|
30695
31126
|
note
|
|
30696
31127
|
];
|
|
30697
31128
|
}
|
|
31129
|
+
function slugify2(value) {
|
|
31130
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
|
|
31131
|
+
}
|
|
31132
|
+
function createUniqueTaskId2(baseId, existingIds) {
|
|
31133
|
+
let candidate = baseId;
|
|
31134
|
+
let index = 2;
|
|
31135
|
+
while (existingIds.has(candidate)) {
|
|
31136
|
+
candidate = `${baseId}-${index}`;
|
|
31137
|
+
index += 1;
|
|
31138
|
+
}
|
|
31139
|
+
existingIds.add(candidate);
|
|
31140
|
+
return candidate;
|
|
31141
|
+
}
|
|
30698
31142
|
function mergeUnique(items) {
|
|
30699
31143
|
return Array.from(new Set(items.filter((item) => item.trim().length > 0)));
|
|
30700
31144
|
}
|
|
@@ -30707,6 +31151,280 @@ function isStaticSiteTask(task, request) {
|
|
|
30707
31151
|
}
|
|
30708
31152
|
return task.filesLikelyTouched.some((filePath) => filePath === "src/app/page.tsx" || filePath === "src/app/layout.tsx" || filePath === "src/pages/index.tsx" || filePath === "index.html" || filePath === "styles.css");
|
|
30709
31153
|
}
|
|
31154
|
+
function shouldDelegateTask(task) {
|
|
31155
|
+
return (task.swarmDepth ?? 0) === 0 && task.type === "implementation" && ![
|
|
31156
|
+
"repo-analyst",
|
|
31157
|
+
"planner",
|
|
31158
|
+
"reviewer",
|
|
31159
|
+
"integrator",
|
|
31160
|
+
"test-debugger",
|
|
31161
|
+
"session-orchestrator",
|
|
31162
|
+
"execution-manager"
|
|
31163
|
+
].includes(task.agentRole);
|
|
31164
|
+
}
|
|
31165
|
+
function terminalExpansionTaskIds(tasks) {
|
|
31166
|
+
const referencedIds = new Set(tasks.flatMap((task) => task.dependsOn));
|
|
31167
|
+
const terminalIds = tasks.filter((task) => !referencedIds.has(task.id)).map((task) => task.id);
|
|
31168
|
+
return terminalIds.length > 0 ? terminalIds : tasks.map((task) => task.id);
|
|
31169
|
+
}
|
|
31170
|
+
function createDefaultDelegationPlan(task, request) {
|
|
31171
|
+
if (!shouldDelegateTask(task)) {
|
|
31172
|
+
return null;
|
|
31173
|
+
}
|
|
31174
|
+
if (task.agentRole === "frontend-specialist" || isStaticSiteTask(task, request)) {
|
|
31175
|
+
return {
|
|
31176
|
+
label: "auto-frontend-swarm",
|
|
31177
|
+
strategy: "parallel",
|
|
31178
|
+
members: [
|
|
31179
|
+
{
|
|
31180
|
+
id: "content-architect",
|
|
31181
|
+
label: "content-architect",
|
|
31182
|
+
agentRole: "frontend-specialist",
|
|
31183
|
+
description: "Own information architecture, page composition, navigation, copy structure, and section flow.",
|
|
31184
|
+
instructions: "Prioritize semantic layout, page structure, routes, content hierarchy, and component composition. Leave purely visual polish to the visual worker unless structure blocks it.",
|
|
31185
|
+
acceptanceCriteria: [
|
|
31186
|
+
"The page structure, sections, and navigation flow are clear and coherent.",
|
|
31187
|
+
"Content hierarchy reflects the requested product, brand, or venue."
|
|
31188
|
+
],
|
|
31189
|
+
outputs: [
|
|
31190
|
+
"Structured page/content update"
|
|
31191
|
+
]
|
|
31192
|
+
},
|
|
31193
|
+
{
|
|
31194
|
+
id: "visual-polisher",
|
|
31195
|
+
label: "visual-polisher",
|
|
31196
|
+
agentRole: "frontend-specialist",
|
|
31197
|
+
description: "Own styling, gradients, motion, responsiveness, spacing, and visual finishing.",
|
|
31198
|
+
instructions: "Focus on visual quality, gradients, motion, responsiveness, spacing, and interaction polish after inspecting the current structure. Keep edits aligned with the existing design language when present.",
|
|
31199
|
+
acceptanceCriteria: [
|
|
31200
|
+
"Visual styling and responsiveness are meaningfully improved.",
|
|
31201
|
+
"Motion and polish remain consistent with the requested UI direction."
|
|
31202
|
+
],
|
|
31203
|
+
outputs: [
|
|
31204
|
+
"Visual polish update"
|
|
31205
|
+
]
|
|
31206
|
+
}
|
|
31207
|
+
]
|
|
31208
|
+
};
|
|
31209
|
+
}
|
|
31210
|
+
if (task.agentRole === "backend-specialist") {
|
|
31211
|
+
return {
|
|
31212
|
+
label: "auto-backend-swarm",
|
|
31213
|
+
strategy: "sequential",
|
|
31214
|
+
members: [
|
|
31215
|
+
{
|
|
31216
|
+
id: "contract-architect",
|
|
31217
|
+
label: "contract-architect",
|
|
31218
|
+
agentRole: "backend-specialist",
|
|
31219
|
+
description: "Define the API/service surface, request-response shapes, and failure behavior.",
|
|
31220
|
+
instructions: "Focus on service and API contracts, input/output shapes, handlers, and edge cases before broad implementation changes.",
|
|
31221
|
+
acceptanceCriteria: [
|
|
31222
|
+
"Contracts and failure cases are explicit.",
|
|
31223
|
+
"The implementation path is narrowed to concrete handlers or services."
|
|
31224
|
+
],
|
|
31225
|
+
outputs: [
|
|
31226
|
+
"Backend contract design"
|
|
31227
|
+
]
|
|
31228
|
+
},
|
|
31229
|
+
{
|
|
31230
|
+
id: "service-implementer",
|
|
31231
|
+
label: "service-implementer",
|
|
31232
|
+
agentRole: "backend-specialist",
|
|
31233
|
+
description: "Implement handlers, services, and domain logic against the agreed contract.",
|
|
31234
|
+
instructions: "Implement the core backend behavior against the established contract. Prefer targeted file edits and verification over broad rewrites.",
|
|
31235
|
+
acceptanceCriteria: [
|
|
31236
|
+
"Business logic is implemented against the contract.",
|
|
31237
|
+
"The backend behavior satisfies the task acceptance criteria."
|
|
31238
|
+
],
|
|
31239
|
+
outputs: [
|
|
31240
|
+
"Backend implementation"
|
|
31241
|
+
]
|
|
31242
|
+
}
|
|
31243
|
+
]
|
|
31244
|
+
};
|
|
31245
|
+
}
|
|
31246
|
+
if (task.agentRole === "database-specialist") {
|
|
31247
|
+
return {
|
|
31248
|
+
label: "auto-database-swarm",
|
|
31249
|
+
strategy: "sequential",
|
|
31250
|
+
members: [
|
|
31251
|
+
{
|
|
31252
|
+
id: "schema-designer",
|
|
31253
|
+
label: "schema-designer",
|
|
31254
|
+
agentRole: "database-specialist",
|
|
31255
|
+
description: "Design models, fields, relations, constraints, and persistence boundaries.",
|
|
31256
|
+
instructions: "Focus on schema structure, data modeling, constraints, and identifiers. Keep the migration path safe and explicit.",
|
|
31257
|
+
acceptanceCriteria: [
|
|
31258
|
+
"Schema design reflects the requested domain.",
|
|
31259
|
+
"Constraints and data relationships are explicit."
|
|
31260
|
+
],
|
|
31261
|
+
outputs: [
|
|
31262
|
+
"Schema design"
|
|
31263
|
+
]
|
|
31264
|
+
},
|
|
31265
|
+
{
|
|
31266
|
+
id: "migration-planner",
|
|
31267
|
+
label: "migration-planner",
|
|
31268
|
+
agentRole: "database-specialist",
|
|
31269
|
+
description: "Apply or prepare migrations and persistence wiring around the approved schema.",
|
|
31270
|
+
instructions: "Translate the schema design into migrations or persistence updates. Keep destructive changes obvious and verification-friendly.",
|
|
31271
|
+
acceptanceCriteria: [
|
|
31272
|
+
"Persistence updates align with the intended schema.",
|
|
31273
|
+
"Migration risk and verification needs are explicit."
|
|
31274
|
+
],
|
|
31275
|
+
outputs: [
|
|
31276
|
+
"Migration/persistence update"
|
|
31277
|
+
]
|
|
31278
|
+
}
|
|
31279
|
+
]
|
|
31280
|
+
};
|
|
31281
|
+
}
|
|
31282
|
+
if (task.agentRole === "infra-specialist") {
|
|
31283
|
+
return {
|
|
31284
|
+
label: "auto-infra-swarm",
|
|
31285
|
+
strategy: "sequential",
|
|
31286
|
+
members: [
|
|
31287
|
+
{
|
|
31288
|
+
id: "workspace-scaffolder",
|
|
31289
|
+
label: "workspace-scaffolder",
|
|
31290
|
+
agentRole: "infra-specialist",
|
|
31291
|
+
description: "Set up required files, scripts, package wiring, and environment plumbing.",
|
|
31292
|
+
instructions: "Focus on scaffolding, package manifests, scripts, environment wiring, and local runtime setup. Keep the workspace runnable.",
|
|
31293
|
+
acceptanceCriteria: [
|
|
31294
|
+
"Required scaffolding and environment wiring are in place.",
|
|
31295
|
+
"The workspace can proceed into implementation or verification."
|
|
31296
|
+
],
|
|
31297
|
+
outputs: [
|
|
31298
|
+
"Workspace scaffolding"
|
|
31299
|
+
]
|
|
31300
|
+
},
|
|
31301
|
+
{
|
|
31302
|
+
id: "runtime-verifier",
|
|
31303
|
+
label: "runtime-verifier",
|
|
31304
|
+
agentRole: "infra-specialist",
|
|
31305
|
+
description: "Verify build/runtime setup and tighten any configuration or tooling gaps.",
|
|
31306
|
+
instructions: "Focus on runtime, toolchain, script, and build verification after the initial setup work has landed.",
|
|
31307
|
+
acceptanceCriteria: [
|
|
31308
|
+
"The environment or app can be verified locally.",
|
|
31309
|
+
"Tooling and configuration gaps are closed."
|
|
31310
|
+
],
|
|
31311
|
+
outputs: [
|
|
31312
|
+
"Runtime/config verification"
|
|
31313
|
+
]
|
|
31314
|
+
}
|
|
31315
|
+
]
|
|
31316
|
+
};
|
|
31317
|
+
}
|
|
31318
|
+
return null;
|
|
31319
|
+
}
|
|
31320
|
+
function materializeDelegatedTasks(plan, task, label, strategy, members) {
|
|
31321
|
+
const existingIds = new Set(flattenPlanTasks(plan).filter((candidate) => candidate.id !== task.id).map((candidate) => candidate.id));
|
|
31322
|
+
const teamMemberIds = members.map((member) => member.id);
|
|
31323
|
+
const generatedTaskIds = members.map((member, index) => createUniqueTaskId2(`${task.id}-${slugify2(member.id) || `worker-${index + 1}`}`, existingIds));
|
|
31324
|
+
return members.map((member, index) => {
|
|
31325
|
+
const taskId = generatedTaskIds[index];
|
|
31326
|
+
const dependsOn = strategy === "parallel" ? [
|
|
31327
|
+
...task.dependsOn
|
|
31328
|
+
] : index === 0 ? [
|
|
31329
|
+
...task.dependsOn
|
|
31330
|
+
] : [
|
|
31331
|
+
generatedTaskIds[index - 1]
|
|
31332
|
+
];
|
|
31333
|
+
return {
|
|
31334
|
+
...task,
|
|
31335
|
+
id: taskId,
|
|
31336
|
+
title: `${task.title} (${member.label})`,
|
|
31337
|
+
description: [
|
|
31338
|
+
task.description,
|
|
31339
|
+
`Delegated worker: ${member.label}.`,
|
|
31340
|
+
member.description
|
|
31341
|
+
].join("\n\n"),
|
|
31342
|
+
agentRole: member.agentRole,
|
|
31343
|
+
dependsOn,
|
|
31344
|
+
acceptanceCriteria: member.acceptanceCriteria?.length ? member.acceptanceCriteria : index === members.length - 1 ? task.acceptanceCriteria : [
|
|
31345
|
+
`Advance ${task.title.toLowerCase()} from the ${member.label} perspective.`,
|
|
31346
|
+
"Leave concrete implementation evidence for the next worker."
|
|
31347
|
+
],
|
|
31348
|
+
outputs: member.outputs?.length ? member.outputs : index === members.length - 1 ? task.outputs : [
|
|
31349
|
+
`Delegated handoff from ${member.id}`
|
|
31350
|
+
],
|
|
31351
|
+
filesLikelyTouched: member.filesLikelyTouched?.length ? member.filesLikelyTouched : task.filesLikelyTouched,
|
|
31352
|
+
originTaskId: task.id,
|
|
31353
|
+
swarmDepth: (task.swarmDepth ?? 0) + 1,
|
|
31354
|
+
teamId: label,
|
|
31355
|
+
teamMemberIds,
|
|
31356
|
+
subagentLabel: member.label,
|
|
31357
|
+
subagentInstructions: member.instructions
|
|
31358
|
+
};
|
|
31359
|
+
});
|
|
31360
|
+
}
|
|
31361
|
+
function appendReviewAndIntegrationTasks(plan, originalTask, expandedTasks, delegationLabel) {
|
|
31362
|
+
const existingIds = new Set(flattenPlanTasks(plan).filter((candidate) => candidate.id !== originalTask.id).map((candidate) => candidate.id));
|
|
31363
|
+
for (const task of expandedTasks) {
|
|
31364
|
+
existingIds.add(task.id);
|
|
31365
|
+
}
|
|
31366
|
+
const terminalIds = terminalExpansionTaskIds(expandedTasks);
|
|
31367
|
+
const reviewerId = createUniqueTaskId2(`${originalTask.id}-review`, existingIds);
|
|
31368
|
+
const integratorId = createUniqueTaskId2(`${originalTask.id}-integrate`, existingIds);
|
|
31369
|
+
const reviewerTask = {
|
|
31370
|
+
...originalTask,
|
|
31371
|
+
id: reviewerId,
|
|
31372
|
+
title: `Review ${originalTask.title.toLowerCase()}`,
|
|
31373
|
+
description: `Inspect delegated worker output from ${delegationLabel} before integration and call out correctness or verification risk.`,
|
|
31374
|
+
type: "verification",
|
|
31375
|
+
agentRole: "reviewer",
|
|
31376
|
+
dependsOn: terminalIds,
|
|
31377
|
+
acceptanceCriteria: [
|
|
31378
|
+
"Inspect the delegated changes for regressions or missing coverage.",
|
|
31379
|
+
"Confirm whether the worker output is ready for integration."
|
|
31380
|
+
],
|
|
31381
|
+
outputs: [
|
|
31382
|
+
"Review summary"
|
|
31383
|
+
],
|
|
31384
|
+
originTaskId: originalTask.id,
|
|
31385
|
+
swarmDepth: (originalTask.swarmDepth ?? 0) + 1,
|
|
31386
|
+
teamId: delegationLabel,
|
|
31387
|
+
teamMemberIds: [
|
|
31388
|
+
...originalTask.teamMemberIds ?? [],
|
|
31389
|
+
"reviewer"
|
|
31390
|
+
],
|
|
31391
|
+
subagentLabel: "reviewer",
|
|
31392
|
+
subagentInstructions: "Inspect the delegated changes, diff, and verification evidence. Surface concrete risks before integration."
|
|
31393
|
+
};
|
|
31394
|
+
const integratorTask = {
|
|
31395
|
+
...originalTask,
|
|
31396
|
+
id: integratorId,
|
|
31397
|
+
title: `Integrate ${originalTask.title.toLowerCase()}`,
|
|
31398
|
+
description: `Finalize and integrate delegated worker output after reviewer sign-off for ${delegationLabel}.`,
|
|
31399
|
+
type: "integration",
|
|
31400
|
+
agentRole: "integrator",
|
|
31401
|
+
dependsOn: [
|
|
31402
|
+
reviewerId
|
|
31403
|
+
],
|
|
31404
|
+
acceptanceCriteria: originalTask.acceptanceCriteria,
|
|
31405
|
+
outputs: mergeUnique([
|
|
31406
|
+
...originalTask.outputs,
|
|
31407
|
+
"Integrated change set"
|
|
31408
|
+
]),
|
|
31409
|
+
originTaskId: originalTask.id,
|
|
31410
|
+
swarmDepth: (originalTask.swarmDepth ?? 0) + 1,
|
|
31411
|
+
teamId: delegationLabel,
|
|
31412
|
+
teamMemberIds: [
|
|
31413
|
+
...originalTask.teamMemberIds ?? [],
|
|
31414
|
+
"integrator"
|
|
31415
|
+
],
|
|
31416
|
+
subagentLabel: "integrator",
|
|
31417
|
+
subagentInstructions: "Finalize the accepted worker output, preserve repository consistency, and leave the workspace ready for downstream verification."
|
|
31418
|
+
};
|
|
31419
|
+
return {
|
|
31420
|
+
tasks: [
|
|
31421
|
+
...expandedTasks,
|
|
31422
|
+
reviewerTask,
|
|
31423
|
+
integratorTask
|
|
31424
|
+
],
|
|
31425
|
+
noteSuffix: `review tail ${reviewerId} -> integration tail ${integratorId}`
|
|
31426
|
+
};
|
|
31427
|
+
}
|
|
30710
31428
|
function renderToolResultSection(results) {
|
|
30711
31429
|
return results.map((result) => {
|
|
30712
31430
|
const lines = [
|
|
@@ -31764,20 +32482,49 @@ var ExecutionOrchestrator = class {
|
|
|
31764
32482
|
}
|
|
31765
32483
|
async maybeExpandReadyTaskGraph(sessionId, request, plan, events, emitProgress) {
|
|
31766
32484
|
const envelope = this.buildEnvelope(request, plan, sessionId);
|
|
31767
|
-
const candidate = envelope.readyTasks.find((task) => (task
|
|
31768
|
-
"repo-analyst",
|
|
31769
|
-
"planner",
|
|
31770
|
-
"reviewer",
|
|
31771
|
-
"integrator",
|
|
31772
|
-
"test-debugger",
|
|
31773
|
-
"session-orchestrator",
|
|
31774
|
-
"execution-manager"
|
|
31775
|
-
].includes(task.agentRole));
|
|
32485
|
+
const candidate = envelope.readyTasks.find((task) => shouldDelegateTask(task));
|
|
31776
32486
|
if (!candidate) {
|
|
31777
32487
|
return {
|
|
31778
32488
|
plan
|
|
31779
32489
|
};
|
|
31780
32490
|
}
|
|
32491
|
+
const matchedTeam = await selectAgentTeam(request.cwd, candidate, request);
|
|
32492
|
+
if (matchedTeam && matchedTeam.members.length > 1) {
|
|
32493
|
+
const teamTasks = materializeDelegatedTasks(plan, candidate, matchedTeam.team.id, matchedTeam.team.strategy, matchedTeam.members.map((member) => ({
|
|
32494
|
+
id: member.id,
|
|
32495
|
+
label: member.label,
|
|
32496
|
+
agentRole: member.baseRole,
|
|
32497
|
+
description: [
|
|
32498
|
+
`Assigned team member: ${member.label} (${member.id}).`,
|
|
32499
|
+
member.purpose ? `Purpose: ${member.purpose}` : "",
|
|
32500
|
+
matchedTeam.team.description ? `Team context: ${matchedTeam.team.description}` : ""
|
|
32501
|
+
].filter((part) => part.length > 0).join("\n\n"),
|
|
32502
|
+
instructions: member.instructions
|
|
32503
|
+
}))).map((task) => ({
|
|
32504
|
+
...task,
|
|
32505
|
+
customAgentId: task.customAgentId ?? task.subagentLabel ? matchedTeam.members.find((member) => member.label === task.subagentLabel)?.id : void 0,
|
|
32506
|
+
teamId: matchedTeam.team.id,
|
|
32507
|
+
teamMemberIds: matchedTeam.members.map((member) => member.id)
|
|
32508
|
+
}));
|
|
32509
|
+
const augmented = appendReviewAndIntegrationTasks(plan, candidate, teamTasks, matchedTeam.team.label);
|
|
32510
|
+
const note = `Agent team ${matchedTeam.team.label} expanded ${candidate.id} into ${teamTasks.length} worker subtasks with reviewer/integrator follow-through: ${augmented.tasks.map((task) => task.id).join(", ")}.`;
|
|
32511
|
+
return {
|
|
32512
|
+
plan: integratePlanTaskExpansion(plan, candidate.id, augmented.tasks, note),
|
|
32513
|
+
note,
|
|
32514
|
+
expandedTaskId: candidate.id
|
|
32515
|
+
};
|
|
32516
|
+
}
|
|
32517
|
+
const defaultDelegation = createDefaultDelegationPlan(candidate, request);
|
|
32518
|
+
if (defaultDelegation && defaultDelegation.members.length > 1) {
|
|
32519
|
+
const delegatedTasks = materializeDelegatedTasks(plan, candidate, defaultDelegation.label, defaultDelegation.strategy, defaultDelegation.members);
|
|
32520
|
+
const augmented = appendReviewAndIntegrationTasks(plan, candidate, delegatedTasks, defaultDelegation.label);
|
|
32521
|
+
const note = `Execution manager delegated ${candidate.id} into ${delegatedTasks.length} subagents plus reviewer/integrator: ${augmented.tasks.map((task) => task.id).join(", ")}.`;
|
|
32522
|
+
return {
|
|
32523
|
+
plan: integratePlanTaskExpansion(plan, candidate.id, augmented.tasks, note),
|
|
32524
|
+
note,
|
|
32525
|
+
expandedTaskId: candidate.id
|
|
32526
|
+
};
|
|
32527
|
+
}
|
|
31781
32528
|
const repoContext = latestRepoAnalysisContext(events);
|
|
31782
32529
|
const config2 = await loadConfig(request.cwd);
|
|
31783
32530
|
if (!config2) {
|
|
@@ -31835,12 +32582,14 @@ var ExecutionOrchestrator = class {
|
|
|
31835
32582
|
plan
|
|
31836
32583
|
};
|
|
31837
32584
|
}
|
|
32585
|
+
const expandedTasks = flattenPlanTasks(result.plan).filter((task) => result.createdTaskIds.includes(task.id));
|
|
32586
|
+
const augmented = appendReviewAndIntegrationTasks(plan, candidate, expandedTasks, "planner-swarm");
|
|
31838
32587
|
const noteParts = [
|
|
31839
|
-
result.note
|
|
32588
|
+
result.note ? `${result.note} | Added reviewer/integrator pass: ${augmented.noteSuffix}.` : `Added reviewer/integrator pass: ${augmented.noteSuffix}.`,
|
|
31840
32589
|
result.warning && result.source !== "model" ? `expander note: ${result.warning}` : ""
|
|
31841
32590
|
].filter(Boolean);
|
|
31842
32591
|
return {
|
|
31843
|
-
plan:
|
|
32592
|
+
plan: integratePlanTaskExpansion(plan, candidate.id, augmented.tasks, noteParts.join(" | ")),
|
|
31844
32593
|
...noteParts.length > 0 ? {
|
|
31845
32594
|
note: noteParts.join(" | ")
|
|
31846
32595
|
} : {},
|
|
@@ -32395,7 +33144,15 @@ async function generateProjectInitFile(cwd, outputFileName = "kimbho_init.md") {
|
|
|
32395
33144
|
return {
|
|
32396
33145
|
outputPath,
|
|
32397
33146
|
indexArtifactPath: artifactPath,
|
|
32398
|
-
summary: `Generated ${outputFileName} with indexed project context for ${import_node_path15.default.basename(cwd)}
|
|
33147
|
+
summary: `Generated ${outputFileName} with indexed project context for ${import_node_path15.default.basename(cwd)}.`,
|
|
33148
|
+
purpose,
|
|
33149
|
+
frameworks,
|
|
33150
|
+
entrypoints,
|
|
33151
|
+
verificationCommands,
|
|
33152
|
+
indexedFiles: index.summary.totalFiles,
|
|
33153
|
+
sourceFiles: index.summary.sourceFiles,
|
|
33154
|
+
routes: index.summary.routes,
|
|
33155
|
+
schemas: index.summary.schemas
|
|
32399
33156
|
};
|
|
32400
33157
|
}
|
|
32401
33158
|
|
|
@@ -32452,7 +33209,15 @@ async function refreshMemoryFiles(cwd) {
|
|
|
32452
33209
|
initFilePath: init.outputPath,
|
|
32453
33210
|
projectMemoryPath,
|
|
32454
33211
|
userMemoryPath,
|
|
32455
|
-
summary: `Refreshed project bootstrap and memory files for ${import_node_path16.default.basename(cwd)}
|
|
33212
|
+
summary: `Refreshed project bootstrap and memory files for ${import_node_path16.default.basename(cwd)}.`,
|
|
33213
|
+
purpose: init.purpose,
|
|
33214
|
+
frameworks: init.frameworks,
|
|
33215
|
+
entrypoints: init.entrypoints,
|
|
33216
|
+
verificationCommands: init.verificationCommands,
|
|
33217
|
+
indexedFiles: init.indexedFiles,
|
|
33218
|
+
sourceFiles: init.sourceFiles,
|
|
33219
|
+
routes: init.routes,
|
|
33220
|
+
schemas: init.schemas
|
|
32456
33221
|
};
|
|
32457
33222
|
}
|
|
32458
33223
|
async function appendMemoryNote(cwd, scope, text, agentId) {
|
|
@@ -32486,21 +33251,24 @@ async function listMemoryPaths(cwd) {
|
|
|
32486
33251
|
function createConfigCommand() {
|
|
32487
33252
|
return new Command("config").description("Show the current config, memory, and MCP file locations.").action(async () => {
|
|
32488
33253
|
const cwd = import_node_process5.default.cwd();
|
|
32489
|
-
const
|
|
33254
|
+
const layers = await loadConfigLayers(cwd);
|
|
32490
33255
|
const memory = await listMemoryPaths(cwd);
|
|
32491
|
-
console.log(`config: ${
|
|
33256
|
+
console.log(`config.user: ${resolveGlobalConfigPath()}`);
|
|
33257
|
+
console.log(`config.project: ${resolveConfigPath(cwd)}`);
|
|
32492
33258
|
console.log(`mcp: ${resolveMcpConfigPath(cwd)}`);
|
|
32493
33259
|
console.log(`memory.project: ${memory.projectMemoryPath}`);
|
|
32494
33260
|
console.log(`memory.user: ${memory.userMemoryPath}`);
|
|
32495
33261
|
console.log(`memory.agents: ${memory.agentMemoryDir}`);
|
|
32496
|
-
if (!
|
|
33262
|
+
if (!layers.resolved) {
|
|
32497
33263
|
console.log("No config found.");
|
|
32498
33264
|
return;
|
|
32499
33265
|
}
|
|
32500
|
-
console.log(`
|
|
32501
|
-
console.log(`
|
|
32502
|
-
console.log(`
|
|
32503
|
-
console.log(`
|
|
33266
|
+
console.log(`layers.user: ${layers.user ? "present" : "missing"}`);
|
|
33267
|
+
console.log(`layers.project: ${layers.project ? "present" : "missing"}`);
|
|
33268
|
+
console.log(`providers: ${layers.resolved.providers.length}`);
|
|
33269
|
+
console.log(`approval: ${layers.resolved.approvalMode}`);
|
|
33270
|
+
console.log(`sandbox: ${layers.resolved.sandboxMode}`);
|
|
33271
|
+
console.log(`trusted directories: ${layers.resolved.trustedDirectories.length}`);
|
|
32504
33272
|
});
|
|
32505
33273
|
}
|
|
32506
33274
|
|
|
@@ -32591,6 +33359,22 @@ function createDoctorCommand() {
|
|
|
32591
33359
|
|
|
32592
33360
|
// src/commands/init.ts
|
|
32593
33361
|
var import_node_process7 = __toESM(require("node:process"), 1);
|
|
33362
|
+
function printProjectUnderstanding(result) {
|
|
33363
|
+
console.log(result.summary);
|
|
33364
|
+
console.log("Now I have a durable understanding of the current project structure.");
|
|
33365
|
+
console.log(`Purpose: ${result.purpose}`);
|
|
33366
|
+
console.log(`Indexed: ${result.indexedFiles} files (${result.sourceFiles} source files), ${result.routes} routes, ${result.schemas} schemas`);
|
|
33367
|
+
console.log(`Frameworks: ${result.frameworks.length > 0 ? result.frameworks.join(", ") : "unknown"}`);
|
|
33368
|
+
if (result.entrypoints.length > 0) {
|
|
33369
|
+
console.log(`Entrypoints: ${result.entrypoints.slice(0, 5).join(", ")}`);
|
|
33370
|
+
}
|
|
33371
|
+
if (result.verificationCommands.length > 0) {
|
|
33372
|
+
console.log(`Verification: ${result.verificationCommands.join(" -> ")}`);
|
|
33373
|
+
}
|
|
33374
|
+
console.log(`Wrote ${result.initFilePath}`);
|
|
33375
|
+
console.log(`Wrote ${result.projectMemoryPath}`);
|
|
33376
|
+
console.log(`Wrote ${result.userMemoryPath}`);
|
|
33377
|
+
}
|
|
32594
33378
|
function createInitCommand() {
|
|
32595
33379
|
return new Command("init").description("Create or refresh local Kimbho config and project bootstrap memory.").option("--template <template>", "Provider template to use for the initial provider").option("--provider-id <id>", "Identifier for the initial provider").option("--driver <driver>", "Provider driver to use when no template is supplied").option("--label <label>", "Human-readable provider label").option("--model <model>", "Default model to use for planner/coder/reviewer brains").option("--fast-model <model>", "Model to use for the fast utility brain").option("--api-key-env <env>", "Environment variable that stores the provider API key").option("--base-url <url>", "Base URL or endpoint for the provider driver").option("--module-path <path>", "Module path for a custom provider driver").option("--config-only", "Only write .kimbho/config.json and skip project memory generation", false).option("--memory-only", "Only generate kimbho_init.md and keep the existing config untouched", false).option("--memory-file <file>", "Project memory file to write", "kimbho_init.md").option("--force", "Overwrite an existing config file", false).action(async (options) => {
|
|
32596
33380
|
const existing = await loadConfig(import_node_process7.default.cwd());
|
|
@@ -32656,10 +33440,7 @@ function createInitCommand() {
|
|
|
32656
33440
|
}
|
|
32657
33441
|
if (!options.configOnly) {
|
|
32658
33442
|
const result = await refreshMemoryFiles(import_node_process7.default.cwd());
|
|
32659
|
-
|
|
32660
|
-
console.log(`Wrote ${result.initFilePath}`);
|
|
32661
|
-
console.log(`Wrote ${result.projectMemoryPath}`);
|
|
32662
|
-
console.log(`Wrote ${result.userMemoryPath}`);
|
|
33443
|
+
printProjectUnderstanding(result);
|
|
32663
33444
|
}
|
|
32664
33445
|
});
|
|
32665
33446
|
}
|
|
@@ -32734,14 +33515,90 @@ async function renderMcpInventory(cwd, name) {
|
|
|
32734
33515
|
}
|
|
32735
33516
|
return filtered.flatMap((server) => [
|
|
32736
33517
|
`${server.name}`,
|
|
33518
|
+
...server.warning ? [
|
|
33519
|
+
` warning: ${server.warning}`
|
|
33520
|
+
] : [],
|
|
32737
33521
|
` tools: ${server.tools.length}`,
|
|
32738
33522
|
...server.tools.slice(0, 20).map((tool) => ` - ${tool.toolName} (${tool.permission})`),
|
|
32739
33523
|
` prompts: ${server.prompts.length}`,
|
|
32740
|
-
...server.prompts.slice(0, 10).
|
|
33524
|
+
...server.prompts.slice(0, 10).flatMap((prompt) => [
|
|
33525
|
+
` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
|
|
33526
|
+
` command: /mcp__${server.name}__${prompt.name}`
|
|
33527
|
+
]),
|
|
32741
33528
|
` resources: ${server.resources.length}`,
|
|
32742
|
-
...server.resources.slice(0, 10).
|
|
33529
|
+
...server.resources.slice(0, 10).flatMap((resource) => [
|
|
33530
|
+
` - ${resource.name} -> ${resource.uri}`,
|
|
33531
|
+
` attach: @${server.name}:${resource.uri}`
|
|
33532
|
+
]),
|
|
33533
|
+
` resource templates: ${server.resourceTemplates.length}`,
|
|
33534
|
+
...server.resourceTemplates.slice(0, 10).map((resourceTemplate) => ` - ${resourceTemplate.name} -> ${resourceTemplate.uriTemplate}`)
|
|
33535
|
+
]);
|
|
33536
|
+
}
|
|
33537
|
+
async function renderMcpPromptList(cwd, name) {
|
|
33538
|
+
const inventory = await loadMcpServerInventory(cwd);
|
|
33539
|
+
const filtered = name ? inventory.filter((server) => server.name === name) : inventory;
|
|
33540
|
+
if (filtered.length === 0) {
|
|
33541
|
+
return [
|
|
33542
|
+
name ? `No MCP prompt inventory found for ${name}.` : "No MCP prompt inventory available."
|
|
33543
|
+
];
|
|
33544
|
+
}
|
|
33545
|
+
return filtered.flatMap((server) => [
|
|
33546
|
+
`${server.name}`,
|
|
33547
|
+
...server.warning ? [
|
|
33548
|
+
` warning: ${server.warning}`
|
|
33549
|
+
] : [],
|
|
33550
|
+
...server.prompts.length > 0 ? server.prompts.flatMap((prompt) => [
|
|
33551
|
+
` - ${prompt.name}${prompt.arguments.length > 0 ? ` (${prompt.arguments.map((argument) => `${argument.name}${argument.required ? "*" : ""}`).join(", ")})` : ""}`,
|
|
33552
|
+
` command: /mcp__${server.name}__${prompt.name}`
|
|
33553
|
+
]) : [
|
|
33554
|
+
" - no prompts"
|
|
33555
|
+
]
|
|
32743
33556
|
]);
|
|
32744
33557
|
}
|
|
33558
|
+
async function renderMcpResourceList(cwd, name) {
|
|
33559
|
+
const inventory = await loadMcpServerInventory(cwd);
|
|
33560
|
+
const filtered = name ? inventory.filter((server) => server.name === name) : inventory;
|
|
33561
|
+
if (filtered.length === 0) {
|
|
33562
|
+
return [
|
|
33563
|
+
name ? `No MCP resource inventory found for ${name}.` : "No MCP resource inventory available."
|
|
33564
|
+
];
|
|
33565
|
+
}
|
|
33566
|
+
return filtered.flatMap((server) => [
|
|
33567
|
+
`${server.name}`,
|
|
33568
|
+
...server.warning ? [
|
|
33569
|
+
` warning: ${server.warning}`
|
|
33570
|
+
] : [],
|
|
33571
|
+
...server.resources.length > 0 ? server.resources.flatMap((resource) => [
|
|
33572
|
+
` - ${resource.name} -> ${resource.uri}`,
|
|
33573
|
+
` attach: @${server.name}:${resource.uri}`
|
|
33574
|
+
]) : [
|
|
33575
|
+
" - no resources"
|
|
33576
|
+
],
|
|
33577
|
+
...server.resourceTemplates.length > 0 ? [
|
|
33578
|
+
" templates:",
|
|
33579
|
+
...server.resourceTemplates.map((resourceTemplate) => ` - ${resourceTemplate.name} -> ${resourceTemplate.uriTemplate}`)
|
|
33580
|
+
] : []
|
|
33581
|
+
]);
|
|
33582
|
+
}
|
|
33583
|
+
async function renderMcpPromptInvocation(cwd, serverName, promptName, args) {
|
|
33584
|
+
const result = await invokeMcpPrompt(cwd, serverName, promptName, args);
|
|
33585
|
+
return [
|
|
33586
|
+
`${result.serverName}/${result.promptName}`,
|
|
33587
|
+
...result.description ? [
|
|
33588
|
+
`description: ${result.description}`
|
|
33589
|
+
] : [],
|
|
33590
|
+
"",
|
|
33591
|
+
result.content
|
|
33592
|
+
];
|
|
33593
|
+
}
|
|
33594
|
+
async function renderMcpResourceRead(cwd, serverName, uri) {
|
|
33595
|
+
const result = await readMcpResource(cwd, serverName, uri);
|
|
33596
|
+
return [
|
|
33597
|
+
`${result.serverName} ${result.uri}`,
|
|
33598
|
+
"",
|
|
33599
|
+
result.content
|
|
33600
|
+
];
|
|
33601
|
+
}
|
|
32745
33602
|
|
|
32746
33603
|
// src/commands/mcp.ts
|
|
32747
33604
|
function collectValues(value, previous = []) {
|
|
@@ -32791,6 +33648,34 @@ function createMcpCommand() {
|
|
|
32791
33648
|
console.log(line);
|
|
32792
33649
|
}
|
|
32793
33650
|
});
|
|
33651
|
+
command.command("discover").description("Show MCP tools, prompts-as-commands, and resource attachment syntax.").argument("[name]", "Optional server name").action(async (name) => {
|
|
33652
|
+
for (const line of await renderMcpInventory(import_node_process8.default.cwd(), name)) {
|
|
33653
|
+
console.log(line);
|
|
33654
|
+
}
|
|
33655
|
+
});
|
|
33656
|
+
command.command("prompts").description("List MCP prompts.").argument("[name]", "Optional server name").action(async (name) => {
|
|
33657
|
+
for (const line of await renderMcpPromptList(import_node_process8.default.cwd(), name)) {
|
|
33658
|
+
console.log(line);
|
|
33659
|
+
}
|
|
33660
|
+
});
|
|
33661
|
+
command.command("resources").description("List MCP resources and resource templates.").argument("[name]", "Optional server name").action(async (name) => {
|
|
33662
|
+
for (const line of await renderMcpResourceList(import_node_process8.default.cwd(), name)) {
|
|
33663
|
+
console.log(line);
|
|
33664
|
+
}
|
|
33665
|
+
});
|
|
33666
|
+
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 = []) => {
|
|
33667
|
+
const args = Object.fromEntries(
|
|
33668
|
+
rawArgs.map((entry) => entry.split("=", 2)).filter((parts) => parts.length === 2 && parts[0].length > 0)
|
|
33669
|
+
);
|
|
33670
|
+
for (const line of await renderMcpPromptInvocation(import_node_process8.default.cwd(), server, prompt, args)) {
|
|
33671
|
+
console.log(line);
|
|
33672
|
+
}
|
|
33673
|
+
});
|
|
33674
|
+
command.command("read").description("Read one MCP resource by URI.").argument("<server>", "Server name").argument("<uri>", "Resource URI").action(async (server, uri) => {
|
|
33675
|
+
for (const line of await renderMcpResourceRead(import_node_process8.default.cwd(), server, uri)) {
|
|
33676
|
+
console.log(line);
|
|
33677
|
+
}
|
|
33678
|
+
});
|
|
32794
33679
|
return command;
|
|
32795
33680
|
}
|
|
32796
33681
|
|
|
@@ -33252,7 +34137,7 @@ function createModelsCommand() {
|
|
|
33252
34137
|
const exists = models.some((item) => item.id === model) || provider.models.includes(model);
|
|
33253
34138
|
if (!exists) {
|
|
33254
34139
|
throw new Error(
|
|
33255
|
-
`Model "${model}" was not found for provider "${provider.id}". Run \`kimbho
|
|
34140
|
+
`Model "${model}" was not found for provider "${provider.id}". Run \`kimbho model find ${model}\` or re-run with --force.`
|
|
33256
34141
|
);
|
|
33257
34142
|
}
|
|
33258
34143
|
} catch (error2) {
|
|
@@ -33595,27 +34480,49 @@ function createPlanCommand() {
|
|
|
33595
34480
|
|
|
33596
34481
|
// src/commands/permissions.ts
|
|
33597
34482
|
var import_node_process13 = __toESM(require("node:process"), 1);
|
|
34483
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
34484
|
+
function getScope(options) {
|
|
34485
|
+
return options.scope === "user" ? "user" : "project";
|
|
34486
|
+
}
|
|
34487
|
+
async function loadScopedConfig(scope, cwd) {
|
|
34488
|
+
const direct = scope === "user" ? await loadUserConfig() : await loadProjectConfig(cwd);
|
|
34489
|
+
const resolved = await loadConfig(cwd);
|
|
34490
|
+
return {
|
|
34491
|
+
direct,
|
|
34492
|
+
resolved
|
|
34493
|
+
};
|
|
34494
|
+
}
|
|
34495
|
+
async function saveScopedConfig(scope, cwd, config2) {
|
|
34496
|
+
return scope === "user" ? saveUserConfig(config2) : saveConfig(config2, cwd);
|
|
34497
|
+
}
|
|
33598
34498
|
function createPermissionsCommand() {
|
|
33599
34499
|
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
|
|
33602
|
-
|
|
34500
|
+
command.command("status").description("Show permission settings.").option("--scope <scope>", "project or user", "project").action(async (options) => {
|
|
34501
|
+
const scope = getScope(options);
|
|
34502
|
+
const { direct, resolved } = await loadScopedConfig(scope, import_node_process13.default.cwd());
|
|
34503
|
+
console.log(`scope: ${scope}`);
|
|
34504
|
+
console.log(`direct config: ${direct ? "present" : "missing"}`);
|
|
34505
|
+
if (!resolved) {
|
|
33603
34506
|
throw new Error("No config found. Run `kimbho init` first.");
|
|
33604
34507
|
}
|
|
33605
|
-
|
|
33606
|
-
console.log(`
|
|
33607
|
-
console.log(`
|
|
33608
|
-
|
|
34508
|
+
const display = direct ?? resolved;
|
|
34509
|
+
console.log(`approval: ${display.approvalMode}`);
|
|
34510
|
+
console.log(`sandbox: ${display.sandboxMode}`);
|
|
34511
|
+
console.log(`trusted directories: ${display.trustedDirectories.length}`);
|
|
34512
|
+
for (const directory of display.trustedDirectories) {
|
|
33609
34513
|
console.log(` - ${directory}`);
|
|
33610
34514
|
}
|
|
33611
34515
|
});
|
|
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
|
|
33614
|
-
|
|
34516
|
+
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) => {
|
|
34517
|
+
const scope = getScope(options);
|
|
34518
|
+
const cwd = import_node_process13.default.cwd();
|
|
34519
|
+
const { direct, resolved } = await loadScopedConfig(scope, cwd);
|
|
34520
|
+
if (!resolved && !direct) {
|
|
33615
34521
|
throw new Error("No config found. Run `kimbho init` first.");
|
|
33616
34522
|
}
|
|
34523
|
+
const base = direct ?? resolved ?? createDefaultConfig();
|
|
33617
34524
|
const next = {
|
|
33618
|
-
...
|
|
34525
|
+
...base,
|
|
33619
34526
|
...options.approval ? {
|
|
33620
34527
|
approvalMode: options.approval
|
|
33621
34528
|
} : {},
|
|
@@ -33623,32 +34530,40 @@ function createPermissionsCommand() {
|
|
|
33623
34530
|
sandboxMode: options.sandbox
|
|
33624
34531
|
} : {}
|
|
33625
34532
|
};
|
|
33626
|
-
const outputPath = await
|
|
34533
|
+
const outputPath = await saveScopedConfig(scope, cwd, next);
|
|
33627
34534
|
console.log(`Updated ${outputPath}`);
|
|
33628
34535
|
});
|
|
33629
|
-
command.command("trust").description("Add a trusted directory.").argument("<path>", "Directory to trust").action(async (trustedPath) => {
|
|
33630
|
-
const
|
|
33631
|
-
|
|
34536
|
+
command.command("trust").description("Add a trusted directory.").argument("<path>", "Directory to trust").option("--scope <scope>", "project or user", "project").action(async (trustedPath, options) => {
|
|
34537
|
+
const scope = getScope(options);
|
|
34538
|
+
const cwd = import_node_process13.default.cwd();
|
|
34539
|
+
const { direct, resolved } = await loadScopedConfig(scope, cwd);
|
|
34540
|
+
if (!resolved && !direct) {
|
|
33632
34541
|
throw new Error("No config found. Run `kimbho init` first.");
|
|
33633
34542
|
}
|
|
33634
|
-
const
|
|
33635
|
-
|
|
34543
|
+
const base = direct ?? resolved ?? createDefaultConfig();
|
|
34544
|
+
const normalizedPath = import_node_path19.default.resolve(cwd, trustedPath);
|
|
34545
|
+
const outputPath = await saveScopedConfig(scope, cwd, {
|
|
34546
|
+
...base,
|
|
33636
34547
|
trustedDirectories: Array.from(/* @__PURE__ */ new Set([
|
|
33637
|
-
...
|
|
33638
|
-
|
|
34548
|
+
...base.trustedDirectories,
|
|
34549
|
+
normalizedPath
|
|
33639
34550
|
]))
|
|
33640
|
-
}
|
|
34551
|
+
});
|
|
33641
34552
|
console.log(`Updated ${outputPath}`);
|
|
33642
34553
|
});
|
|
33643
|
-
command.command("untrust").description("Remove a trusted directory.").argument("<path>", "Directory to remove").action(async (trustedPath) => {
|
|
33644
|
-
const
|
|
33645
|
-
|
|
34554
|
+
command.command("untrust").description("Remove a trusted directory.").argument("<path>", "Directory to remove").option("--scope <scope>", "project or user", "project").action(async (trustedPath, options) => {
|
|
34555
|
+
const scope = getScope(options);
|
|
34556
|
+
const cwd = import_node_process13.default.cwd();
|
|
34557
|
+
const { direct, resolved } = await loadScopedConfig(scope, cwd);
|
|
34558
|
+
if (!resolved && !direct) {
|
|
33646
34559
|
throw new Error("No config found. Run `kimbho init` first.");
|
|
33647
34560
|
}
|
|
33648
|
-
const
|
|
33649
|
-
|
|
33650
|
-
|
|
33651
|
-
|
|
34561
|
+
const base = direct ?? resolved ?? createDefaultConfig();
|
|
34562
|
+
const normalizedPath = import_node_path19.default.resolve(cwd, trustedPath);
|
|
34563
|
+
const outputPath = await saveScopedConfig(scope, cwd, {
|
|
34564
|
+
...base,
|
|
34565
|
+
trustedDirectories: base.trustedDirectories.filter((directory) => directory !== trustedPath && directory !== normalizedPath)
|
|
34566
|
+
});
|
|
33652
34567
|
console.log(`Updated ${outputPath}`);
|
|
33653
34568
|
});
|
|
33654
34569
|
return command;
|
|
@@ -33762,7 +34677,7 @@ function createProvidersCommand() {
|
|
|
33762
34677
|
|
|
33763
34678
|
// src/commands/review.ts
|
|
33764
34679
|
var import_promises17 = require("node:fs/promises");
|
|
33765
|
-
var
|
|
34680
|
+
var import_node_path20 = __toESM(require("node:path"), 1);
|
|
33766
34681
|
var import_node_process15 = __toESM(require("node:process"), 1);
|
|
33767
34682
|
function summarizeChangedFiles2(diff) {
|
|
33768
34683
|
const files = /* @__PURE__ */ new Set();
|
|
@@ -33846,7 +34761,7 @@ ${diff.stdout}`
|
|
|
33846
34761
|
}
|
|
33847
34762
|
}
|
|
33848
34763
|
await ensureKimbhoDir(cwd);
|
|
33849
|
-
const artifactPath =
|
|
34764
|
+
const artifactPath = import_node_path20.default.join(resolveKimbhoDir(cwd), "logs", `review-${Date.now()}.md`);
|
|
33850
34765
|
await (0, import_promises17.writeFile)(artifactPath, [
|
|
33851
34766
|
`# Review`,
|
|
33852
34767
|
``,
|
|
@@ -34080,6 +34995,12 @@ function normalizeCliTokens(tokens) {
|
|
|
34080
34995
|
}
|
|
34081
34996
|
function createProgram(onOpenShell) {
|
|
34082
34997
|
const program2 = new Command();
|
|
34998
|
+
const legacyProviders = createProvidersCommand();
|
|
34999
|
+
const legacyModels = createModelsCommand();
|
|
35000
|
+
const legacyBrains = createBrainsCommand();
|
|
35001
|
+
legacyProviders._hidden = true;
|
|
35002
|
+
legacyModels._hidden = true;
|
|
35003
|
+
legacyBrains._hidden = true;
|
|
34083
35004
|
program2.name("kimbho").description(KIMBHO_DESCRIPTION).version(KIMBHO_VERSION);
|
|
34084
35005
|
program2.addCommand(createInitCommand());
|
|
34085
35006
|
program2.addCommand(createPlanCommand());
|
|
@@ -34090,9 +35011,9 @@ function createProgram(onOpenShell) {
|
|
|
34090
35011
|
program2.addCommand(createMcpCommand());
|
|
34091
35012
|
program2.addCommand(createModelCommand());
|
|
34092
35013
|
program2.addCommand(createAgentsCommand());
|
|
34093
|
-
program2.addCommand(
|
|
34094
|
-
program2.addCommand(
|
|
34095
|
-
program2.addCommand(
|
|
35014
|
+
program2.addCommand(legacyProviders);
|
|
35015
|
+
program2.addCommand(legacyModels);
|
|
35016
|
+
program2.addCommand(legacyBrains);
|
|
34096
35017
|
program2.addCommand(createRunCommand());
|
|
34097
35018
|
program2.addCommand(createResumeCommand());
|
|
34098
35019
|
program2.addCommand(createFixCommand());
|
|
@@ -34108,7 +35029,7 @@ function createProgram(onOpenShell) {
|
|
|
34108
35029
|
// src/shell.ts
|
|
34109
35030
|
var import_node_readline = require("node:readline");
|
|
34110
35031
|
var import_promises18 = require("node:readline/promises");
|
|
34111
|
-
var
|
|
35032
|
+
var import_node_path21 = __toESM(require("node:path"), 1);
|
|
34112
35033
|
var import_node_process18 = __toESM(require("node:process"), 1);
|
|
34113
35034
|
|
|
34114
35035
|
// src/agent-management.ts
|
|
@@ -34162,6 +35083,7 @@ var TOP_LEVEL_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
34162
35083
|
"help",
|
|
34163
35084
|
"init",
|
|
34164
35085
|
"mcp",
|
|
35086
|
+
"mcp__",
|
|
34165
35087
|
"memory",
|
|
34166
35088
|
"model",
|
|
34167
35089
|
"models",
|
|
@@ -34263,9 +35185,67 @@ var PLAN_PREFIXES = [
|
|
|
34263
35185
|
"outline ",
|
|
34264
35186
|
"propose "
|
|
34265
35187
|
];
|
|
35188
|
+
function looksLikeExecutionRequest(input) {
|
|
35189
|
+
return /\b(build|create|make|scaffold|implement|fix|refactor|setup|set up|generate|add|change|update|edit|rewrite|restyle|redesign|improve|enhance)\b/.test(input);
|
|
35190
|
+
}
|
|
34266
35191
|
function color(code, value) {
|
|
34267
35192
|
return `${code}${value}${RESET}`;
|
|
34268
35193
|
}
|
|
35194
|
+
function extractMcpResourceReferences(prompt) {
|
|
35195
|
+
const matches = prompt.match(/@[A-Za-z0-9_-]+:[^\s]+/g) ?? [];
|
|
35196
|
+
return matches.flatMap((token) => {
|
|
35197
|
+
const withoutAt = token.slice(1);
|
|
35198
|
+
const separatorIndex = withoutAt.indexOf(":");
|
|
35199
|
+
if (separatorIndex === -1) {
|
|
35200
|
+
return [];
|
|
35201
|
+
}
|
|
35202
|
+
return [
|
|
35203
|
+
{
|
|
35204
|
+
token,
|
|
35205
|
+
serverName: withoutAt.slice(0, separatorIndex),
|
|
35206
|
+
uri: withoutAt.slice(separatorIndex + 1)
|
|
35207
|
+
}
|
|
35208
|
+
];
|
|
35209
|
+
});
|
|
35210
|
+
}
|
|
35211
|
+
async function hydratePromptWithMcpResources(cwd, prompt) {
|
|
35212
|
+
const references = extractMcpResourceReferences(prompt);
|
|
35213
|
+
if (references.length === 0) {
|
|
35214
|
+
return {
|
|
35215
|
+
prompt,
|
|
35216
|
+
notes: []
|
|
35217
|
+
};
|
|
35218
|
+
}
|
|
35219
|
+
const blocks = [];
|
|
35220
|
+
const notes = [];
|
|
35221
|
+
for (const reference of references) {
|
|
35222
|
+
try {
|
|
35223
|
+
const rendered = await renderMcpResourceRead(cwd, reference.serverName, reference.uri);
|
|
35224
|
+
notes.push(`attached MCP resource ${reference.token}`);
|
|
35225
|
+
blocks.push([
|
|
35226
|
+
`Attached MCP resource ${reference.token}:`,
|
|
35227
|
+
...rendered
|
|
35228
|
+
].join("\n"));
|
|
35229
|
+
} catch (error2) {
|
|
35230
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
35231
|
+
notes.push(`failed to attach ${reference.token}: ${message}`);
|
|
35232
|
+
}
|
|
35233
|
+
}
|
|
35234
|
+
if (blocks.length === 0) {
|
|
35235
|
+
return {
|
|
35236
|
+
prompt,
|
|
35237
|
+
notes
|
|
35238
|
+
};
|
|
35239
|
+
}
|
|
35240
|
+
return {
|
|
35241
|
+
prompt: [
|
|
35242
|
+
prompt,
|
|
35243
|
+
"",
|
|
35244
|
+
...blocks
|
|
35245
|
+
].join("\n"),
|
|
35246
|
+
notes
|
|
35247
|
+
};
|
|
35248
|
+
}
|
|
34269
35249
|
function createExecutionTelemetry() {
|
|
34270
35250
|
return {
|
|
34271
35251
|
toolCalls: 0,
|
|
@@ -34535,6 +35515,11 @@ function renderHelp() {
|
|
|
34535
35515
|
"/mcp add <name> --command <cmd> [--arg <value>] [--env KEY=VALUE]",
|
|
34536
35516
|
" Add an MCP stdio server to .mcp.json.",
|
|
34537
35517
|
"/mcp tools [server] Discover tools/prompts/resources from MCP servers.",
|
|
35518
|
+
"/mcp prompts [server] List MCP prompts.",
|
|
35519
|
+
"/mcp resources [server] List MCP resources and templates.",
|
|
35520
|
+
"/mcp prompt <server> <prompt> [key=value ...]",
|
|
35521
|
+
" Render an MCP prompt.",
|
|
35522
|
+
"/mcp read <server> <uri> Read one MCP resource.",
|
|
34538
35523
|
"/agents List custom agents and teams.",
|
|
34539
35524
|
"/agents create <id> --base <role> Create a markdown custom agent.",
|
|
34540
35525
|
"/agents team create <id> <agent...> Create a markdown team file.",
|
|
@@ -34571,7 +35556,7 @@ function renderStartupCard(cwd, state) {
|
|
|
34571
35556
|
renderCardLine("shortcuts", "/ask /run /model /permissions /memory /mcp /quit")
|
|
34572
35557
|
];
|
|
34573
35558
|
if (!state.configured) {
|
|
34574
|
-
cardLines.push("setup: run /init or /
|
|
35559
|
+
cardLines.push("setup: run /init or /model add <template> to create .kimbho/config.json");
|
|
34575
35560
|
}
|
|
34576
35561
|
return renderBox(cardLines);
|
|
34577
35562
|
}
|
|
@@ -34596,20 +35581,20 @@ function inferPromptIntent(input) {
|
|
|
34596
35581
|
if (/^(hi|hello|hey|yo|sup)\b/.test(normalized)) {
|
|
34597
35582
|
return "chat";
|
|
34598
35583
|
}
|
|
34599
|
-
if (normalized.endsWith("?")) {
|
|
34600
|
-
return "chat";
|
|
34601
|
-
}
|
|
34602
35584
|
if (PLAN_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
34603
35585
|
return "plan";
|
|
34604
35586
|
}
|
|
34605
35587
|
if (EXECUTION_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
34606
35588
|
return "run";
|
|
34607
35589
|
}
|
|
34608
|
-
if (
|
|
35590
|
+
if (looksLikeExecutionRequest(normalized)) {
|
|
35591
|
+
return "run";
|
|
35592
|
+
}
|
|
35593
|
+
if (normalized.endsWith("?")) {
|
|
34609
35594
|
return "chat";
|
|
34610
35595
|
}
|
|
34611
|
-
if (
|
|
34612
|
-
return "
|
|
35596
|
+
if (CHAT_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
35597
|
+
return "chat";
|
|
34613
35598
|
}
|
|
34614
35599
|
return normalized.split(/\s+/).length >= 4 ? "plan" : "chat";
|
|
34615
35600
|
}
|
|
@@ -34863,6 +35848,12 @@ function hasRenderableDiff(toolId, output) {
|
|
|
34863
35848
|
}
|
|
34864
35849
|
return false;
|
|
34865
35850
|
}
|
|
35851
|
+
function isMutationTool(toolId) {
|
|
35852
|
+
return toolId === "file.write" || toolId === "file.patch" || toolId === "scaffold.generate";
|
|
35853
|
+
}
|
|
35854
|
+
function renderMutationArtifacts(artifacts, label = "changed", maxItems = 4) {
|
|
35855
|
+
return artifacts.slice(0, maxItems).map((artifact) => ` ${color(DIM, `${label}: ${shortenMiddle(artifact, 120)}`)}`);
|
|
35856
|
+
}
|
|
34866
35857
|
function colorDiffLine(line) {
|
|
34867
35858
|
if (line.startsWith("diff --git ")) {
|
|
34868
35859
|
const match = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
|
|
@@ -34921,14 +35912,26 @@ function renderShellSessionSummary(snapshot, planPath, sessionPath) {
|
|
|
34921
35912
|
lines.push(color(BOLD, "Activity"));
|
|
34922
35913
|
for (const event of snapshot.events.slice(-8)) {
|
|
34923
35914
|
lines.push(`- ${renderEventType(event.type)} | ${event.agentRole ?? "session"} | ${event.message}`);
|
|
35915
|
+
let landedMutation = false;
|
|
34924
35916
|
for (const toolResult of event.toolResults.slice(-4)) {
|
|
34925
35917
|
lines.push(` ${toolResult.success ? "ok" : "fail"} ${toolResult.toolId}: ${toolResult.summary}`);
|
|
34926
35918
|
if (toolResult.stdout && hasRenderableDiff(toolResult.toolId, toolResult.stdout)) {
|
|
35919
|
+
landedMutation = landedMutation || isMutationTool(toolResult.toolId);
|
|
34927
35920
|
lines.push(...renderDiffPreview(toolResult.stdout, 6));
|
|
34928
35921
|
}
|
|
34929
35922
|
if ((toolResult.toolId.startsWith("process.") || toolResult.toolId === "http.fetch" || toolResult.toolId.startsWith("browser.") || toolResult.toolId.startsWith("repo.")) && toolResult.stdout) {
|
|
34930
35923
|
lines.push(...renderTextPreview(toolResult.stdout, 6));
|
|
34931
35924
|
}
|
|
35925
|
+
if (toolResult.success && isMutationTool(toolResult.toolId) && toolResult.artifacts.length > 0) {
|
|
35926
|
+
landedMutation = true;
|
|
35927
|
+
lines.push(...renderMutationArtifacts(
|
|
35928
|
+
toolResult.artifacts,
|
|
35929
|
+
toolResult.toolId === "scaffold.generate" ? "created" : "changed"
|
|
35930
|
+
));
|
|
35931
|
+
}
|
|
35932
|
+
}
|
|
35933
|
+
if (event.type === "task-blocked" && !landedMutation) {
|
|
35934
|
+
lines.push(` ${color(DIM, "no safe edit landed before this task blocked.")}`);
|
|
34932
35935
|
}
|
|
34933
35936
|
for (const artifact of event.artifacts.slice(-3)) {
|
|
34934
35937
|
lines.push(` artifact: ${artifact}`);
|
|
@@ -35041,7 +36044,11 @@ function renderLiveExecutionEvent(event) {
|
|
|
35041
36044
|
case "tool-finished":
|
|
35042
36045
|
return [
|
|
35043
36046
|
` ${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) : []
|
|
36047
|
+
...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) : [],
|
|
36048
|
+
...event.toolResult.success && isMutationTool(event.toolResult.toolId) && event.toolResult.artifacts.length > 0 ? renderMutationArtifacts(
|
|
36049
|
+
event.toolResult.artifacts,
|
|
36050
|
+
event.toolResult.toolId === "scaffold.generate" ? "created" : "changed"
|
|
36051
|
+
) : []
|
|
35045
36052
|
];
|
|
35046
36053
|
default:
|
|
35047
36054
|
return [];
|
|
@@ -35190,13 +36197,18 @@ function clearAllConversations(runtime) {
|
|
|
35190
36197
|
}
|
|
35191
36198
|
}
|
|
35192
36199
|
async function handleChatPrompt(cwd, prompt, runtime) {
|
|
36200
|
+
const hydrated = await hydratePromptWithMcpResources(cwd, prompt);
|
|
35193
36201
|
const config2 = await loadConfig(cwd);
|
|
35194
36202
|
if (!config2) {
|
|
35195
|
-
throw new Error("No config found. Run /init or /
|
|
36203
|
+
throw new Error("No config found. Run /init or /model add <template> first.");
|
|
35196
36204
|
}
|
|
35197
36205
|
const registry2 = createDefaultBrainProviderRegistry(cwd);
|
|
35198
36206
|
const resolver = new BrainResolver(config2, registry2);
|
|
35199
36207
|
const brain = await resolver.resolve(runtime.focusRole);
|
|
36208
|
+
const memoryContext = await loadMarkdownMemoryContext(cwd, {
|
|
36209
|
+
maxFiles: 8,
|
|
36210
|
+
maxCharsPerFile: 3e3
|
|
36211
|
+
});
|
|
35200
36212
|
const compactionPath = await compactConversationIfNeeded(cwd, runtime, runtime.focusRole);
|
|
35201
36213
|
if (compactionPath) {
|
|
35202
36214
|
console.log(color(DIM, `[compacting] condensed ${runtime.focusRole} chat context -> ${compactionPath}`));
|
|
@@ -35206,7 +36218,7 @@ async function handleChatPrompt(cwd, prompt, runtime) {
|
|
|
35206
36218
|
...history,
|
|
35207
36219
|
{
|
|
35208
36220
|
role: "user",
|
|
35209
|
-
content: prompt
|
|
36221
|
+
content: hydrated.prompt
|
|
35210
36222
|
}
|
|
35211
36223
|
]);
|
|
35212
36224
|
let result;
|
|
@@ -35219,6 +36231,11 @@ async function handleChatPrompt(cwd, prompt, runtime) {
|
|
|
35219
36231
|
...brain.settings.promptPreamble || runtime.conversationSummaries[runtime.focusRole] ? {
|
|
35220
36232
|
systemPrompt: [
|
|
35221
36233
|
brain.settings.promptPreamble,
|
|
36234
|
+
memoryContext.length > 0 ? [
|
|
36235
|
+
"Workspace memory:",
|
|
36236
|
+
...memoryContext.map((record2) => `File: ${record2.filePath}
|
|
36237
|
+
${record2.content}`)
|
|
36238
|
+
].join("\n\n") : null,
|
|
35222
36239
|
runtime.conversationSummaries[runtime.focusRole] ? `Compacted conversation summary:
|
|
35223
36240
|
${runtime.conversationSummaries[runtime.focusRole]}` : null
|
|
35224
36241
|
].filter((value) => Boolean(value)).join("\n\n")
|
|
@@ -35245,16 +36262,20 @@ ${runtime.conversationSummaries[runtime.focusRole]}` : null
|
|
|
35245
36262
|
]);
|
|
35246
36263
|
runtime.conversations[runtime.focusRole] = nextConversation;
|
|
35247
36264
|
console.log(color(DIM, `[${brain.role}] ${brain.provider.id}/${brain.model}`));
|
|
36265
|
+
for (const note of hydrated.notes) {
|
|
36266
|
+
console.log(color(DIM, note));
|
|
36267
|
+
}
|
|
35248
36268
|
if (result.usage) {
|
|
35249
36269
|
console.log(color(DIM, `tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`));
|
|
35250
36270
|
}
|
|
35251
36271
|
console.log(renderTerminalMarkdown(result.text));
|
|
35252
36272
|
}
|
|
35253
36273
|
async function runGoalExecution(cwd, goal, runtime) {
|
|
36274
|
+
const hydrated = await hydratePromptWithMcpResources(cwd, goal);
|
|
35254
36275
|
const orchestrator = new ExecutionOrchestrator();
|
|
35255
|
-
const workspace = await resolveExecutionWorkspace(cwd,
|
|
36276
|
+
const workspace = await resolveExecutionWorkspace(cwd, hydrated.prompt);
|
|
35256
36277
|
const request = {
|
|
35257
|
-
goal,
|
|
36278
|
+
goal: hydrated.prompt,
|
|
35258
36279
|
mode: "run",
|
|
35259
36280
|
cwd: workspace.cwd,
|
|
35260
36281
|
workspaceState: workspace.workspaceState,
|
|
@@ -35269,6 +36290,9 @@ async function runGoalExecution(cwd, goal, runtime) {
|
|
|
35269
36290
|
};
|
|
35270
36291
|
const planningSpinner = new ShellActivityIndicator("planning");
|
|
35271
36292
|
console.log(color(DIM, `Working on: ${goal}`));
|
|
36293
|
+
for (const note of hydrated.notes) {
|
|
36294
|
+
console.log(color(DIM, note));
|
|
36295
|
+
}
|
|
35272
36296
|
for (const note of workspace.notes) {
|
|
35273
36297
|
console.log(color(DIM, note));
|
|
35274
36298
|
}
|
|
@@ -35529,7 +36553,7 @@ async function printLatestPlanSummary(cwd) {
|
|
|
35529
36553
|
async function handleApprovalModeCommand(cwd, tokens) {
|
|
35530
36554
|
const config2 = await loadConfig(cwd);
|
|
35531
36555
|
if (!config2) {
|
|
35532
|
-
throw new Error("No config found. Run /init or /
|
|
36556
|
+
throw new Error("No config found. Run /init or /model add <template> first.");
|
|
35533
36557
|
}
|
|
35534
36558
|
const subcommand = tokens[1]?.trim().toLowerCase();
|
|
35535
36559
|
if (!subcommand || subcommand === "status") {
|
|
@@ -35549,44 +36573,55 @@ async function handleApprovalModeCommand(cwd, tokens) {
|
|
|
35549
36573
|
console.log(`approval mode: ${subcommand}`);
|
|
35550
36574
|
}
|
|
35551
36575
|
async function printConfigSummary(cwd) {
|
|
35552
|
-
const
|
|
36576
|
+
const layers = await loadConfigLayers(cwd);
|
|
35553
36577
|
const paths = await listMemoryPaths(cwd);
|
|
35554
|
-
console.log(`config: ${resolveConfigPath(cwd)}`);
|
|
36578
|
+
console.log(`config.project: ${resolveConfigPath(cwd)}`);
|
|
36579
|
+
console.log(`config.user: ${resolveGlobalConfigPath()}`);
|
|
35555
36580
|
console.log(`mcp: ${resolveMcpConfigPath(cwd)}`);
|
|
35556
36581
|
console.log(`memory.project: ${paths.projectMemoryPath}`);
|
|
35557
36582
|
console.log(`memory.user: ${paths.userMemoryPath}`);
|
|
35558
36583
|
console.log(`memory.agents: ${paths.agentMemoryDir}`);
|
|
35559
|
-
if (!
|
|
36584
|
+
if (!layers.resolved) {
|
|
35560
36585
|
console.log("No config found. Run /init first.");
|
|
35561
36586
|
return;
|
|
35562
36587
|
}
|
|
36588
|
+
console.log(`layers: user=${layers.user ? "yes" : "no"} project=${layers.project ? "yes" : "no"}`);
|
|
36589
|
+
const config2 = layers.resolved;
|
|
35563
36590
|
console.log(`providers: ${config2.providers.length}`);
|
|
35564
36591
|
console.log(`approval: ${config2.approvalMode}`);
|
|
35565
36592
|
console.log(`sandbox: ${config2.sandboxMode}`);
|
|
35566
36593
|
console.log(`trusted directories: ${config2.trustedDirectories.length}`);
|
|
35567
36594
|
}
|
|
35568
36595
|
async function handlePermissionsCommand(cwd, tokens) {
|
|
35569
|
-
const
|
|
35570
|
-
|
|
36596
|
+
const subcommand = tokens[1]?.trim().toLowerCase();
|
|
36597
|
+
const scopeToken = tokens.find((token, index) => index > 0 && (token === "user" || token === "project"));
|
|
36598
|
+
const scope = scopeToken === "user" ? "user" : "project";
|
|
36599
|
+
const direct = scope === "user" ? await loadUserConfig() : await loadProjectConfig(cwd);
|
|
36600
|
+
const resolved = await loadConfig(cwd);
|
|
36601
|
+
if (!resolved && !direct) {
|
|
35571
36602
|
throw new Error("No config found. Run /init or /model add <template> first.");
|
|
35572
36603
|
}
|
|
35573
|
-
const
|
|
36604
|
+
const effective = direct ?? resolved ?? createDefaultConfig();
|
|
36605
|
+
const saveScoped = async (next) => scope === "user" ? saveUserConfig(next) : saveConfig(next, cwd);
|
|
35574
36606
|
if (!subcommand || subcommand === "show" || subcommand === "status") {
|
|
35575
|
-
console.log(`
|
|
35576
|
-
console.log(`
|
|
35577
|
-
console.log(`
|
|
35578
|
-
|
|
36607
|
+
console.log(`scope: ${scope}`);
|
|
36608
|
+
console.log(`direct config: ${direct ? "present" : "missing"}`);
|
|
36609
|
+
console.log(`approval: ${effective.approvalMode}`);
|
|
36610
|
+
console.log(`sandbox: ${effective.sandboxMode}`);
|
|
36611
|
+
console.log(`trusted directories: ${effective.trustedDirectories.length}`);
|
|
36612
|
+
for (const directory of effective.trustedDirectories) {
|
|
35579
36613
|
console.log(` - ${directory}`);
|
|
35580
36614
|
}
|
|
35581
36615
|
return;
|
|
35582
36616
|
}
|
|
35583
36617
|
if (subcommand === "auto" || subcommand === "manual") {
|
|
35584
|
-
const outputPath = await
|
|
35585
|
-
...
|
|
36618
|
+
const outputPath = await saveScoped({
|
|
36619
|
+
...effective,
|
|
35586
36620
|
approvalMode: subcommand
|
|
35587
|
-
}
|
|
36621
|
+
});
|
|
35588
36622
|
console.log(`Updated ${outputPath}`);
|
|
35589
36623
|
console.log(`approval mode: ${subcommand}`);
|
|
36624
|
+
console.log(`scope: ${scope}`);
|
|
35590
36625
|
return;
|
|
35591
36626
|
}
|
|
35592
36627
|
if (subcommand === "sandbox") {
|
|
@@ -35598,12 +36633,13 @@ async function handlePermissionsCommand(cwd, tokens) {
|
|
|
35598
36633
|
].includes(mode)) {
|
|
35599
36634
|
throw new Error("Usage: /permissions sandbox <read-only|workspace-write|full>");
|
|
35600
36635
|
}
|
|
35601
|
-
const outputPath = await
|
|
35602
|
-
...
|
|
36636
|
+
const outputPath = await saveScoped({
|
|
36637
|
+
...effective,
|
|
35603
36638
|
sandboxMode: mode
|
|
35604
|
-
}
|
|
36639
|
+
});
|
|
35605
36640
|
console.log(`Updated ${outputPath}`);
|
|
35606
36641
|
console.log(`sandbox: ${mode}`);
|
|
36642
|
+
console.log(`scope: ${scope}`);
|
|
35607
36643
|
return;
|
|
35608
36644
|
}
|
|
35609
36645
|
if (subcommand === "trust" || subcommand === "untrust") {
|
|
@@ -35611,19 +36647,21 @@ async function handlePermissionsCommand(cwd, tokens) {
|
|
|
35611
36647
|
if (!target) {
|
|
35612
36648
|
throw new Error(`Usage: /permissions ${subcommand} <path>`);
|
|
35613
36649
|
}
|
|
36650
|
+
const normalizedTarget = import_node_path21.default.resolve(cwd, target);
|
|
35614
36651
|
const trustedDirectories = subcommand === "trust" ? Array.from(/* @__PURE__ */ new Set([
|
|
35615
|
-
...
|
|
35616
|
-
|
|
35617
|
-
])) :
|
|
35618
|
-
const outputPath = await
|
|
35619
|
-
...
|
|
36652
|
+
...effective.trustedDirectories,
|
|
36653
|
+
normalizedTarget
|
|
36654
|
+
])) : effective.trustedDirectories.filter((directory) => directory !== target && directory !== normalizedTarget);
|
|
36655
|
+
const outputPath = await saveScoped({
|
|
36656
|
+
...effective,
|
|
35620
36657
|
trustedDirectories
|
|
35621
|
-
}
|
|
36658
|
+
});
|
|
35622
36659
|
console.log(`Updated ${outputPath}`);
|
|
35623
|
-
console.log(`${subcommand === "trust" ? "Trusted" : "Removed"} ${
|
|
36660
|
+
console.log(`${subcommand === "trust" ? "Trusted" : "Removed"} ${normalizedTarget}`);
|
|
36661
|
+
console.log(`scope: ${scope}`);
|
|
35624
36662
|
return;
|
|
35625
36663
|
}
|
|
35626
|
-
throw new Error("Usage: /permissions [show|auto|manual|sandbox <mode>|trust <path>|untrust <path>]");
|
|
36664
|
+
throw new Error("Usage: /permissions [show|auto|manual|sandbox <mode>|trust <path>|untrust <path>] [project|user]");
|
|
35627
36665
|
}
|
|
35628
36666
|
async function handleMemoryCommand(cwd, tokens) {
|
|
35629
36667
|
const subcommand = tokens[1]?.trim().toLowerCase();
|
|
@@ -35638,6 +36676,16 @@ async function handleMemoryCommand(cwd, tokens) {
|
|
|
35638
36676
|
if (subcommand === "refresh") {
|
|
35639
36677
|
const result = await refreshMemoryFiles(cwd);
|
|
35640
36678
|
console.log(result.summary);
|
|
36679
|
+
console.log("Now I have a durable understanding of the current project structure.");
|
|
36680
|
+
console.log(`Purpose: ${result.purpose}`);
|
|
36681
|
+
console.log(`Indexed: ${result.indexedFiles} files (${result.sourceFiles} source files), ${result.routes} routes, ${result.schemas} schemas`);
|
|
36682
|
+
console.log(`Frameworks: ${result.frameworks.length > 0 ? result.frameworks.join(", ") : "unknown"}`);
|
|
36683
|
+
if (result.entrypoints.length > 0) {
|
|
36684
|
+
console.log(`Entrypoints: ${result.entrypoints.slice(0, 5).join(", ")}`);
|
|
36685
|
+
}
|
|
36686
|
+
if (result.verificationCommands.length > 0) {
|
|
36687
|
+
console.log(`Verification: ${result.verificationCommands.join(" -> ")}`);
|
|
36688
|
+
}
|
|
35641
36689
|
console.log(`Wrote ${result.initFilePath}`);
|
|
35642
36690
|
console.log(`Wrote ${result.projectMemoryPath}`);
|
|
35643
36691
|
console.log(`Wrote ${result.userMemoryPath}`);
|
|
@@ -35650,7 +36698,7 @@ async function handleMemoryCommand(cwd, tokens) {
|
|
|
35650
36698
|
}
|
|
35651
36699
|
let filePath;
|
|
35652
36700
|
if (scope === "init") {
|
|
35653
|
-
filePath =
|
|
36701
|
+
filePath = import_node_path21.default.join(cwd, "kimbho_init.md");
|
|
35654
36702
|
} else if (scope === "project") {
|
|
35655
36703
|
filePath = resolveProjectMemoryPath(cwd);
|
|
35656
36704
|
} else if (scope === "user") {
|
|
@@ -35660,7 +36708,7 @@ async function handleMemoryCommand(cwd, tokens) {
|
|
|
35660
36708
|
if (!agentId) {
|
|
35661
36709
|
throw new Error("Usage: /memory show agent <agent-id>");
|
|
35662
36710
|
}
|
|
35663
|
-
filePath =
|
|
36711
|
+
filePath = import_node_path21.default.join(resolveAgentMemoryDir(cwd), `${agentId}.md`);
|
|
35664
36712
|
} else {
|
|
35665
36713
|
throw new Error("Usage: /memory show <init|project|user|agent> [id]");
|
|
35666
36714
|
}
|
|
@@ -35728,6 +36776,45 @@ async function handleMcpCommand(cwd, tokens) {
|
|
|
35728
36776
|
}
|
|
35729
36777
|
return;
|
|
35730
36778
|
}
|
|
36779
|
+
if (subcommand === "prompts") {
|
|
36780
|
+
const name = tokens[2]?.trim();
|
|
36781
|
+
for (const line of await renderMcpPromptList(cwd, name)) {
|
|
36782
|
+
console.log(line);
|
|
36783
|
+
}
|
|
36784
|
+
return;
|
|
36785
|
+
}
|
|
36786
|
+
if (subcommand === "resources") {
|
|
36787
|
+
const name = tokens[2]?.trim();
|
|
36788
|
+
for (const line of await renderMcpResourceList(cwd, name)) {
|
|
36789
|
+
console.log(line);
|
|
36790
|
+
}
|
|
36791
|
+
return;
|
|
36792
|
+
}
|
|
36793
|
+
if (subcommand === "prompt") {
|
|
36794
|
+
const serverName = tokens[2]?.trim();
|
|
36795
|
+
const promptName = tokens[3]?.trim();
|
|
36796
|
+
if (!serverName || !promptName) {
|
|
36797
|
+
throw new Error("Usage: /mcp prompt <server> <prompt> [key=value ...]");
|
|
36798
|
+
}
|
|
36799
|
+
const args = Object.fromEntries(
|
|
36800
|
+
tokens.slice(4).map((entry) => entry.split("=", 2)).filter((parts) => parts.length === 2 && parts[0].length > 0)
|
|
36801
|
+
);
|
|
36802
|
+
for (const line of await renderMcpPromptInvocation(cwd, serverName, promptName, args)) {
|
|
36803
|
+
console.log(line);
|
|
36804
|
+
}
|
|
36805
|
+
return;
|
|
36806
|
+
}
|
|
36807
|
+
if (subcommand === "read") {
|
|
36808
|
+
const serverName = tokens[2]?.trim();
|
|
36809
|
+
const uri = tokens.slice(3).join(" ").trim();
|
|
36810
|
+
if (!serverName || !uri) {
|
|
36811
|
+
throw new Error("Usage: /mcp read <server> <uri>");
|
|
36812
|
+
}
|
|
36813
|
+
for (const line of await renderMcpResourceRead(cwd, serverName, uri)) {
|
|
36814
|
+
console.log(line);
|
|
36815
|
+
}
|
|
36816
|
+
return;
|
|
36817
|
+
}
|
|
35731
36818
|
if (subcommand === "remove") {
|
|
35732
36819
|
const name = tokens[2]?.trim();
|
|
35733
36820
|
if (!name) {
|
|
@@ -35791,7 +36878,7 @@ async function handleMcpCommand(cwd, tokens) {
|
|
|
35791
36878
|
console.log(`Added MCP server ${name}`);
|
|
35792
36879
|
return;
|
|
35793
36880
|
}
|
|
35794
|
-
throw new Error("Usage: /mcp [list|tools [server]|
|
|
36881
|
+
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
36882
|
}
|
|
35796
36883
|
async function handleAgentsCommand(cwd, tokens) {
|
|
35797
36884
|
const subcommand = tokens[1]?.trim().toLowerCase();
|
|
@@ -35922,11 +37009,12 @@ async function handleModelSurfaceCommand(cwd, tokens, runtime) {
|
|
|
35922
37009
|
throw new Error("Usage: /model [show|providers|templates|add <tpl>|use <provider> [model]|find [search]|select <n>|focus <role>|check]");
|
|
35923
37010
|
}
|
|
35924
37011
|
async function createPlanOnly(cwd, goal) {
|
|
37012
|
+
const hydrated = await hydratePromptWithMcpResources(cwd, goal);
|
|
35925
37013
|
const request = {
|
|
35926
|
-
goal,
|
|
37014
|
+
goal: hydrated.prompt,
|
|
35927
37015
|
mode: "plan",
|
|
35928
37016
|
cwd,
|
|
35929
|
-
workspaceState: await inferPlanningWorkspaceState(cwd,
|
|
37017
|
+
workspaceState: await inferPlanningWorkspaceState(cwd, hydrated.prompt),
|
|
35930
37018
|
constraints: []
|
|
35931
37019
|
};
|
|
35932
37020
|
const activity = new ShellActivityIndicator("planning");
|
|
@@ -35939,6 +37027,9 @@ async function createPlanOnly(cwd, goal) {
|
|
|
35939
37027
|
}
|
|
35940
37028
|
const plan = planResult.plan;
|
|
35941
37029
|
const planPath = await savePlan(plan, cwd);
|
|
37030
|
+
for (const note of hydrated.notes) {
|
|
37031
|
+
console.log(color(DIM, note));
|
|
37032
|
+
}
|
|
35942
37033
|
for (const line of renderPlanGenerationNotes(planResult)) {
|
|
35943
37034
|
console.log(line);
|
|
35944
37035
|
}
|
|
@@ -36111,7 +37202,7 @@ function renderModelLine2(model) {
|
|
|
36111
37202
|
async function printProviderList(cwd, focusRole) {
|
|
36112
37203
|
const config2 = await loadConfig(cwd);
|
|
36113
37204
|
if (!config2) {
|
|
36114
|
-
console.log("No config found. Run /init or /
|
|
37205
|
+
console.log("No config found. Run /init or /model add <template> first.");
|
|
36115
37206
|
return;
|
|
36116
37207
|
}
|
|
36117
37208
|
const activeProviderId = config2.brains[focusRole].providerId;
|
|
@@ -36163,7 +37254,7 @@ async function addProviderFromTemplate(cwd, options) {
|
|
|
36163
37254
|
async function printProviderHealth(cwd) {
|
|
36164
37255
|
const config2 = await loadConfig(cwd);
|
|
36165
37256
|
if (!config2) {
|
|
36166
|
-
console.log("No config found. Run /init or /
|
|
37257
|
+
console.log("No config found. Run /init or /model add <template> first.");
|
|
36167
37258
|
return;
|
|
36168
37259
|
}
|
|
36169
37260
|
const registry2 = createDefaultBrainProviderRegistry(cwd);
|
|
@@ -36180,7 +37271,7 @@ async function printProviderHealth(cwd) {
|
|
|
36180
37271
|
async function printBrainAssignments(cwd) {
|
|
36181
37272
|
const config2 = await loadConfig(cwd);
|
|
36182
37273
|
if (!config2) {
|
|
36183
|
-
console.log("No config found. Run /init or /
|
|
37274
|
+
console.log("No config found. Run /init or /model add <template> first.");
|
|
36184
37275
|
return;
|
|
36185
37276
|
}
|
|
36186
37277
|
for (const role of BRAIN_ROLES) {
|
|
@@ -36218,7 +37309,7 @@ async function handleProvidersCommand(cwd, tokens, runtime) {
|
|
|
36218
37309
|
if (options.baseUrl) {
|
|
36219
37310
|
console.log(`Base URL ${options.baseUrl}`);
|
|
36220
37311
|
}
|
|
36221
|
-
console.log(`Next: /
|
|
37312
|
+
console.log(`Next: /model use ${resolvedProviderId}`);
|
|
36222
37313
|
return;
|
|
36223
37314
|
}
|
|
36224
37315
|
if (subcommand === "use") {
|
|
@@ -36258,7 +37349,7 @@ async function handleBrainCommand(cwd, tokens, runtime) {
|
|
|
36258
37349
|
async function handleModelsCommand(cwd, tokens, runtime) {
|
|
36259
37350
|
const config2 = await loadConfig(cwd);
|
|
36260
37351
|
if (!config2) {
|
|
36261
|
-
throw new Error("No config found. Run /init or /
|
|
37352
|
+
throw new Error("No config found. Run /init or /model add <template> first.");
|
|
36262
37353
|
}
|
|
36263
37354
|
const providerId = config2.brains[runtime.focusRole].providerId;
|
|
36264
37355
|
const provider = findProviderById(config2, providerId);
|
|
@@ -36301,12 +37392,12 @@ async function handleModelsCommand(cwd, tokens, runtime) {
|
|
|
36301
37392
|
console.log(` ${index + 1}. ${renderModelLine2(model)}`);
|
|
36302
37393
|
}
|
|
36303
37394
|
console.log(``);
|
|
36304
|
-
console.log("Use /select <number> or /use-
|
|
37395
|
+
console.log("Use /model select <number> or /model use <provider-id> <model-id> to assign one to all roles.");
|
|
36305
37396
|
}
|
|
36306
37397
|
async function handleModelSelection(cwd, modelId, runtime) {
|
|
36307
37398
|
const config2 = await loadConfig(cwd);
|
|
36308
37399
|
if (!config2) {
|
|
36309
|
-
throw new Error("No config found. Run /init or /
|
|
37400
|
+
throw new Error("No config found. Run /init or /model add <template> first.");
|
|
36310
37401
|
}
|
|
36311
37402
|
const providerId = config2.brains[runtime.focusRole].providerId;
|
|
36312
37403
|
const provider = findProviderById(config2, providerId);
|
|
@@ -36415,6 +37506,23 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
|
|
|
36415
37506
|
if (!head) {
|
|
36416
37507
|
return cwd;
|
|
36417
37508
|
}
|
|
37509
|
+
if (head.startsWith("mcp__")) {
|
|
37510
|
+
const parts = head.split("__").filter((part) => part.length > 0);
|
|
37511
|
+
if (parts.length < 3) {
|
|
37512
|
+
throw new Error("Usage: /mcp__<server>__<prompt> [key=value ...]");
|
|
37513
|
+
}
|
|
37514
|
+
const serverName = parts[1];
|
|
37515
|
+
const promptName = parts[2];
|
|
37516
|
+
if (!serverName || !promptName) {
|
|
37517
|
+
throw new Error("Usage: /mcp__<server>__<prompt> [key=value ...]");
|
|
37518
|
+
}
|
|
37519
|
+
const args = Object.fromEntries(
|
|
37520
|
+
tokens.slice(1).map((entry) => entry.split("=", 2)).filter((pair) => pair.length === 2 && pair[0].length > 0)
|
|
37521
|
+
);
|
|
37522
|
+
const rendered = await renderMcpPromptInvocation(cwd, serverName, promptName, args);
|
|
37523
|
+
await handleChatPrompt(cwd, rendered.join("\n"), runtime);
|
|
37524
|
+
return cwd;
|
|
37525
|
+
}
|
|
36418
37526
|
if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
|
|
36419
37527
|
const intent = inferPromptIntent(trimmed);
|
|
36420
37528
|
if (intent === "run") {
|