@kimbho/kimbho-cli 0.1.23 → 0.1.26

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
@@ -12718,7 +12718,7 @@ function createCompletionRuntimeCommand(program2) {
12718
12718
  // package.json
12719
12719
  var package_default = {
12720
12720
  name: "@kimbho/kimbho-cli",
12721
- version: "0.1.23",
12721
+ version: "0.1.26",
12722
12722
  description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
12723
12723
  type: "module",
12724
12724
  engines: {
@@ -24540,7 +24540,8 @@ async function runShellCommand(toolId, command, cwd, timeoutMs, signal) {
24540
24540
  command
24541
24541
  ], cwd, timeoutMs, signal);
24542
24542
  const success2 = !result.timedOut && result.code === 0;
24543
- const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : success2 ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
24543
+ const interactiveSetupSummary = !success2 ? detectInteractiveShellPrompt(command, result.stdout, result.stderr) : null;
24544
+ const summary = result.timedOut ? `Command timed out after ${timeoutMs}ms.` : interactiveSetupSummary ? interactiveSetupSummary : success2 ? `Command completed successfully.` : `Command exited with code ${result.code ?? "unknown"}.`;
24544
24545
  return ToolResultSchema.parse({
24545
24546
  toolId,
24546
24547
  success: success2,
@@ -24549,6 +24550,18 @@ async function runShellCommand(toolId, command, cwd, timeoutMs, signal) {
24549
24550
  stderr: truncateOutput(result.stderr)
24550
24551
  });
24551
24552
  }
24553
+ function detectInteractiveShellPrompt(command, stdout, stderr) {
24554
+ const combined = `${stdout}
24555
+ ${stderr}`;
24556
+ const normalizedCommand = command.trim().toLowerCase();
24557
+ if (/how would you like to configure eslint\?/i.test(combined) || normalizedCommand.includes("next lint") && /eslint/i.test(combined) && /(strict \(recommended\)|base|cancel)/i.test(combined)) {
24558
+ return "Interactive ESLint setup required before next lint can run.";
24559
+ }
24560
+ if (/\?\s+[^\n]+\n(?:\s*❯|\s*>|\s*\*)/i.test(combined) || /(would you like to|choose one of the following|select an option|enter a value)/i.test(combined)) {
24561
+ return "Command requires interactive input before it can continue.";
24562
+ }
24563
+ return null;
24564
+ }
24552
24565
  async function executeFileRead(input, context) {
24553
24566
  if (context.signal?.aborted) {
24554
24567
  const error2 = new Error("Tool execution aborted.");
@@ -33108,6 +33121,24 @@ function isVerificationAction(action) {
33108
33121
  const command = typeof action.input.command === "string" ? action.input.command : "";
33109
33122
  return command.length > 0 && isVerificationCommand(command);
33110
33123
  }
33124
+ function extractShellCommand(action) {
33125
+ if (action.type !== "tool" || action.tool !== "shell.exec") {
33126
+ return "";
33127
+ }
33128
+ return typeof action.input.command === "string" ? action.input.command.trim() : "";
33129
+ }
33130
+ function isInteractiveVerificationSetupFailure(action, result) {
33131
+ if (!isVerificationAction(action) || result.success) {
33132
+ return false;
33133
+ }
33134
+ const command = extractShellCommand(action).toLowerCase();
33135
+ const combined = [
33136
+ result.summary,
33137
+ result.stdout ?? "",
33138
+ result.stderr ?? ""
33139
+ ].join("\n").toLowerCase();
33140
+ return combined.includes("interactive eslint setup required") || combined.includes("command requires interactive input before it can continue") || command.includes("lint") && combined.includes("how would you like to configure eslint");
33141
+ }
33111
33142
  function buildSystemPrompt(agent, task, request, allowedTools, plan, extraInstructions) {
33112
33143
  const toolShape = allowedTools.join("|");
33113
33144
  const dependencyTasks = plan ? flattenPlanTasks(plan).filter((candidate) => task.dependsOn.includes(candidate.id)) : [];
@@ -33150,6 +33181,7 @@ function buildSystemPrompt(agent, task, request, allowedTools, plan, extraInstru
33150
33181
  `- Keep paths relative to the workspace.`,
33151
33182
  `- In an existing repo, inspect and change the likely source files before running build or test verification. Do not front-load verification unless you are confirming an already-completed change or diagnosing a failure.`,
33152
33183
  `- After changing code, run verification with tests.run or shell.exec when appropriate.`,
33184
+ `- If a verification command asks for interactive setup or operator input, do not rerun it unchanged. Choose a different non-interactive verifier, or configure that verifier only if the task explicitly requires it.`,
33153
33185
  `- Do not claim success unless the task acceptance criteria are satisfied.`,
33154
33186
  `- If the task is underspecified, make a pragmatic implementation choice and continue.`,
33155
33187
  ...task.subagentInstructions ? [
@@ -33820,6 +33852,7 @@ ${truncateForModel(customAgentMemory)}`);
33820
33852
  const applyToolResult = async (parsedAction, result, step, transcriptEntry) => {
33821
33853
  const mutatingAction = isMutatingAction(parsedAction);
33822
33854
  const verificationAction = isVerificationAction(parsedAction);
33855
+ const interactiveVerificationSetupFailure = verificationAction && !result.success && isInteractiveVerificationSetupFailure(parsedAction, result);
33823
33856
  if (mutatingAction && result.success) {
33824
33857
  changedWorkspace = true;
33825
33858
  verifiedAfterLatestChange = false;
@@ -33834,6 +33867,10 @@ ${truncateForModel(customAgentMemory)}`);
33834
33867
  repairRequiredBeforeVerification = false;
33835
33868
  repairAppliedSinceFailure = false;
33836
33869
  lastVerificationFailure = null;
33870
+ } else if (interactiveVerificationSetupFailure) {
33871
+ repairRequiredBeforeVerification = false;
33872
+ repairAppliedSinceFailure = false;
33873
+ lastVerificationFailure = result;
33837
33874
  } else {
33838
33875
  verificationFailures += 1;
33839
33876
  repairRequiredBeforeVerification = true;
@@ -33853,9 +33890,25 @@ ${truncateForModel(customAgentMemory)}`);
33853
33890
  if (mutatingAction && result.success) {
33854
33891
  followUp.push("Code or workspace state changed. Inspect the diff if needed and run verification before finishing.");
33855
33892
  }
33856
- if (verificationAction && !result.success) {
33893
+ if (verificationAction && interactiveVerificationSetupFailure) {
33894
+ const failureSummary = `${result.toolId}: ${result.summary}`;
33895
+ transcriptEntry.runtimeNote = [
33896
+ `Verification could not run non-interactively. Latest failure: ${failureSummary}`,
33897
+ "Executor should choose a different non-interactive verifier or configure the verifier explicitly."
33898
+ ].join(" ");
33899
+ await emitProgress({
33900
+ type: "task-note",
33901
+ sessionId,
33902
+ taskId: task.id,
33903
+ agentRole: task.agentRole,
33904
+ step,
33905
+ message: `Verification needs setup via ${failureSummary}. Choosing a different non-interactive verifier or configuration path.`
33906
+ });
33907
+ followUp.push(`The attempted verification was not a code failure. It requires interactive setup or input (${failureSummary}). Do not rerun the same verifier unchanged. Choose a different non-interactive verification command, or configure the verifier explicitly if the task requires it.`);
33908
+ } else if (verificationAction && !result.success) {
33909
+ const failureSummary = `${result.toolId}: ${result.summary}`;
33857
33910
  transcriptEntry.runtimeNote = [
33858
- `Verification failed (${verificationFailures}/${maxRepairAttempts} repair attempts used).`,
33911
+ `Verification failed (${verificationFailures}/${maxRepairAttempts} repair attempts used). Latest failure: ${failureSummary}`,
33859
33912
  "Executor requires a repair action before the next verification run."
33860
33913
  ].join(" ");
33861
33914
  await emitProgress({
@@ -33864,9 +33917,9 @@ ${truncateForModel(customAgentMemory)}`);
33864
33917
  taskId: task.id,
33865
33918
  agentRole: task.agentRole,
33866
33919
  step,
33867
- message: `Verification failed. Repair attempts used: ${verificationFailures}/${maxRepairAttempts}.`
33920
+ message: `Verification failed via ${failureSummary}. Repair attempts used: ${verificationFailures}/${maxRepairAttempts}.`
33868
33921
  });
33869
- followUp.push(`Verification failed. Inspect the failure output, repair the issue, and run verification again before finishing. Repair attempts used: ${verificationFailures}/${maxRepairAttempts}.`);
33922
+ followUp.push(`Verification failed via ${failureSummary}. Inspect the failure output, repair the issue, and run verification again before finishing. Repair attempts used: ${verificationFailures}/${maxRepairAttempts}.`);
33870
33923
  if (verificationFailures >= maxRepairAttempts) {
33871
33924
  transcriptEntry.runtimeNote = [
33872
33925
  transcriptEntry.runtimeNote,
@@ -43930,6 +43983,14 @@ var SPINNER_FRAMES = [
43930
43983
  "|",
43931
43984
  "/"
43932
43985
  ];
43986
+ var ELLIPSIS_FRAMES = [
43987
+ ". ",
43988
+ ".. ",
43989
+ "...",
43990
+ " ..",
43991
+ " .",
43992
+ " "
43993
+ ];
43933
43994
  var IDLE_STATUS_LABELS = [
43934
43995
  "thinking",
43935
43996
  "musing",
@@ -44023,6 +44084,11 @@ function shouldAutoStartApprovedRuns(runtime) {
44023
44084
  const approvalMode = resolveRuntimeOverrideApprovalMode(runtime);
44024
44085
  return approvalMode === "auto" || approvalMode === "dontAsk" || approvalMode === "acceptEdits" || approvalMode === "bypassPermissions";
44025
44086
  }
44087
+ function shouldAutoStartProposal(runtime, proposal) {
44088
+ return Boolean(
44089
+ proposal && shouldAutoStartApprovedRuns(runtime) && !planRequiresOperatorReview(proposal.plan)
44090
+ );
44091
+ }
44026
44092
  function resolveShellMaxAutoTasks(runtime) {
44027
44093
  return shouldAutoStartApprovedRuns(runtime) ? UNBOUNDED_MAX_AUTO_TASKS : DEFAULT_MAX_AUTO_TASKS;
44028
44094
  }
@@ -44170,7 +44236,7 @@ function createExecutionTelemetry() {
44170
44236
  }
44171
44237
  var ShellActivityIndicator = class {
44172
44238
  interval = null;
44173
- frameIndex = 0;
44239
+ animationTick = 0;
44174
44240
  label;
44175
44241
  activeLine = false;
44176
44242
  constructor(label) {
@@ -44183,14 +44249,14 @@ var ShellActivityIndicator = class {
44183
44249
  this.activeLine = true;
44184
44250
  this.render();
44185
44251
  this.interval = setInterval(() => {
44186
- this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
44252
+ this.animationTick += 1;
44187
44253
  this.render();
44188
- }, 120);
44254
+ }, 140);
44189
44255
  this.interval.unref?.();
44190
44256
  }
44191
44257
  update(label) {
44192
44258
  if (label !== this.label) {
44193
- this.frameIndex = 0;
44259
+ this.animationTick = 0;
44194
44260
  }
44195
44261
  this.label = label;
44196
44262
  if (this.interval) {
@@ -44221,9 +44287,10 @@ var ShellActivityIndicator = class {
44221
44287
  if (!import_node_process26.default.stdout.isTTY) {
44222
44288
  return;
44223
44289
  }
44224
- const frame = color(AMBER, SPINNER_FRAMES[this.frameIndex]);
44225
- const status = renderShimmeringLabel(this.label, this.frameIndex);
44226
- const raw = `${frame} ${status}${color(DIM, "...")}`;
44290
+ const frame = color(AMBER, SPINNER_FRAMES[this.animationTick % SPINNER_FRAMES.length]);
44291
+ const status = renderShimmeringLabel(this.label, this.animationTick);
44292
+ const dots = color(DIM, ELLIPSIS_FRAMES[this.animationTick % ELLIPSIS_FRAMES.length]);
44293
+ const raw = `${frame} ${status}${dots}`;
44227
44294
  this.clear();
44228
44295
  (0, import_node_readline2.cursorTo)(import_node_process26.default.stdout, 0);
44229
44296
  import_node_process26.default.stdout.write(raw);
@@ -44532,6 +44599,27 @@ function hashString(value) {
44532
44599
  function pickStatusLabel(message) {
44533
44600
  const lower = message.toLowerCase();
44534
44601
  const choose = (labels) => labels[hashString(message) % labels.length];
44602
+ if (lower.includes("verification needs setup") || lower.includes("different non-interactive verifier")) {
44603
+ return choose([
44604
+ "adapting",
44605
+ "rerouting",
44606
+ "recovering"
44607
+ ]);
44608
+ }
44609
+ if (lower.includes("verification failed") || lower.includes("repair attempts used")) {
44610
+ return choose([
44611
+ "repairing",
44612
+ "debugging",
44613
+ "recovering"
44614
+ ]);
44615
+ }
44616
+ if (lower.includes("repair budget exhausted") || lower.includes("debugger escalation")) {
44617
+ return choose([
44618
+ "escalating",
44619
+ "stopping",
44620
+ "blocking"
44621
+ ]);
44622
+ }
44535
44623
  if (lower.includes("synthesizing") || lower.includes("architecture brief")) {
44536
44624
  return choose([
44537
44625
  "planning",
@@ -44581,6 +44669,13 @@ function pickStatusLabel(message) {
44581
44669
  "untangling"
44582
44670
  ]);
44583
44671
  }
44672
+ if (lower.includes("verify") || lower.includes("verification")) {
44673
+ return choose([
44674
+ "verifying",
44675
+ "checking",
44676
+ "confirming"
44677
+ ]);
44678
+ }
44584
44679
  return choose([
44585
44680
  "thinking",
44586
44681
  "musing",
@@ -44591,6 +44686,21 @@ function pickStatusLabel(message) {
44591
44686
  function pickIdleStatusLabel(seed) {
44592
44687
  return IDLE_STATUS_LABELS[hashString(seed) % IDLE_STATUS_LABELS.length];
44593
44688
  }
44689
+ function statusLabelForToolStart(event) {
44690
+ if (event.toolId === "shell.exec" || event.toolId === "tests.run") {
44691
+ const command = typeof event.input?.command === "string" ? event.input.command.trim().toLowerCase() : "";
44692
+ if (command.includes("build") || command.includes("lint") || command.includes("test")) {
44693
+ return "verifying";
44694
+ }
44695
+ if (command.includes("dev") || command.includes("start")) {
44696
+ return "serving";
44697
+ }
44698
+ if (command.length > 0) {
44699
+ return pickStatusLabel(command);
44700
+ }
44701
+ }
44702
+ return pickStatusLabel(event.reason ?? event.toolId);
44703
+ }
44594
44704
  function statusLabelForEvent(event) {
44595
44705
  switch (event.type) {
44596
44706
  case "task-note":
@@ -44598,7 +44708,7 @@ function statusLabelForEvent(event) {
44598
44708
  case "task-started":
44599
44709
  return pickStatusLabel(event.task.title);
44600
44710
  case "tool-started":
44601
- return pickStatusLabel(event.reason ?? event.toolId);
44711
+ return statusLabelForToolStart(event);
44602
44712
  case "model-usage":
44603
44713
  return "thinking";
44604
44714
  case "approval-requested":
@@ -44708,14 +44818,67 @@ function renderShellPlanSummary(plan) {
44708
44818
  }
44709
44819
  return lines;
44710
44820
  }
44821
+ function inferRecommendedPlanAnswer(question, plan) {
44822
+ const lower = question.toLowerCase();
44823
+ const goal = plan.goal.toLowerCase();
44824
+ if (lower.includes("brand voice") || lower.includes("visual direction")) {
44825
+ if (goal.includes("blog") || goal.includes("post")) {
44826
+ return "Use a premium editorial direction: clean typography, strong whitespace, restrained motion, and a polished reading-first layout.";
44827
+ }
44828
+ return "Use a restrained premium product direction: crisp typography, strong whitespace, subtle motion, and a clear call to action.";
44829
+ }
44830
+ if (lower.includes("which sections")) {
44831
+ if (goal.includes("blog") || goal.includes("post")) {
44832
+ return "Start with navigation, hero, featured posts, topic/category grid, newsletter CTA, and a compact footer.";
44833
+ }
44834
+ return "Start with navigation, hero, key benefits, proof/examples, primary CTA, and footer.";
44835
+ }
44836
+ if (lower.includes("primary call to action")) {
44837
+ if (goal.includes("blog") || goal.includes("post")) {
44838
+ return "Drive readers to featured posts first, with newsletter signup as the secondary CTA.";
44839
+ }
44840
+ return "Drive the main product action with one clear primary CTA and keep secondary actions minimal.";
44841
+ }
44842
+ if (lower.includes("which stack") || lower.includes("deployment target")) {
44843
+ return "Stay with the current repo stack and existing deployment shape unless the user asks for a migration.";
44844
+ }
44845
+ if (lower.includes("authentication model")) {
44846
+ return "Assume no authentication in the first version unless the request explicitly needs gated user flows.";
44847
+ }
44848
+ if (lower.includes("external services") || lower.includes("apis")) {
44849
+ return "Assume no new external integrations beyond what already exists in the repository.";
44850
+ }
44851
+ return "Proceed with the most pragmatic default that preserves the current repo and keeps the first version focused.";
44852
+ }
44853
+ function extractPlanReviewPrompts(plan) {
44854
+ return plan.openQuestions.slice(0, 3).map((question) => ({
44855
+ question,
44856
+ recommendation: inferRecommendedPlanAnswer(question, plan)
44857
+ }));
44858
+ }
44859
+ function planRequiresOperatorReview(plan) {
44860
+ return extractPlanReviewPrompts(plan).length > 0;
44861
+ }
44711
44862
  function renderPendingRunProposal(proposal, options = {}) {
44863
+ const reviewPrompts = extractPlanReviewPrompts(proposal.plan);
44864
+ const requiresReview = reviewPrompts.length > 0;
44712
44865
  const lines = [
44713
44866
  `${color(BOLD, proposal.source === "queued" ? "Queued Plan Ready" : "Ready To Run")}`,
44714
- ...renderShellPlanSummary(proposal.plan),
44715
- "",
44716
- color(DIM, `saved plan: ${proposal.planPath}`),
44717
- options.autoStart ? color(DIM, "next: auto-starting in the current permission mode") : "next: /approve to start, /run revise <feedback> to adjust, /run cancel to drop it"
44867
+ ...renderShellPlanSummary(proposal.plan)
44718
44868
  ];
44869
+ if (requiresReview) {
44870
+ lines.push("");
44871
+ lines.push(color(BOLD, "Recommended Defaults"));
44872
+ for (const prompt of reviewPrompts) {
44873
+ lines.push(`\u2022 ${prompt.question}`);
44874
+ lines.push(color(DIM, ` recommended: ${prompt.recommendation}`));
44875
+ }
44876
+ }
44877
+ lines.push("");
44878
+ lines.push(color(DIM, `saved plan: ${proposal.planPath}`));
44879
+ lines.push(
44880
+ requiresReview ? "next: /run approve to accept the recommended defaults, /run revise <feedback> to adjust, /run cancel to drop it" : options.autoStart ? color(DIM, "next: auto-starting in the current permission mode") : "next: /approve to start, /run revise <feedback> to adjust, /run cancel to drop it"
44881
+ );
44719
44882
  if (proposal.source === "queued") {
44720
44883
  lines.splice(1, 0, color(DIM, "The next queued request has been planned and is waiting for your approval."));
44721
44884
  }
@@ -44813,6 +44976,24 @@ function renderConciseProgress(board, startedAt) {
44813
44976
  ] : []
44814
44977
  ];
44815
44978
  }
44979
+ function summarizeToolStart(event) {
44980
+ if (event.toolId === "shell.exec" || event.toolId === "tests.run") {
44981
+ const command = typeof event.input?.command === "string" ? event.input.command.trim() : "";
44982
+ if (command) {
44983
+ return `Running ${shortenMiddle(command, 90)}`;
44984
+ }
44985
+ }
44986
+ if (event.toolId === "http.fetch" && typeof event.input?.url === "string") {
44987
+ return `Checking ${shortenMiddle(event.input.url, 90)}`;
44988
+ }
44989
+ if (event.toolId === "process.start" && typeof event.input?.command === "string") {
44990
+ return `Starting ${shortenMiddle(event.input.command, 90)}`;
44991
+ }
44992
+ if (event.toolId === "browser.open" && typeof event.input?.url === "string") {
44993
+ return `Opening ${shortenMiddle(event.input.url, 90)}`;
44994
+ }
44995
+ return null;
44996
+ }
44816
44997
  function renderLiveExecutionEvent(event, board, startedAt) {
44817
44998
  switch (event.type) {
44818
44999
  case "task-started":
@@ -44828,6 +45009,13 @@ function renderLiveExecutionEvent(event, board, startedAt) {
44828
45009
  )} ${simplifyExecutionSummary(event.summary) || event.taskId}`,
44829
45010
  ...renderConciseProgress(board, startedAt)
44830
45011
  ];
45012
+ case "tool-started": {
45013
+ const summary = summarizeToolStart(event);
45014
+ return summary ? [
45015
+ `${color(DIM, "working")} ${summary}`,
45016
+ ...renderConciseProgress(board, startedAt)
45017
+ ] : [];
45018
+ }
44831
45019
  case "task-note":
44832
45020
  return isUserVisibleTaskNote(event.message) ? [
44833
45021
  `${color(DIM, "note")} ${simplifyTaskNote(event.message)}`
@@ -45006,7 +45194,7 @@ async function drainQueuedWork(runtime) {
45006
45194
  source: "queued"
45007
45195
  });
45008
45196
  runtime.currentCwd = nextCwd;
45009
- if (shouldAutoStartApprovedRuns(runtime)) {
45197
+ if (shouldAutoStartProposal(runtime, runtime.pendingRunProposal)) {
45010
45198
  console.log(color(DIM, "Auto-starting queued request in the current permission mode."));
45011
45199
  startPendingRunExecution(runtime);
45012
45200
  }
@@ -45179,7 +45367,7 @@ async function prepareRunProposal(cwd, goal, runtime, options = {}) {
45179
45367
  console.log(renderPendingRunProposal(
45180
45368
  runtime.pendingRunProposal,
45181
45369
  {
45182
- autoStart: shouldAutoStartApprovedRuns(runtime)
45370
+ autoStart: shouldAutoStartProposal(runtime, runtime.pendingRunProposal)
45183
45371
  }
45184
45372
  ).join("\n"));
45185
45373
  return request.cwd;
@@ -46589,7 +46777,7 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
46589
46777
  const nextCwd = await prepareRunProposal(cwd, trimmed, runtime, {
46590
46778
  source: "direct"
46591
46779
  });
46592
- if (shouldAutoStartApprovedRuns(runtime)) {
46780
+ if (shouldAutoStartProposal(runtime, runtime.pendingRunProposal)) {
46593
46781
  console.log(color(DIM, "Auto-starting in the current permission mode."));
46594
46782
  startPendingRunExecution(runtime);
46595
46783
  }
@@ -46719,7 +46907,7 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
46719
46907
  console.log(renderPendingRunProposal(
46720
46908
  runtime.pendingRunProposal,
46721
46909
  {
46722
- autoStart: shouldAutoStartApprovedRuns(runtime)
46910
+ autoStart: shouldAutoStartProposal(runtime, runtime.pendingRunProposal)
46723
46911
  }
46724
46912
  ).join("\n"));
46725
46913
  return cwd;
@@ -46756,7 +46944,7 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
46756
46944
  const nextCwd = await prepareRunProposal(cwd, goal, runtime, {
46757
46945
  source: "direct"
46758
46946
  });
46759
- if (shouldAutoStartApprovedRuns(runtime)) {
46947
+ if (shouldAutoStartProposal(runtime, runtime.pendingRunProposal)) {
46760
46948
  console.log(color(DIM, "Auto-starting in the current permission mode."));
46761
46949
  startPendingRunExecution(runtime);
46762
46950
  }