@kimbho/kimbho-cli 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1147,7 +1147,7 @@ var require_command = __commonJS({
1147
1147
  "../../node_modules/commander/lib/command.js"(exports2) {
1148
1148
  var EventEmitter = require("node:events").EventEmitter;
1149
1149
  var childProcess = require("node:child_process");
1150
- var path13 = require("node:path");
1150
+ var path14 = require("node:path");
1151
1151
  var fs = require("node:fs");
1152
1152
  var process14 = 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 = path13.resolve(baseDir, baseName);
2150
+ const localBin = path14.resolve(baseDir, baseName);
2151
2151
  if (fs.existsSync(localBin)) return localBin;
2152
- if (sourceExt.includes(path13.extname(baseName))) return void 0;
2152
+ if (sourceExt.includes(path14.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 = path13.resolve(
2171
- path13.dirname(resolvedScriptPath),
2170
+ executableDir = path14.resolve(
2171
+ path14.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 = path13.basename(
2178
+ const legacyName = path14.basename(
2179
2179
  this._scriptPath,
2180
- path13.extname(this._scriptPath)
2180
+ path14.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(path13.extname(executableFile));
2191
+ launchWithNode = sourceExt.includes(path14.extname(executableFile));
2192
2192
  let proc;
2193
2193
  if (process14.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 = path13.basename(filename, path13.extname(filename));
3038
+ this._name = path14.basename(filename, path14.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(path14) {
3053
- if (path14 === void 0) return this._executableDir;
3054
- this._executableDir = path14;
3052
+ executableDir(path15) {
3053
+ if (path15 === void 0) return this._executableDir;
3054
+ this._executableDir = path15;
3055
3055
  return this;
3056
3056
  }
3057
3057
  /**
@@ -3346,7 +3346,7 @@ var {
3346
3346
  // package.json
3347
3347
  var package_default = {
3348
3348
  name: "@kimbho/kimbho-cli",
3349
- version: "0.1.6",
3349
+ version: "0.1.7",
3350
3350
  description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
3351
3351
  type: "module",
3352
3352
  engines: {
@@ -4140,8 +4140,8 @@ function getErrorMap() {
4140
4140
 
4141
4141
  // ../../node_modules/zod/v3/helpers/parseUtil.js
4142
4142
  var makeIssue = (params) => {
4143
- const { data, path: path13, errorMaps, issueData } = params;
4144
- const fullPath = [...path13, ...issueData.path || []];
4143
+ const { data, path: path14, errorMaps, issueData } = params;
4144
+ const fullPath = [...path14, ...issueData.path || []];
4145
4145
  const fullIssue = {
4146
4146
  ...issueData,
4147
4147
  path: fullPath
@@ -4257,11 +4257,11 @@ var errorUtil;
4257
4257
 
4258
4258
  // ../../node_modules/zod/v3/types.js
4259
4259
  var ParseInputLazyPath = class {
4260
- constructor(parent, value, path13, key) {
4260
+ constructor(parent, value, path14, key) {
4261
4261
  this._cachedPath = [];
4262
4262
  this.parent = parent;
4263
4263
  this.data = value;
4264
- this._path = path13;
4264
+ this._path = path14;
4265
4265
  this._key = key;
4266
4266
  }
4267
4267
  get path() {
@@ -9353,9 +9353,15 @@ var AutonomousTaskExecutor = class {
9353
9353
  try {
9354
9354
  result = await this.toolRuntime.run(toolId, input, options.signal ? {
9355
9355
  cwd: request.cwd,
9356
- signal: options.signal
9356
+ signal: options.signal,
9357
+ approvalMode: this.config.approvalMode,
9358
+ sandboxMode: this.config.sandboxMode,
9359
+ agentRole: task.agentRole
9357
9360
  } : {
9358
- cwd: request.cwd
9361
+ cwd: request.cwd,
9362
+ approvalMode: this.config.approvalMode,
9363
+ sandboxMode: this.config.sandboxMode,
9364
+ agentRole: task.agentRole
9359
9365
  });
9360
9366
  } catch (error) {
9361
9367
  if (isAbortError(error)) {
@@ -9639,9 +9645,15 @@ var AutonomousTaskExecutor = class {
9639
9645
  try {
9640
9646
  result = await this.toolRuntime.run(parsedAction.tool, parsedAction.input, options.signal ? {
9641
9647
  cwd: request.cwd,
9642
- signal: options.signal
9648
+ signal: options.signal,
9649
+ approvalMode: this.config.approvalMode,
9650
+ sandboxMode: this.config.sandboxMode,
9651
+ agentRole: task.agentRole
9643
9652
  } : {
9644
- cwd: request.cwd
9653
+ cwd: request.cwd,
9654
+ approvalMode: this.config.approvalMode,
9655
+ sandboxMode: this.config.sandboxMode,
9656
+ agentRole: task.agentRole
9645
9657
  });
9646
9658
  } catch (error) {
9647
9659
  if (isAbortError(error)) {
@@ -10740,7 +10752,12 @@ async function loadTypeScript() {
10740
10752
  return module2;
10741
10753
  }
10742
10754
  async function walkWorkspace(root, current = root, results = []) {
10743
- const entries = await (0, import_promises7.readdir)(current, { withFileTypes: true });
10755
+ const entries = await (0, import_promises7.readdir)(current, { withFileTypes: true }).catch((error) => {
10756
+ if (error.code === "EACCES" || error.code === "EPERM" || error.code === "ENOENT") {
10757
+ return [];
10758
+ }
10759
+ throw error;
10760
+ });
10744
10761
  for (const entry of entries) {
10745
10762
  const fullPath = import_node_path7.default.join(current, entry.name);
10746
10763
  const relativePath = import_node_path7.default.relative(root, fullPath);
@@ -11627,6 +11644,111 @@ function resolveWorkspacePath(cwd, filePath) {
11627
11644
  }
11628
11645
  return resolved;
11629
11646
  }
11647
+ var READ_ONLY_SHELL_PREFIXES = [
11648
+ "pwd",
11649
+ "ls",
11650
+ "find ",
11651
+ "rg ",
11652
+ "grep ",
11653
+ "cat ",
11654
+ "sed ",
11655
+ "head ",
11656
+ "tail ",
11657
+ "git status",
11658
+ "git diff",
11659
+ "git rev-parse",
11660
+ "git branch",
11661
+ "git log",
11662
+ "git show",
11663
+ "npm ls",
11664
+ "pnpm ls",
11665
+ "yarn list"
11666
+ ];
11667
+ var DESTRUCTIVE_SHELL_PATTERNS = [
11668
+ /\brm\s+-rf\b/i,
11669
+ /\brm\s+-fr\b/i,
11670
+ /\bgit\s+reset\s+--hard\b/i,
11671
+ /\bgit\s+clean\s+-f(d|x|fd|fdx)\b/i,
11672
+ /\bgit\s+checkout\s+--\b/i,
11673
+ /\bmv\s+.+\s+\/dev\/null\b/i,
11674
+ /\bdd\s+if=/i,
11675
+ /\bmkfs(\.| )/i,
11676
+ /\bsudo\b/i
11677
+ ];
11678
+ var PROTECTED_WRITE_PREFIXES = [
11679
+ ".git",
11680
+ ".kimbho/config.json",
11681
+ ".kimbho/sessions",
11682
+ ".kimbho/plans"
11683
+ ];
11684
+ function isReadOnlyShellCommand2(command) {
11685
+ const normalized = command.trim().toLowerCase();
11686
+ return READ_ONLY_SHELL_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(prefix));
11687
+ }
11688
+ function isDestructiveShellCommand(command) {
11689
+ return DESTRUCTIVE_SHELL_PATTERNS.some((pattern) => pattern.test(command));
11690
+ }
11691
+ function normalizeArtifactRelativePath(cwd, absolutePath) {
11692
+ const relative = import_node_path9.default.relative(cwd, absolutePath) || import_node_path9.default.basename(absolutePath);
11693
+ return relative.replace(/\\/g, "/");
11694
+ }
11695
+ function isProtectedWritePath(relativePath) {
11696
+ return PROTECTED_WRITE_PREFIXES.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}/`));
11697
+ }
11698
+ function extractProspectiveWriteTargets(toolId, input, cwd) {
11699
+ if (toolId === "file.write" && typeof input.path === "string") {
11700
+ return [
11701
+ normalizeArtifactRelativePath(cwd, resolveWorkspacePath(cwd, input.path))
11702
+ ];
11703
+ }
11704
+ if (toolId === "file.patch" && typeof input.patch === "string") {
11705
+ return extractPatchArtifacts(cwd, input.patch).map((artifactPath) => normalizeArtifactRelativePath(cwd, artifactPath));
11706
+ }
11707
+ if (toolId === "scaffold.generate") {
11708
+ return [
11709
+ "(scaffold workspace)"
11710
+ ];
11711
+ }
11712
+ return [];
11713
+ }
11714
+ function createPolicyFailure(toolId, reason) {
11715
+ return ToolResultSchema.parse({
11716
+ toolId,
11717
+ success: false,
11718
+ summary: reason,
11719
+ stderr: reason,
11720
+ artifacts: []
11721
+ });
11722
+ }
11723
+ function enforceToolPolicy(toolId, input, descriptor, context) {
11724
+ if (!descriptor) {
11725
+ return null;
11726
+ }
11727
+ const sandboxMode = context.sandboxMode ?? "workspace-write";
11728
+ const approvalMode = context.approvalMode ?? "manual";
11729
+ const command = typeof input.command === "string" ? input.command : "";
11730
+ if (sandboxMode === "read-only") {
11731
+ if (toolId === "shell.exec" && isReadOnlyShellCommand2(command)) {
11732
+ return null;
11733
+ }
11734
+ if (descriptor.permission !== "safe") {
11735
+ return createPolicyFailure(toolId, `Blocked ${toolId} because sandbox mode is read-only.`);
11736
+ }
11737
+ }
11738
+ if (toolId === "shell.exec" && isDestructiveShellCommand(command)) {
11739
+ if (!context.operatorApproved && approvalMode !== "auto") {
11740
+ return createPolicyFailure(toolId, `Blocked destructive shell command under manual approval mode: ${command}`);
11741
+ }
11742
+ }
11743
+ if ((toolId === "file.write" || toolId === "file.patch") && sandboxMode === "workspace-write") {
11744
+ const targets = extractProspectiveWriteTargets(toolId, input, context.cwd);
11745
+ const protectedTargets = targets.filter((target) => isProtectedWritePath(target));
11746
+ if (protectedTargets.length > 0) {
11747
+ return createPolicyFailure(toolId, `Blocked write to protected workspace paths: ${protectedTargets.join(", ")}`);
11748
+ }
11749
+ }
11750
+ return null;
11751
+ }
11630
11752
  async function runSpawn(command, args, cwd, timeoutMs, signal) {
11631
11753
  return new Promise((resolve, reject) => {
11632
11754
  if (signal?.aborted) {
@@ -12447,6 +12569,10 @@ var ToolRuntime = class {
12447
12569
  if (!executor) {
12448
12570
  throw new Error(`No executor registered for "${toolId}".`);
12449
12571
  }
12572
+ const policyFailure = enforceToolPolicy(toolId, input, descriptor, context);
12573
+ if (policyFailure) {
12574
+ return policyFailure;
12575
+ }
12450
12576
  return executor(input, context, descriptor.timeoutMs);
12451
12577
  }
12452
12578
  };
@@ -12747,6 +12873,66 @@ async function cleanupWorktree(cwd, worktreePath, signal) {
12747
12873
  force: true
12748
12874
  });
12749
12875
  }
12876
+ async function writeConflictBundle(input) {
12877
+ await ensureKimbhoDir(input.mainCwd);
12878
+ const logsDir = import_node_path10.default.join(resolveKimbhoDir(input.mainCwd), "logs");
12879
+ const baseName = import_node_path10.default.basename(input.originalPatchArtifactPath, ".patch");
12880
+ const outputPath = import_node_path10.default.join(logsDir, `${baseName}-integration-conflict.md`);
12881
+ const lines = [
12882
+ "# Integration Conflict",
12883
+ "",
12884
+ `- summary: ${input.summary}`,
12885
+ `- original patch: ${input.originalPatchArtifactPath}`,
12886
+ ...input.refreshedPatchArtifactPath ? [
12887
+ `- refreshed patch: ${input.refreshedPatchArtifactPath}`
12888
+ ] : [],
12889
+ `- changed files: ${input.changedFiles.length > 0 ? input.changedFiles.join(", ") : "(unknown)"}`,
12890
+ "",
12891
+ "## Notes",
12892
+ ...input.notes.map((note) => `- ${note}`),
12893
+ ""
12894
+ ];
12895
+ if (input.directApplyStdout || input.directApplyStderr) {
12896
+ lines.push("## Direct Apply");
12897
+ if (input.directApplyStdout) {
12898
+ lines.push("");
12899
+ lines.push("```text");
12900
+ lines.push(input.directApplyStdout);
12901
+ lines.push("```");
12902
+ }
12903
+ if (input.directApplyStderr) {
12904
+ lines.push("");
12905
+ lines.push("```text");
12906
+ lines.push(input.directApplyStderr);
12907
+ lines.push("```");
12908
+ }
12909
+ lines.push("");
12910
+ }
12911
+ if (input.replayStdout || input.replayStderr) {
12912
+ lines.push("## Replay Attempt");
12913
+ if (input.replayStdout) {
12914
+ lines.push("");
12915
+ lines.push("```text");
12916
+ lines.push(input.replayStdout);
12917
+ lines.push("```");
12918
+ }
12919
+ if (input.replayStderr) {
12920
+ lines.push("");
12921
+ lines.push("```text");
12922
+ lines.push(input.replayStderr);
12923
+ lines.push("```");
12924
+ }
12925
+ lines.push("");
12926
+ }
12927
+ lines.push("## Operator Next Steps");
12928
+ lines.push("- Inspect the original patch and the refreshed patch if present.");
12929
+ lines.push("- Compare the listed changed files against the current workspace diff.");
12930
+ lines.push("- Resolve the conflict manually or re-run the task after adjusting overlapping files.");
12931
+ lines.push("");
12932
+ await (0, import_promises10.writeFile)(outputPath, `${lines.join("\n")}
12933
+ `, "utf8");
12934
+ return outputPath;
12935
+ }
12750
12936
  async function readTextFileIfPresent(filePath) {
12751
12937
  return (0, import_promises10.readFile)(filePath, "utf8").catch((error) => {
12752
12938
  if (error.code === "ENOENT") {
@@ -12848,6 +13034,16 @@ async function createReplayWorktree(cwd, basePatchArtifactPath, signal) {
12848
13034
  async function replayPatchOnLatestWorkspace(mainCwd, sourceWorktreePath, originalPatchArtifactPath, changedFilesSummary, signal) {
12849
13035
  const replayWorktree = await createReplayWorktree(mainCwd, originalPatchArtifactPath, signal);
12850
13036
  if (!replayWorktree) {
13037
+ const notes2 = [
13038
+ `Replay sandbox creation failed. Original patch preserved at ${originalPatchArtifactPath}.`
13039
+ ];
13040
+ const conflictArtifactPath = await writeConflictBundle({
13041
+ mainCwd,
13042
+ originalPatchArtifactPath,
13043
+ changedFiles: changedFilesSummary ? changedFilesSummary.split(", ").map((item) => item.replace(/ \(\+\d+ more\)$/, "")) : [],
13044
+ notes: notes2,
13045
+ summary: changedFilesSummary ? `Failed to replay the isolated worktree diff because no git replay sandbox could be created. Files involved: ${changedFilesSummary}.` : "Failed to replay the isolated worktree diff because no git replay sandbox could be created."
13046
+ });
12851
13047
  return {
12852
13048
  toolResults: [
12853
13049
  {
@@ -12855,16 +13051,16 @@ async function replayPatchOnLatestWorkspace(mainCwd, sourceWorktreePath, origina
12855
13051
  success: false,
12856
13052
  summary: changedFilesSummary ? `Failed to replay the isolated worktree diff because no git replay sandbox could be created. Files involved: ${changedFilesSummary}.` : "Failed to replay the isolated worktree diff because no git replay sandbox could be created.",
12857
13053
  artifacts: [
12858
- originalPatchArtifactPath
13054
+ originalPatchArtifactPath,
13055
+ conflictArtifactPath
12859
13056
  ]
12860
13057
  }
12861
13058
  ],
12862
13059
  artifacts: [
12863
- originalPatchArtifactPath
12864
- ],
12865
- notes: [
12866
- `Replay sandbox creation failed. Original patch preserved at ${originalPatchArtifactPath}.`
13060
+ originalPatchArtifactPath,
13061
+ conflictArtifactPath
12867
13062
  ],
13063
+ notes: notes2,
12868
13064
  integrationFailed: true
12869
13065
  };
12870
13066
  }
@@ -12887,16 +13083,34 @@ async function replayPatchOnLatestWorkspace(mainCwd, sourceWorktreePath, origina
12887
13083
  if (replayApplyResult.code !== 0) {
12888
13084
  const textualReplay = await attemptTextualReplayMerge(mainCwd, replayWorktree.worktreePath, sourceWorktreePath, changedFiles, signal);
12889
13085
  if (!textualReplay.success) {
13086
+ const failureSummary = changedFilesSummary ? `Failed to replay the isolated worktree diff onto the latest workspace snapshot. Files involved: ${changedFilesSummary}.` : "Failed to replay the isolated worktree diff onto the latest workspace snapshot.";
13087
+ const failureNotes = [
13088
+ ...notes,
13089
+ textualReplay.note,
13090
+ `Replay failed in the retry sandbox. Original patch preserved at ${originalPatchArtifactPath}.`
13091
+ ];
13092
+ const conflictArtifactPath = await writeConflictBundle({
13093
+ mainCwd,
13094
+ originalPatchArtifactPath,
13095
+ changedFiles,
13096
+ notes: failureNotes,
13097
+ summary: failureSummary,
13098
+ directApplyStdout: replayApplyResult.stdout,
13099
+ directApplyStderr: replayApplyResult.stderr,
13100
+ replayStdout: textualReplay.stdout,
13101
+ replayStderr: textualReplay.stderr
13102
+ });
13103
+ artifacts.add(conflictArtifactPath);
12890
13104
  toolResults.push({
12891
13105
  toolId: "file.patch",
12892
13106
  success: false,
12893
- summary: changedFilesSummary ? `Failed to replay the isolated worktree diff onto the latest workspace snapshot. Files involved: ${changedFilesSummary}.` : "Failed to replay the isolated worktree diff onto the latest workspace snapshot.",
13107
+ summary: failureSummary,
12894
13108
  stdout: replayApplyResult.stdout || textualReplay.stdout,
12895
13109
  stderr: replayApplyResult.stderr || textualReplay.stderr,
12896
13110
  artifacts: Array.from(artifacts)
12897
13111
  });
12898
13112
  notes.push(textualReplay.note);
12899
- notes.push(`Replay failed in the retry sandbox. Original patch preserved at ${originalPatchArtifactPath}.`);
13113
+ notes.push(`Replay failed in the retry sandbox. Original patch preserved at ${originalPatchArtifactPath}. Conflict bundle saved to ${conflictArtifactPath}.`);
12900
13114
  return {
12901
13115
  toolResults,
12902
13116
  artifacts: Array.from(artifacts),
@@ -12948,15 +13162,31 @@ async function replayPatchOnLatestWorkspace(mainCwd, sourceWorktreePath, origina
12948
13162
  replayWorktree.patchArtifactPath
12949
13163
  ], signal);
12950
13164
  if (replayCheckResult.code !== 0) {
13165
+ const failureSummary = changedFilesSummary ? `Replayed patch still does not apply cleanly to the latest main workspace. Files involved: ${changedFilesSummary}.` : "Replayed patch still does not apply cleanly to the latest main workspace.";
13166
+ const failureNotes = [
13167
+ ...notes,
13168
+ `Replay generated a refreshed patch, but it still does not apply cleanly. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}.`
13169
+ ];
13170
+ const conflictArtifactPath = await writeConflictBundle({
13171
+ mainCwd,
13172
+ originalPatchArtifactPath,
13173
+ changedFiles,
13174
+ notes: failureNotes,
13175
+ summary: failureSummary,
13176
+ replayStdout: replayCheckResult.stdout,
13177
+ replayStderr: replayCheckResult.stderr,
13178
+ refreshedPatchArtifactPath: replayWorktree.patchArtifactPath
13179
+ });
13180
+ artifacts.add(conflictArtifactPath);
12951
13181
  toolResults.push({
12952
13182
  toolId: "file.patch",
12953
13183
  success: false,
12954
- summary: changedFilesSummary ? `Replayed patch still does not apply cleanly to the latest main workspace. Files involved: ${changedFilesSummary}.` : "Replayed patch still does not apply cleanly to the latest main workspace.",
13184
+ summary: failureSummary,
12955
13185
  stdout: replayCheckResult.stdout,
12956
13186
  stderr: replayCheckResult.stderr,
12957
13187
  artifacts: Array.from(artifacts)
12958
13188
  });
12959
- notes.push(`Replay generated a refreshed patch, but it still does not apply cleanly. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}.`);
13189
+ notes.push(`Replay generated a refreshed patch, but it still does not apply cleanly. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}. Conflict bundle saved to ${conflictArtifactPath}.`);
12960
13190
  return {
12961
13191
  toolResults,
12962
13192
  artifacts: Array.from(artifacts),
@@ -12972,15 +13202,31 @@ async function replayPatchOnLatestWorkspace(mainCwd, sourceWorktreePath, origina
12972
13202
  replayWorktree.patchArtifactPath
12973
13203
  ], signal);
12974
13204
  if (replayMainApplyResult.code !== 0) {
13205
+ const failureSummary = changedFilesSummary ? `Failed to apply the refreshed replay patch back to the main workspace. Files involved: ${changedFilesSummary}.` : "Failed to apply the refreshed replay patch back to the main workspace.";
13206
+ const failureNotes = [
13207
+ ...notes,
13208
+ `Replay patch could not be applied back to the main workspace. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}.`
13209
+ ];
13210
+ const conflictArtifactPath = await writeConflictBundle({
13211
+ mainCwd,
13212
+ originalPatchArtifactPath,
13213
+ changedFiles,
13214
+ notes: failureNotes,
13215
+ summary: failureSummary,
13216
+ replayStdout: replayMainApplyResult.stdout,
13217
+ replayStderr: replayMainApplyResult.stderr,
13218
+ refreshedPatchArtifactPath: replayWorktree.patchArtifactPath
13219
+ });
13220
+ artifacts.add(conflictArtifactPath);
12975
13221
  toolResults.push({
12976
13222
  toolId: "file.patch",
12977
13223
  success: false,
12978
- summary: changedFilesSummary ? `Failed to apply the refreshed replay patch back to the main workspace. Files involved: ${changedFilesSummary}.` : "Failed to apply the refreshed replay patch back to the main workspace.",
13224
+ summary: failureSummary,
12979
13225
  stdout: replayMainApplyResult.stdout,
12980
13226
  stderr: replayMainApplyResult.stderr,
12981
13227
  artifacts: Array.from(artifacts)
12982
13228
  });
12983
- notes.push(`Replay patch could not be applied back to the main workspace. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}.`);
13229
+ notes.push(`Replay patch could not be applied back to the main workspace. Refreshed patch preserved at ${replayWorktree.patchArtifactPath}. Conflict bundle saved to ${conflictArtifactPath}.`);
12984
13230
  return {
12985
13231
  toolResults,
12986
13232
  artifacts: Array.from(artifacts),
@@ -13574,6 +13820,11 @@ var ExecutionOrchestrator = class {
13574
13820
  }
13575
13821
  const scaffoldResult = await this.safeRunTool("scaffold.generate", scaffoldInput, {
13576
13822
  cwd: request.cwd,
13823
+ ...config ? {
13824
+ approvalMode: config.approvalMode,
13825
+ sandboxMode: config.sandboxMode,
13826
+ agentRole: task.agentRole
13827
+ } : {},
13577
13828
  ...options.signal ? {
13578
13829
  signal: options.signal
13579
13830
  } : {}
@@ -13608,6 +13859,11 @@ var ExecutionOrchestrator = class {
13608
13859
  }
13609
13860
  const repoIndexResult = await this.safeRunTool("repo.index", {}, {
13610
13861
  cwd: request.cwd,
13862
+ ...config ? {
13863
+ approvalMode: config.approvalMode,
13864
+ sandboxMode: config.sandboxMode,
13865
+ agentRole: task.agentRole
13866
+ } : {},
13611
13867
  ...options.signal ? {
13612
13868
  signal: options.signal
13613
13869
  } : {}
@@ -13727,7 +13983,9 @@ var ExecutionOrchestrator = class {
13727
13983
  async executeRepoAnalysisTask(sessionId, task, request, emitProgress, signal) {
13728
13984
  const context = { cwd: request.cwd };
13729
13985
  const toolResults = [];
13730
- const probes = [
13986
+ const probes = request.workspaceState === "empty" ? [
13987
+ { toolId: "repo.index", input: {} }
13988
+ ] : [
13731
13989
  { toolId: "repo.index", input: {} },
13732
13990
  { toolId: "git.status", input: {} },
13733
13991
  { toolId: "file.read", input: { path: "package.json" } },
@@ -14420,7 +14678,6 @@ function createModelsCommand() {
14420
14678
  }
14421
14679
 
14422
14680
  // src/commands/plan.ts
14423
- var import_promises12 = require("node:fs/promises");
14424
14681
  var import_node_process7 = __toESM(require("node:process"), 1);
14425
14682
 
14426
14683
  // ../planner/dist/planner.js
@@ -14429,6 +14686,9 @@ function normalizeGoal(goal) {
14429
14686
  }
14430
14687
  function inferProjectShape(goal) {
14431
14688
  const lower = goal.toLowerCase();
14689
+ if (lower.includes("landing page") || lower.includes("homepage") || lower.includes("marketing page") || lower.includes("storefront") || lower.includes("restaurant website") || lower.includes("restaurant site") || lower.includes("static site")) {
14690
+ return "static-site";
14691
+ }
14432
14692
  if (lower.includes("agent") || lower.includes("cli") || lower.includes("terminal")) {
14433
14693
  return "cli-agent";
14434
14694
  }
@@ -14453,7 +14713,7 @@ function buildTask(id, title, description, agentRole, type, dependsOn, acceptanc
14453
14713
  };
14454
14714
  }
14455
14715
  function foundationMilestone(shape) {
14456
- const objective = shape === "cli-agent" ? "Establish the CLI monorepo, shared contracts, and first operational commands." : "Establish the application structure, baseline tooling, and implementation scaffolding.";
14716
+ const objective = shape === "cli-agent" ? "Establish the CLI monorepo, shared contracts, and first operational commands." : shape === "static-site" ? "Establish the site structure, initial content direction, and deterministic scaffold." : "Establish the application structure, baseline tooling, and implementation scaffolding.";
14457
14717
  return {
14458
14718
  id: "m1-foundation",
14459
14719
  title: "Foundation",
@@ -14498,6 +14758,27 @@ function foundationMilestone(shape) {
14498
14758
  };
14499
14759
  }
14500
14760
  function implementationMilestone(shape) {
14761
+ if (shape === "static-site") {
14762
+ return {
14763
+ id: "m2-experience",
14764
+ title: "Experience",
14765
+ objective: "Refine the landing-page experience, content, and responsive presentation.",
14766
+ tasks: [
14767
+ buildTask("t4-experience", "Tailor the landing-page experience", "Customize the generated landing page so the layout, copy, and visual direction fit the requested business or venue.", "frontend-specialist", "implementation", [
14768
+ "t3-scaffold"
14769
+ ], [
14770
+ "The page reflects the requested business domain and tone.",
14771
+ "The layout is responsive and visually coherent."
14772
+ ], [
14773
+ "Customized landing page"
14774
+ ], [
14775
+ "index.html",
14776
+ "styles.css",
14777
+ "src/"
14778
+ ], "medium")
14779
+ ]
14780
+ };
14781
+ }
14501
14782
  if (shape === "cli-agent") {
14502
14783
  return {
14503
14784
  id: "m2-runtime",
@@ -14614,6 +14895,8 @@ function verificationMilestone(shape) {
14614
14895
  "t6-runtime",
14615
14896
  "t7-tools",
14616
14897
  "t8-shell"
14898
+ ] : shape === "static-site" ? [
14899
+ "t4-experience"
14617
14900
  ] : [
14618
14901
  "t5-backend",
14619
14902
  "t6-frontend"
@@ -14656,6 +14939,9 @@ function deriveAssumptions(shape, request) {
14656
14939
  if (shape === "full-stack-app") {
14657
14940
  assumptions.push("The product needs a frontend, backend, and persistence layer.");
14658
14941
  }
14942
+ if (shape === "static-site") {
14943
+ assumptions.push("The initial deliverable can ship as a static site without backend infrastructure.");
14944
+ }
14659
14945
  if (request.constraints.length > 0) {
14660
14946
  assumptions.push(`The plan must honor ${request.constraints.length} explicit user constraints.`);
14661
14947
  }
@@ -14676,6 +14962,13 @@ function deriveOpenQuestions(shape) {
14676
14962
  "What external services or APIs must be integrated?"
14677
14963
  ];
14678
14964
  }
14965
+ if (shape === "static-site") {
14966
+ return [
14967
+ "What brand voice and visual direction should the page follow?",
14968
+ "Which sections are required on the first version of the landing page?",
14969
+ "What primary call to action should the page drive?"
14970
+ ];
14971
+ }
14679
14972
  return [
14680
14973
  "Which stack and deployment target should be preferred?",
14681
14974
  "What user journeys count as the minimum viable release?"
@@ -14689,6 +14982,9 @@ function deriveVerificationChecklist(shape) {
14689
14982
  if (shape === "cli-agent") {
14690
14983
  checklist.push("CLI commands render correctly and save plan artifacts.");
14691
14984
  checklist.push("Plan contracts remain schema-valid across packages.");
14985
+ } else if (shape === "static-site") {
14986
+ checklist.push("The page renders cleanly on desktop and mobile-sized viewports.");
14987
+ checklist.push("Primary calls to action and anchor navigation work.");
14692
14988
  } else if (shape === "full-stack-app") {
14693
14989
  checklist.push("The build, lint, and test flows pass.");
14694
14990
  checklist.push("Primary UI and API paths are smoke-tested.");
@@ -14702,6 +14998,9 @@ function buildSummary(shape, goal) {
14702
14998
  if (shape === "full-stack-app") {
14703
14999
  return `Deliver the full-stack implementation for "${goal}" through staged architecture, implementation, and verification milestones.`;
14704
15000
  }
15001
+ if (shape === "static-site") {
15002
+ return `Deliver a polished landing-page implementation for "${goal}" through scaffold, refinement, and verification milestones.`;
15003
+ }
14705
15004
  return `Deliver "${goal}" through structured analysis, implementation, and verification milestones.`;
14706
15005
  }
14707
15006
  function createPlan(input) {
@@ -14734,12 +15033,176 @@ function createPlan(input) {
14734
15033
  });
14735
15034
  }
14736
15035
 
14737
- // src/commands/plan.ts
15036
+ // src/workspace.ts
15037
+ var import_promises12 = require("node:fs/promises");
15038
+ var import_node_path12 = __toESM(require("node:path"), 1);
15039
+ var PROJECT_MARKERS = /* @__PURE__ */ new Set([
15040
+ ".git",
15041
+ "package.json",
15042
+ "tsconfig.json",
15043
+ "pnpm-workspace.yaml",
15044
+ "turbo.json",
15045
+ "vite.config.ts",
15046
+ "vite.config.js",
15047
+ "next.config.ts",
15048
+ "next.config.js",
15049
+ "src",
15050
+ "app",
15051
+ "pages",
15052
+ "prisma",
15053
+ "pyproject.toml",
15054
+ "Cargo.toml",
15055
+ "go.mod"
15056
+ ]);
15057
+ var GREENFIELD_KEYWORDS = [
15058
+ "build",
15059
+ "create",
15060
+ "make",
15061
+ "scaffold",
15062
+ "generate",
15063
+ "new app",
15064
+ "new project",
15065
+ "landing page",
15066
+ "homepage",
15067
+ "marketing page",
15068
+ "storefront",
15069
+ "website"
15070
+ ];
15071
+ var EXISTING_REPO_PATTERNS = [
15072
+ /\bthis repo\b/i,
15073
+ /\bthis repository\b/i,
15074
+ /\bthis project\b/i,
15075
+ /\bthis codebase\b/i,
15076
+ /\bcurrent repo\b/i,
15077
+ /\bcurrent project\b/i,
15078
+ /\bexisting repo\b/i,
15079
+ /\bexisting project\b/i,
15080
+ /\bin this repo\b/i,
15081
+ /\bin this project\b/i
15082
+ ];
15083
+ var WORKSPACE_NAME_STOP_WORDS = /* @__PURE__ */ new Set([
15084
+ "a",
15085
+ "an",
15086
+ "and",
15087
+ "app",
15088
+ "application",
15089
+ "build",
15090
+ "create",
15091
+ "for",
15092
+ "generate",
15093
+ "make",
15094
+ "new",
15095
+ "page",
15096
+ "please",
15097
+ "project",
15098
+ "scaffold",
15099
+ "site",
15100
+ "that",
15101
+ "the",
15102
+ "to",
15103
+ "website",
15104
+ "with"
15105
+ ]);
15106
+ async function readEntries(cwd) {
15107
+ try {
15108
+ return await (0, import_promises12.readdir)(cwd);
15109
+ } catch {
15110
+ return [];
15111
+ }
15112
+ }
15113
+ async function readMeaningfulEntries(cwd) {
15114
+ const entries = await readEntries(cwd);
15115
+ return entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
15116
+ }
15117
+ async function isProjectWorkspace(cwd) {
15118
+ const entries = await readEntries(cwd);
15119
+ return entries.some((entry) => PROJECT_MARKERS.has(entry));
15120
+ }
15121
+ function isGreenfieldGoal(goal) {
15122
+ const normalized = goal.trim().toLowerCase();
15123
+ if (!normalized || EXISTING_REPO_PATTERNS.some((pattern) => pattern.test(normalized))) {
15124
+ return false;
15125
+ }
15126
+ return GREENFIELD_KEYWORDS.some((keyword) => normalized.includes(keyword));
15127
+ }
15128
+ function sanitizeWorkspaceName(value) {
15129
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-");
15130
+ }
15131
+ function inferWorkspaceName(goal) {
15132
+ const normalized = goal.trim().toLowerCase();
15133
+ const trailingContext = normalized.split(/\bfor\b/).at(-1)?.trim() ?? normalized;
15134
+ const contextWords = trailingContext.split(/\s+/).map((word) => sanitizeWorkspaceName(word)).filter((word) => word.length > 0 && !WORKSPACE_NAME_STOP_WORDS.has(word));
15135
+ const goalWords = normalized.split(/\s+/).map((word) => sanitizeWorkspaceName(word)).filter((word) => word.length > 0 && !WORKSPACE_NAME_STOP_WORDS.has(word));
15136
+ const preferred = [
15137
+ ...contextWords,
15138
+ ...goalWords
15139
+ ];
15140
+ const deduped = Array.from(new Set(preferred)).slice(0, 4);
15141
+ const candidate = sanitizeWorkspaceName(deduped.join("-"));
15142
+ return candidate.length > 0 ? candidate : "kimbho-app";
15143
+ }
15144
+ async function chooseWorkspacePath(baseCwd, workspaceName) {
15145
+ const basePath = import_node_path12.default.join(baseCwd, workspaceName);
15146
+ let attempt = 0;
15147
+ while (attempt < 50) {
15148
+ const suffix = attempt === 0 ? "" : `-${attempt + 1}`;
15149
+ const candidatePath = `${basePath}${suffix}`;
15150
+ const entries = await readMeaningfulEntries(candidatePath);
15151
+ if (entries.length === 0) {
15152
+ return candidatePath;
15153
+ }
15154
+ attempt += 1;
15155
+ }
15156
+ return `${basePath}-${Date.now()}`;
15157
+ }
14738
15158
  async function detectWorkspaceState(cwd) {
14739
- const entries = await (0, import_promises12.readdir)(cwd);
14740
- const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
15159
+ if (await isProjectWorkspace(cwd)) {
15160
+ return "existing";
15161
+ }
15162
+ const meaningfulEntries = await readMeaningfulEntries(cwd);
14741
15163
  return meaningfulEntries.length === 0 ? "empty" : "existing";
14742
15164
  }
15165
+ async function inferPlanningWorkspaceState(cwd, goal) {
15166
+ const existingState = await detectWorkspaceState(cwd);
15167
+ if (existingState === "empty" || await isProjectWorkspace(cwd) || !isGreenfieldGoal(goal)) {
15168
+ return existingState;
15169
+ }
15170
+ return "empty";
15171
+ }
15172
+ async function resolveExecutionWorkspace(cwd, goal) {
15173
+ const workspaceState = await detectWorkspaceState(cwd);
15174
+ if (workspaceState === "empty" || await isProjectWorkspace(cwd) || !isGreenfieldGoal(goal)) {
15175
+ return {
15176
+ cwd,
15177
+ workspaceState,
15178
+ notes: []
15179
+ };
15180
+ }
15181
+ const workspaceName = inferWorkspaceName(goal);
15182
+ const targetCwd = await chooseWorkspacePath(cwd, workspaceName);
15183
+ await (0, import_promises12.mkdir)(targetCwd, { recursive: true });
15184
+ const notes = [
15185
+ `Created workspace ${targetCwd} for this greenfield build because ${cwd} is not a project workspace.`
15186
+ ];
15187
+ let inheritedConfigPath;
15188
+ const sourceConfig = await loadConfig(cwd);
15189
+ const targetConfig = await loadConfig(targetCwd);
15190
+ if (sourceConfig && !targetConfig) {
15191
+ inheritedConfigPath = await saveConfig(sourceConfig, targetCwd);
15192
+ notes.push(`Copied the active Kimbho config into ${targetCwd} so model/provider settings carry over.`);
15193
+ }
15194
+ return {
15195
+ cwd: targetCwd,
15196
+ workspaceState: await detectWorkspaceState(targetCwd),
15197
+ createdWorkspacePath: targetCwd,
15198
+ ...inheritedConfigPath ? {
15199
+ inheritedConfigPath
15200
+ } : {},
15201
+ notes
15202
+ };
15203
+ }
15204
+
15205
+ // src/commands/plan.ts
14743
15206
  function createPlanCommand() {
14744
15207
  return new Command("plan").description("Generate a structured Kimbho execution plan for the provided goal.").argument("<goal>", "High-level product or engineering goal").option("--stack <stack>", "Preferred stack or preset").option(
14745
15208
  "--constraint <constraint>",
@@ -14752,7 +15215,7 @@ function createPlanCommand() {
14752
15215
  goal,
14753
15216
  mode: "plan",
14754
15217
  cwd,
14755
- workspaceState: await detectWorkspaceState(cwd),
15218
+ workspaceState: await inferPlanningWorkspaceState(cwd, goal),
14756
15219
  stackPreference: options.stack,
14757
15220
  constraints: options.constraint
14758
15221
  };
@@ -14891,7 +15354,7 @@ function createProvidersCommand() {
14891
15354
 
14892
15355
  // src/commands/review.ts
14893
15356
  var import_promises13 = require("node:fs/promises");
14894
- var import_node_path12 = __toESM(require("node:path"), 1);
15357
+ var import_node_path13 = __toESM(require("node:path"), 1);
14895
15358
  var import_node_process9 = __toESM(require("node:process"), 1);
14896
15359
  function summarizeChangedFiles2(diff) {
14897
15360
  const files = /* @__PURE__ */ new Set();
@@ -14975,7 +15438,7 @@ ${diff.stdout}`
14975
15438
  }
14976
15439
  }
14977
15440
  await ensureKimbhoDir(cwd);
14978
- const artifactPath = import_node_path12.default.join(resolveKimbhoDir(cwd), "logs", `review-${Date.now()}.md`);
15441
+ const artifactPath = import_node_path13.default.join(resolveKimbhoDir(cwd), "logs", `review-${Date.now()}.md`);
14979
15442
  await (0, import_promises13.writeFile)(artifactPath, [
14980
15443
  `# Review`,
14981
15444
  ``,
@@ -15030,7 +15493,7 @@ function parseCount(value) {
15030
15493
  return parsed;
15031
15494
  }
15032
15495
  function createResumeCommand() {
15033
- return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).option("--execute", "Continue auto-executing the ready-task frontier", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount, 2).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount, 2).action(async (options) => {
15496
+ return new Command("resume").description("Resume the latest saved Kimbho session snapshot.").option("--json", "Print the latest session as JSON", false).option("--execute", "Continue auto-executing the ready-task frontier", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount, 3).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount, 2).action(async (options) => {
15034
15497
  const cwd = import_node_process10.default.cwd();
15035
15498
  const session = await loadLatestSession(cwd);
15036
15499
  if (!session) {
@@ -15055,13 +15518,7 @@ function createResumeCommand() {
15055
15518
  }
15056
15519
 
15057
15520
  // src/commands/run.ts
15058
- var import_promises14 = require("node:fs/promises");
15059
15521
  var import_node_process11 = __toESM(require("node:process"), 1);
15060
- async function detectWorkspaceState2(cwd) {
15061
- const entries = await (0, import_promises14.readdir)(cwd);
15062
- const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
15063
- return meaningfulEntries.length === 0 ? "empty" : "existing";
15064
- }
15065
15522
  function parseCount2(value) {
15066
15523
  const parsed = Number(value);
15067
15524
  if (!Number.isInteger(parsed) || parsed <= 0) {
@@ -15075,7 +15532,7 @@ function createRunCommand() {
15075
15532
  "Explicit execution constraint; can be provided multiple times",
15076
15533
  (value, previous = []) => [...previous, value],
15077
15534
  []
15078
- ).option("--json", "Print the session snapshot as JSON", false).option("--snapshot-only", "Create the session without auto-executing ready tasks", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount2, 2).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount2, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount2, 2).action(async (goal, options) => {
15535
+ ).option("--json", "Print the session snapshot as JSON", false).option("--snapshot-only", "Create the session without auto-executing ready tasks", false).option("--max-auto-tasks <count>", "Maximum ready tasks to auto-execute", parseCount2, 3).option("--max-agent-steps <count>", "Maximum tool/model steps per autonomous task", parseCount2, 8).option("--max-repair-attempts <count>", "Maximum failed verification cycles per autonomous task", parseCount2, 2).action(async (goal, options) => {
15079
15536
  const cwd = import_node_process11.default.cwd();
15080
15537
  const orchestrator = new ExecutionOrchestrator();
15081
15538
  let request;
@@ -15087,22 +15544,26 @@ function createRunCommand() {
15087
15544
  return;
15088
15545
  }
15089
15546
  if (goal) {
15547
+ const workspace = await resolveExecutionWorkspace(cwd, goal);
15090
15548
  request = {
15091
15549
  goal,
15092
15550
  mode: "run",
15093
- cwd,
15094
- workspaceState: await detectWorkspaceState2(cwd),
15551
+ cwd: workspace.cwd,
15552
+ workspaceState: workspace.workspaceState,
15095
15553
  stackPreference: options.stack,
15096
15554
  constraints: options.constraint
15097
15555
  };
15098
15556
  plan = createPlan(request);
15099
- planPath = await savePlan(plan, cwd);
15557
+ planPath = await savePlan(plan, workspace.cwd);
15558
+ for (const note of workspace.notes) {
15559
+ console.log(note);
15560
+ }
15100
15561
  } else {
15101
15562
  request = {
15102
15563
  goal: plan.goal,
15103
15564
  mode: "run",
15104
15565
  cwd,
15105
- workspaceState: await detectWorkspaceState2(cwd),
15566
+ workspaceState: plan.repoStrategy.mode === "greenfield" ? "empty" : "existing",
15106
15567
  stackPreference: options.stack,
15107
15568
  constraints: options.constraint
15108
15569
  };
@@ -15114,7 +15575,7 @@ function createRunCommand() {
15114
15575
  maxAgentSteps: options.maxAgentSteps,
15115
15576
  maxRepairAttempts: options.maxRepairAttempts
15116
15577
  });
15117
- const sessionPath = await saveSession(snapshot, cwd);
15578
+ const sessionPath = await saveSession(snapshot, request.cwd);
15118
15579
  if (options.json) {
15119
15580
  console.log(
15120
15581
  renderJson({
@@ -15133,6 +15594,9 @@ function createRunCommand() {
15133
15594
  console.log(`Saved plan: ${planPath}`);
15134
15595
  }
15135
15596
  console.log(`Saved session: ${sessionPath}`);
15597
+ if (request.cwd !== cwd) {
15598
+ console.log(`Continue in: cd ${request.cwd}`);
15599
+ }
15136
15600
  });
15137
15601
  }
15138
15602
 
@@ -15193,8 +15657,7 @@ function createProgram(onOpenShell) {
15193
15657
  }
15194
15658
 
15195
15659
  // src/shell.ts
15196
- var import_promises15 = require("node:fs/promises");
15197
- var import_promises16 = require("node:readline/promises");
15660
+ var import_promises14 = require("node:readline/promises");
15198
15661
  var import_node_process12 = __toESM(require("node:process"), 1);
15199
15662
  var AMBER = "\x1B[38;5;214m";
15200
15663
  var CYAN = "\x1B[38;5;45m";
@@ -15241,7 +15704,7 @@ var BRAIN_ROLES = [
15241
15704
  "fast"
15242
15705
  ];
15243
15706
  var MAX_CHAT_MESSAGES = 12;
15244
- var DEFAULT_MAX_AUTO_TASKS = 2;
15707
+ var DEFAULT_MAX_AUTO_TASKS = 3;
15245
15708
  var DEFAULT_MAX_AGENT_STEPS = 8;
15246
15709
  var DEFAULT_MAX_REPAIR_ATTEMPTS2 = 2;
15247
15710
  var EXECUTION_PREFIXES = [
@@ -15524,11 +15987,6 @@ function printHeader(cwd, state) {
15524
15987
  console.log(renderHelp());
15525
15988
  console.log("");
15526
15989
  }
15527
- async function detectWorkspaceState3(cwd) {
15528
- const entries = await (0, import_promises15.readdir)(cwd);
15529
- const meaningfulEntries = entries.filter((entry) => entry !== ".kimbho" && !entry.startsWith("."));
15530
- return meaningfulEntries.length === 0 ? "empty" : "existing";
15531
- }
15532
15990
  function inferPromptIntent(input) {
15533
15991
  const normalized = input.trim().toLowerCase();
15534
15992
  if (!normalized) {
@@ -15952,15 +16410,16 @@ async function handleChatPrompt(cwd, prompt, runtime) {
15952
16410
  }
15953
16411
  async function runGoalExecution(cwd, goal, runtime) {
15954
16412
  const orchestrator = new ExecutionOrchestrator();
16413
+ const workspace = await resolveExecutionWorkspace(cwd, goal);
15955
16414
  const request = {
15956
16415
  goal,
15957
16416
  mode: "run",
15958
- cwd,
15959
- workspaceState: await detectWorkspaceState3(cwd),
16417
+ cwd: workspace.cwd,
16418
+ workspaceState: workspace.workspaceState,
15960
16419
  constraints: []
15961
16420
  };
15962
16421
  const plan = createPlan(request);
15963
- const planPath = await savePlan(plan, cwd);
16422
+ const planPath = await savePlan(plan, request.cwd);
15964
16423
  const envelope = orchestrator.buildEnvelope(request, plan);
15965
16424
  const initialSnapshot = orchestrator.createSessionSnapshot(envelope);
15966
16425
  const startedAt = Date.now();
@@ -15980,8 +16439,8 @@ async function runGoalExecution(cwd, goal, runtime) {
15980
16439
  label: goal
15981
16440
  };
15982
16441
  console.log(color(DIM, `Working on: ${goal}`));
15983
- if (cwd === import_node_process12.default.env.HOME) {
15984
- console.log(color(DIM, `warning: current directory is ${cwd}; created files will land here unless you cd into a project folder first.`));
16442
+ for (const note of workspace.notes) {
16443
+ console.log(color(DIM, note));
15985
16444
  }
15986
16445
  console.log(renderShellPlanPreview(plan).join("\n"));
15987
16446
  console.log(renderRunStartCard(liveBoard));
@@ -16019,11 +16478,13 @@ async function runGoalExecution(cwd, goal, runtime) {
16019
16478
  } finally {
16020
16479
  runtime.activeExecution = null;
16021
16480
  }
16022
- const sessionPath = await saveSession(snapshot, cwd);
16481
+ const sessionPath = await saveSession(snapshot, request.cwd);
16482
+ runtime.currentCwd = request.cwd;
16023
16483
  console.log("");
16024
16484
  console.log(color(DIM, `elapsed: ${((Date.now() - startedAt) / 1e3).toFixed(1)}s`));
16025
16485
  console.log(color(DIM, renderExecutionTelemetry(telemetry, startedAt)));
16026
16486
  console.log(renderShellSessionSummary(snapshot, planPath, sessionPath));
16487
+ return request.cwd;
16027
16488
  }
16028
16489
  async function resumeGoalExecution(cwd, runtime) {
16029
16490
  const session = await loadLatestSession(cwd);
@@ -16099,7 +16560,7 @@ async function createPlanOnly(cwd, goal) {
16099
16560
  goal,
16100
16561
  mode: "plan",
16101
16562
  cwd,
16102
- workspaceState: await detectWorkspaceState3(cwd),
16563
+ workspaceState: await inferPlanningWorkspaceState(cwd, goal),
16103
16564
  constraints: []
16104
16565
  };
16105
16566
  const plan = createPlan(request);
@@ -16543,18 +17004,18 @@ function toExternalCommandTokens(input, state) {
16543
17004
  async function handleShellCommand(cwd, input, state, runtime, execute) {
16544
17005
  const trimmed = input.trim();
16545
17006
  if (!trimmed) {
16546
- return;
17007
+ return cwd;
16547
17008
  }
16548
17009
  if (trimmed === "/exit" || trimmed === "exit" || trimmed === "quit" || trimmed === "/quit") {
16549
17010
  throw new Error("__kimbho_exit__");
16550
17011
  }
16551
17012
  if (trimmed === "/help" || trimmed === "help" || trimmed === "?") {
16552
17013
  console.log(renderHelp());
16553
- return;
17014
+ return cwd;
16554
17015
  }
16555
17016
  if (trimmed === "/status" || trimmed === "status") {
16556
17017
  console.log(renderStartupCard(cwd, state));
16557
- return;
17018
+ return cwd;
16558
17019
  }
16559
17020
  if (trimmed === "/clear" || trimmed === "clear") {
16560
17021
  if (import_node_process12.default.stdout.isTTY) {
@@ -16562,28 +17023,28 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
16562
17023
  }
16563
17024
  const nextState = await getShellSessionState(cwd, runtime.focusRole);
16564
17025
  printHeader(cwd, nextState);
16565
- return;
17026
+ return cwd;
16566
17027
  }
16567
17028
  const { tokens, head } = normalizeInputTokens(trimmed);
16568
17029
  const firstToken = tokens[0];
16569
17030
  const isSlashCommand = firstToken?.startsWith("/") ?? false;
16570
17031
  if (!head) {
16571
- return;
17032
+ return cwd;
16572
17033
  }
16573
17034
  if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
16574
17035
  const intent = inferPromptIntent(trimmed);
16575
17036
  if (intent === "run") {
16576
- await runGoalExecution(cwd, trimmed, runtime);
17037
+ const nextCwd = await runGoalExecution(cwd, trimmed, runtime);
16577
17038
  clearAllConversations(runtime);
16578
17039
  runtime.lastModels = null;
16579
- return;
17040
+ return nextCwd;
16580
17041
  }
16581
17042
  if (intent === "plan") {
16582
17043
  await createPlanOnly(cwd, normalizePlanPrompt(trimmed));
16583
- return;
17044
+ return cwd;
16584
17045
  }
16585
17046
  await handleChatPrompt(cwd, trimmed, runtime);
16586
- return;
17047
+ return cwd;
16587
17048
  }
16588
17049
  if (head === "provider" || head === "providers") {
16589
17050
  const subcommand = tokens[1];
@@ -16593,7 +17054,7 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
16593
17054
  "providers",
16594
17055
  ...tokens.slice(1)
16595
17056
  ], runtime);
16596
- return;
17057
+ return cwd;
16597
17058
  }
16598
17059
  }
16599
17060
  if (head === "brain" || head === "brains") {
@@ -16605,12 +17066,12 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
16605
17066
  "brain",
16606
17067
  ...tokens.slice(1)
16607
17068
  ], runtime);
16608
- return;
17069
+ return cwd;
16609
17070
  }
16610
17071
  }
16611
17072
  if (head === "model") {
16612
17073
  await printBrainAssignments(cwd);
16613
- return;
17074
+ return cwd;
16614
17075
  }
16615
17076
  if (head === "ask" || head === "chat") {
16616
17077
  const prompt = tokens.slice(1).join(" ").trim();
@@ -16618,19 +17079,19 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
16618
17079
  throw new Error(`Usage: /${head} <prompt>`);
16619
17080
  }
16620
17081
  await handleChatPrompt(cwd, prompt, runtime);
16621
- return;
17082
+ return cwd;
16622
17083
  }
16623
17084
  if (head === "reset-chat") {
16624
17085
  clearConversation(runtime, runtime.focusRole);
16625
17086
  console.log(`Cleared ${runtime.focusRole} conversation history.`);
16626
- return;
17087
+ return cwd;
16627
17088
  }
16628
17089
  if (head === "models") {
16629
17090
  await handleModelsCommand(cwd, [
16630
17091
  "models",
16631
17092
  ...tokens.slice(1)
16632
17093
  ], runtime);
16633
- return;
17094
+ return cwd;
16634
17095
  }
16635
17096
  if (head === "use-model") {
16636
17097
  const modelId = tokens.slice(1).join(" ").trim();
@@ -16638,50 +17099,51 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
16638
17099
  throw new Error("Usage: /use-model <model-id>");
16639
17100
  }
16640
17101
  await handleModelSelection(cwd, modelId, runtime);
16641
- return;
17102
+ return cwd;
16642
17103
  }
16643
17104
  if (head === "select") {
16644
17105
  await handleSelectCommand(cwd, [
16645
17106
  "select",
16646
17107
  ...tokens.slice(1)
16647
17108
  ], runtime);
16648
- return;
17109
+ return cwd;
16649
17110
  }
16650
17111
  if (head === "run" || head === "new" || head === "scaffold") {
16651
17112
  const goal = head === "run" ? tokens.slice(1).join(" ").trim() : head === "new" ? tokens.slice(1).join(" ").trim() : `scaffold ${tokens.slice(1).join(" ").trim()}`.trim();
16652
17113
  if (!goal) {
16653
17114
  throw new Error(`Usage: /${head} <goal>`);
16654
17115
  }
16655
- await runGoalExecution(cwd, goal, runtime);
17116
+ const nextCwd = await runGoalExecution(cwd, goal, runtime);
16656
17117
  clearAllConversations(runtime);
16657
17118
  runtime.lastModels = null;
16658
- return;
17119
+ return nextCwd;
16659
17120
  }
16660
17121
  if (head === "plan") {
16661
17122
  if (!isSlashCommand) {
16662
17123
  await createPlanOnly(cwd, normalizePlanPrompt(trimmed));
16663
- return;
17124
+ return cwd;
16664
17125
  }
16665
17126
  const goal = tokens.slice(1).join(" ").trim();
16666
17127
  if (!goal) {
16667
17128
  await printLatestPlanSummary(cwd);
16668
- return;
17129
+ return cwd;
16669
17130
  }
16670
17131
  await createPlanOnly(cwd, goal);
16671
- return;
17132
+ return cwd;
16672
17133
  }
16673
17134
  if (head === "resume") {
16674
17135
  await resumeGoalExecution(cwd, runtime);
16675
- return;
17136
+ return cwd;
16676
17137
  }
16677
17138
  const externalTokens = toExternalCommandTokens(trimmed, state);
16678
17139
  if (externalTokens.length === 0) {
16679
- return;
17140
+ return cwd;
16680
17141
  }
16681
17142
  await execute(externalTokens);
17143
+ return cwd;
16682
17144
  }
16683
17145
  async function runInteractiveShell(options) {
16684
- const readline = (0, import_promises16.createInterface)({
17146
+ const readline = (0, import_promises14.createInterface)({
16685
17147
  input: import_node_process12.default.stdin,
16686
17148
  output: import_node_process12.default.stdout,
16687
17149
  terminal: Boolean(import_node_process12.default.stdin.isTTY && import_node_process12.default.stdout.isTTY)
@@ -16689,6 +17151,7 @@ async function runInteractiveShell(options) {
16689
17151
  const runtime = {
16690
17152
  focusRole: "coder",
16691
17153
  lastModels: null,
17154
+ currentCwd: options.cwd,
16692
17155
  activeExecution: null,
16693
17156
  conversations: {
16694
17157
  planner: [],
@@ -16697,6 +17160,7 @@ async function runInteractiveShell(options) {
16697
17160
  fast: []
16698
17161
  }
16699
17162
  };
17163
+ let currentCwd = options.cwd;
16700
17164
  let closed = false;
16701
17165
  readline.on("SIGINT", () => {
16702
17166
  if (runtime.activeExecution) {
@@ -16708,18 +17172,19 @@ async function runInteractiveShell(options) {
16708
17172
  closed = true;
16709
17173
  readline.close();
16710
17174
  });
16711
- let state = await getShellSessionState(options.cwd, runtime.focusRole);
16712
- printHeader(options.cwd, state);
17175
+ let state = await getShellSessionState(currentCwd, runtime.focusRole);
17176
+ printHeader(currentCwd, state);
16713
17177
  while (!closed) {
16714
17178
  let line;
16715
17179
  try {
16716
- state = await getShellSessionState(options.cwd, runtime.focusRole);
17180
+ state = await getShellSessionState(currentCwd, runtime.focusRole);
16717
17181
  line = await readline.question(formatPrompt(state));
16718
17182
  } catch {
16719
17183
  break;
16720
17184
  }
16721
17185
  try {
16722
- await handleShellCommand(options.cwd, line, state, runtime, options.execute);
17186
+ currentCwd = await handleShellCommand(currentCwd, line, state, runtime, options.execute);
17187
+ runtime.currentCwd = currentCwd;
16723
17188
  } catch (error) {
16724
17189
  const message = error instanceof Error ? error.message : String(error);
16725
17190
  if (message === "__kimbho_exit__") {