@kimbho/kimbho-cli 0.1.31 → 0.1.32

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.31",
12721
+ version: "0.1.32",
12722
12722
  description: "Kimbho CLI is a terminal-native coding agent for planning, execution, and verification.",
12723
12723
  type: "module",
12724
12724
  engines: {
@@ -42456,7 +42456,13 @@ var CHAT_PREFIXES = [
42456
42456
  "explain ",
42457
42457
  "summarize ",
42458
42458
  "review ",
42459
- "analyze "
42459
+ "analyze ",
42460
+ "tell me ",
42461
+ "describe ",
42462
+ "detail ",
42463
+ "details ",
42464
+ "walk me through ",
42465
+ "help me understand "
42460
42466
  ];
42461
42467
  var PLAN_PREFIXES = [
42462
42468
  "plan ",
@@ -42468,6 +42474,18 @@ var PLAN_PREFIXES = [
42468
42474
  function looksLikeExecutionRequest(input) {
42469
42475
  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);
42470
42476
  }
42477
+ function looksLikeInformationalRequest(input) {
42478
+ if (/\b(tell me|describe|walk me through|help me understand)\b/.test(input)) {
42479
+ return true;
42480
+ }
42481
+ if (/\b(details?|overview|summary|walkthrough|explanation)\b/.test(input) && /\b(project|repo|repository|codebase|workspace|app)\b/.test(input)) {
42482
+ return true;
42483
+ }
42484
+ return /\b(current|this|the)\s+(project|repo|repository|codebase|workspace|app)\b/.test(input) && /\b(details?|overview|summary|about)\b/.test(input);
42485
+ }
42486
+ function looksLikeWorkspaceInfoPrompt(input) {
42487
+ return looksLikeInformationalRequest(input.toLowerCase()) && /\b(project|repo|repository|codebase|workspace|app)\b/.test(input.toLowerCase());
42488
+ }
42471
42489
  function stripConversationalLead(input) {
42472
42490
  let normalized = input.trim().toLowerCase();
42473
42491
  normalized = normalized.replace(/^(hi|hello|hey|yo|sup)\b[!,. ]*/u, "").trim();
@@ -42495,6 +42513,9 @@ function inferPromptIntent(input) {
42495
42513
  if (looksLikeExecutionRequest(taskCandidate)) {
42496
42514
  return "run";
42497
42515
  }
42516
+ if (looksLikeInformationalRequest(taskCandidate)) {
42517
+ return "chat";
42518
+ }
42498
42519
  if (normalized.endsWith("?")) {
42499
42520
  return "chat";
42500
42521
  }
@@ -50290,6 +50311,138 @@ function renderPendingRunProposal(proposal, options = {}) {
50290
50311
  }
50291
50312
  return lines;
50292
50313
  }
50314
+ function buildWorkspaceAnalysisPlan(request, prompt) {
50315
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
50316
+ return {
50317
+ goal: request.goal,
50318
+ generatedAt,
50319
+ summary: "Read-only multi-agent analysis of the current repository to answer the operator's question with concrete workspace evidence.",
50320
+ repoStrategy: {
50321
+ mode: "existing-repo",
50322
+ reasoning: "This is an analysis-only request about the current workspace, so the agent team should inspect the repo and synthesize findings without editing source files."
50323
+ },
50324
+ assumptions: [
50325
+ "The user wants a grounded explanation of the current repository, not code changes.",
50326
+ "The analysis should stay read-only and avoid modifying product files."
50327
+ ],
50328
+ openQuestions: [],
50329
+ milestones: [
50330
+ {
50331
+ id: "m1-repo-survey",
50332
+ title: "Repository Survey",
50333
+ objective: "Inspect the current workspace structure, entrypoints, tooling, and conventions.",
50334
+ tasks: [
50335
+ {
50336
+ id: "t1-repo-analysis",
50337
+ title: "Analyze the current workspace and conventions",
50338
+ description: "Map the repository shape, commands, entrypoints, and constraints before answering the user's question.",
50339
+ type: "analysis",
50340
+ status: "pending",
50341
+ agentRole: "repo-analyst",
50342
+ dependsOn: [],
50343
+ acceptanceCriteria: [
50344
+ "Repository shape and commands are captured.",
50345
+ "Likely entrypoints and major packages are identified."
50346
+ ],
50347
+ outputs: [
50348
+ "Repo analysis summary"
50349
+ ],
50350
+ filesLikelyTouched: [
50351
+ ".",
50352
+ ".kimbho/"
50353
+ ],
50354
+ riskLevel: "low",
50355
+ sandboxModeOverride: "read-only"
50356
+ },
50357
+ {
50358
+ id: "t2-architecture",
50359
+ title: "Synthesize the architecture and package boundaries",
50360
+ description: "Turn the repository findings into a clear explanation of the system structure and responsibilities.",
50361
+ type: "documentation",
50362
+ status: "pending",
50363
+ agentRole: "planner",
50364
+ dependsOn: [
50365
+ "t1-repo-analysis"
50366
+ ],
50367
+ acceptanceCriteria: [
50368
+ "Architecture summary is grounded in repo analysis findings.",
50369
+ "The main packages and responsibilities are explicit."
50370
+ ],
50371
+ outputs: [
50372
+ "Architecture brief"
50373
+ ],
50374
+ filesLikelyTouched: [
50375
+ ".kimbho/"
50376
+ ],
50377
+ riskLevel: "low",
50378
+ sandboxModeOverride: "read-only"
50379
+ }
50380
+ ]
50381
+ },
50382
+ {
50383
+ id: "m2-answer",
50384
+ title: "Answer",
50385
+ objective: "Review the gathered analysis artifacts and produce the final project explanation.",
50386
+ tasks: [
50387
+ {
50388
+ id: "t3-project-brief",
50389
+ title: "Review the analysis artifacts and prepare the project explanation",
50390
+ description: "Inspect the repo-analysis and architecture artifacts, then summarize the current project in terms that answer the user's question.",
50391
+ type: "documentation",
50392
+ status: "pending",
50393
+ agentRole: "reviewer",
50394
+ dependsOn: [
50395
+ "t1-repo-analysis",
50396
+ "t2-architecture"
50397
+ ],
50398
+ acceptanceCriteria: [
50399
+ "The answer addresses the user's question directly.",
50400
+ "The summary is grounded in repository evidence instead of generic assistant chatter."
50401
+ ],
50402
+ outputs: [
50403
+ "Project explanation summary"
50404
+ ],
50405
+ filesLikelyTouched: [
50406
+ ".kimbho/logs/",
50407
+ "kimbho_init.md"
50408
+ ],
50409
+ riskLevel: "low",
50410
+ sandboxModeOverride: "read-only",
50411
+ allowedTools: [
50412
+ "file.read",
50413
+ "file.search",
50414
+ "file.list",
50415
+ "repo.index",
50416
+ "repo.query",
50417
+ "git.diff"
50418
+ ],
50419
+ deniedTools: [
50420
+ "file.write",
50421
+ "file.patch",
50422
+ "shell.exec"
50423
+ ],
50424
+ agentPromptPreamble: [
50425
+ "This is a read-only repository explanation task.",
50426
+ "Do not modify product files, install packages, or run development servers.",
50427
+ `Answer the operator's question: ${prompt}`,
50428
+ "Use the repo-analysis and architecture artifacts plus direct file inspection if needed.",
50429
+ "Finish with a concise but concrete summary of the project, stack, entrypoints, commands, and notable risks or gaps."
50430
+ ].join("\n")
50431
+ }
50432
+ ]
50433
+ }
50434
+ ],
50435
+ verificationChecklist: [
50436
+ "Repo analysis artifacts were captured.",
50437
+ "The architecture summary is grounded in the workspace.",
50438
+ "The final explanation answers the user's question without changing source files."
50439
+ ],
50440
+ executionNotes: [
50441
+ "Read-only analysis run triggered directly from a workspace question.",
50442
+ "Prefer repo/file tools and existing memory artifacts over generic assistant responses."
50443
+ ]
50444
+ };
50445
+ }
50293
50446
  function hasRenderableDiff(toolId, output) {
50294
50447
  if (!output || output.trim().length === 0) {
50295
50448
  return false;
@@ -50913,6 +51066,22 @@ async function handleChatPrompt(cwd, prompt, runtime) {
50913
51066
  maxCharsPerFile: 3e3
50914
51067
  });
50915
51068
  const skillContext = await resolveActivatedSkillContext(cwd, hydrated.prompt);
51069
+ const workspaceInfoPrompt = looksLikeWorkspaceInfoPrompt(hydrated.prompt);
51070
+ let workspaceOverview = null;
51071
+ if (workspaceInfoPrompt) {
51072
+ const existingOverview = await readMarkdownIfExists(import_node_path33.default.join(cwd, "kimbho_init.md"));
51073
+ if (existingOverview) {
51074
+ workspaceOverview = existingOverview.slice(0, 6e3);
51075
+ } else {
51076
+ try {
51077
+ const generated = await generateProjectInitFile(cwd, "kimbho_init.md");
51078
+ const generatedOverview = await readMarkdownIfExists(generated.outputPath);
51079
+ workspaceOverview = generatedOverview?.slice(0, 6e3) ?? null;
51080
+ } catch {
51081
+ workspaceOverview = null;
51082
+ }
51083
+ }
51084
+ }
50916
51085
  const compactionPath = await compactConversationIfNeeded(cwd, runtime, runtime.focusRole);
50917
51086
  if (compactionPath) {
50918
51087
  console.log(color(DIM, `[compacting] condensed ${runtime.focusRole} chat context -> ${compactionPath}`));
@@ -50928,27 +51097,34 @@ async function handleChatPrompt(cwd, prompt, runtime) {
50928
51097
  let result;
50929
51098
  const activity = new ShellActivityIndicator(pickIdleStatusLabel(prompt));
50930
51099
  activity.start();
51100
+ const systemPromptSections = [
51101
+ brain.settings.promptPreamble,
51102
+ adaptiveMemory.chatSummary.length > 0 ? `Adaptive memory:
51103
+ ${adaptiveMemory.chatSummary}` : null,
51104
+ memoryContext.length > 0 ? [
51105
+ "Workspace memory:",
51106
+ ...memoryContext.map((record2) => `File: ${record2.filePath}
51107
+ ${record2.content}`)
51108
+ ].join("\n\n") : null,
51109
+ workspaceOverview ? [
51110
+ "Workspace question handling:",
51111
+ "The user is asking about the current repository. Ground the answer in the workspace context below and do not reply with a generic assistant introduction.",
51112
+ `Workspace overview:
51113
+ ${workspaceOverview}`
51114
+ ].join("\n\n") : null,
51115
+ skillContext.systemPromptSections.length > 0 ? [
51116
+ "Active skill packs:",
51117
+ ...skillContext.systemPromptSections
51118
+ ].join("\n\n") : null,
51119
+ runtime.conversationSummaries[runtime.focusRole] ? `Compacted conversation summary:
51120
+ ${runtime.conversationSummaries[runtime.focusRole]}` : null
51121
+ ].filter((value) => Boolean(value));
50931
51122
  try {
50932
51123
  result = await brain.client.generateText({
50933
51124
  model: brain.model,
50934
51125
  messages,
50935
- ...brain.settings.promptPreamble || runtime.conversationSummaries[runtime.focusRole] ? {
50936
- systemPrompt: [
50937
- brain.settings.promptPreamble,
50938
- adaptiveMemory.chatSummary.length > 0 ? `Adaptive memory:
50939
- ${adaptiveMemory.chatSummary}` : null,
50940
- memoryContext.length > 0 ? [
50941
- "Workspace memory:",
50942
- ...memoryContext.map((record2) => `File: ${record2.filePath}
50943
- ${record2.content}`)
50944
- ].join("\n\n") : null,
50945
- skillContext.systemPromptSections.length > 0 ? [
50946
- "Active skill packs:",
50947
- ...skillContext.systemPromptSections
50948
- ].join("\n\n") : null,
50949
- runtime.conversationSummaries[runtime.focusRole] ? `Compacted conversation summary:
50950
- ${runtime.conversationSummaries[runtime.focusRole]}` : null
50951
- ].filter((value) => Boolean(value)).join("\n\n")
51126
+ ...systemPromptSections.length > 0 ? {
51127
+ systemPrompt: systemPromptSections.join("\n\n")
50952
51128
  } : {},
50953
51129
  ...typeof brain.settings.temperature === "number" ? {
50954
51130
  temperature: brain.settings.temperature
@@ -51147,6 +51323,174 @@ async function executePendingRunProposal(runtime) {
51147
51323
  void drainQueuedWork(runtime);
51148
51324
  return request.cwd;
51149
51325
  }
51326
+ async function synthesizeWorkspaceAnalysisAnswer(cwd, prompt, runtime, snapshot) {
51327
+ const config2 = await loadConfig(cwd);
51328
+ const projectInit = await readMarkdownIfExists(import_node_path33.default.join(cwd, "kimbho_init.md"));
51329
+ const artifactPaths = Array.from(new Set(
51330
+ snapshot.events.flatMap((event) => [
51331
+ ...event.artifacts,
51332
+ ...event.toolResults.flatMap((toolResult) => toolResult.artifacts)
51333
+ ])
51334
+ ));
51335
+ const prioritizedArtifacts = artifactPaths.filter((artifactPath) => /repo-analysis|architecture-brief|project-brief|transcript/i.test(import_node_path33.default.basename(artifactPath))).slice(0, 4);
51336
+ const artifactSections = (await Promise.all(
51337
+ prioritizedArtifacts.map(async (artifactPath) => {
51338
+ const content = await readMarkdownIfExists(artifactPath);
51339
+ if (!content) {
51340
+ return null;
51341
+ }
51342
+ return `Artifact: ${artifactPath}
51343
+ ${content.slice(0, 4e3)}`;
51344
+ })
51345
+ )).filter((section) => Boolean(section));
51346
+ if (!config2) {
51347
+ return [
51348
+ "Repository analysis completed.",
51349
+ projectInit ? `Current workspace summary:
51350
+ ${projectInit.slice(0, 4e3)}` : "Project summary is available in the session artifacts, but no configured model was available to synthesize a narrative answer."
51351
+ ].join("\n\n");
51352
+ }
51353
+ const resolver = new BrainResolver(config2, createDefaultBrainProviderRegistry(cwd));
51354
+ const brain = await resolver.resolve(runtime.focusRole);
51355
+ const result = await brain.client.generateText({
51356
+ model: brain.model,
51357
+ systemPrompt: [
51358
+ brain.settings.promptPreamble,
51359
+ "You are summarizing the current repository after a read-only multi-agent analysis session.",
51360
+ "Answer the user's question directly and concretely from the provided workspace evidence.",
51361
+ "Do not greet, do not speak generically about your own capabilities, and do not invent repository details.",
51362
+ "Prefer a crisp project overview with stack, structure, key entrypoints, commands, and notable risks or gaps."
51363
+ ].filter(Boolean).join("\n\n"),
51364
+ userPrompt: [
51365
+ `User question: ${prompt}`,
51366
+ projectInit ? `Workspace overview:
51367
+ ${projectInit.slice(0, 6e3)}` : null,
51368
+ artifactSections.length > 0 ? artifactSections.join("\n\n") : "No detailed analysis artifacts were available; answer from the workspace overview only."
51369
+ ].filter((value) => Boolean(value)).join("\n\n"),
51370
+ ...typeof brain.settings.temperature === "number" ? {
51371
+ temperature: brain.settings.temperature
51372
+ } : {},
51373
+ ...typeof brain.settings.maxTokens === "number" ? {
51374
+ maxTokens: brain.settings.maxTokens
51375
+ } : {}
51376
+ });
51377
+ return result.text;
51378
+ }
51379
+ async function handleWorkspaceAnalysisPrompt(cwd, prompt, runtime) {
51380
+ const hydrated = await hydratePromptWithMcpResources(cwd, prompt);
51381
+ await recordAdaptiveTextObservation(cwd, hydrated.prompt, "chat");
51382
+ const skillAwareRequest = await preparePlanningRequest({
51383
+ goal: `Analyze the current repository and answer the user's question: ${hydrated.prompt}`,
51384
+ mode: "run",
51385
+ cwd,
51386
+ workspaceState: "existing",
51387
+ constraints: [
51388
+ "Read-only repository analysis only.",
51389
+ "Do not modify source files, install packages, or start development servers.",
51390
+ "Use multiple agents to inspect the workspace and synthesize the answer when helpful."
51391
+ ]
51392
+ });
51393
+ const request = skillAwareRequest.request;
51394
+ const plan = buildWorkspaceAnalysisPlan(request, hydrated.prompt);
51395
+ const planPath = await savePlan(plan, request.cwd);
51396
+ const orchestrator = new ExecutionOrchestrator();
51397
+ const initialSnapshot = orchestrator.createSessionSnapshot(
51398
+ orchestrator.buildEnvelope(request, plan)
51399
+ );
51400
+ const startedAt = Date.now();
51401
+ const telemetry = createExecutionTelemetry();
51402
+ const controller = new AbortController();
51403
+ runtime.activeExecution = {
51404
+ controller,
51405
+ label: hydrated.prompt
51406
+ };
51407
+ const liveBoard = createLiveRunBoard(
51408
+ initialSnapshot.id,
51409
+ hydrated.prompt,
51410
+ request,
51411
+ plan,
51412
+ resolveShellMaxAutoTasks(runtime),
51413
+ resolveShellMaxAgentSteps(runtime),
51414
+ DEFAULT_MAX_REPAIR_ATTEMPTS2
51415
+ );
51416
+ const tui = import_node_process35.default.stdout.isTTY ? new ShellExecutionTui(liveBoard, telemetry, startedAt, "analyzing", !runtime.dashboard) : null;
51417
+ const activity = tui ? null : new ShellActivityIndicator("analyzing");
51418
+ if (tui) {
51419
+ tui.start();
51420
+ tui.pushEvent(renderLiveExecutionEvent({
51421
+ type: "task-note",
51422
+ sessionId: initialSnapshot.id,
51423
+ message: "Read-only repository analysis session started."
51424
+ }, liveBoard, startedAt, {
51425
+ includeProgress: false
51426
+ }));
51427
+ } else {
51428
+ console.log(renderRunStartCard(liveBoard));
51429
+ console.log("");
51430
+ activity?.start();
51431
+ }
51432
+ let snapshot;
51433
+ try {
51434
+ snapshot = await orchestrator.continueSession(initialSnapshot, {
51435
+ maxAutoTasks: resolveShellMaxAutoTasks(runtime),
51436
+ maxAgentSteps: resolveShellMaxAgentSteps(runtime),
51437
+ maxRepairAttempts: DEFAULT_MAX_REPAIR_ATTEMPTS2,
51438
+ signal: controller.signal,
51439
+ onProgress: async (event) => {
51440
+ updateLiveRunBoard(liveBoard, event);
51441
+ if (event.type === "tool-started") {
51442
+ telemetry.toolCalls += 1;
51443
+ }
51444
+ if (event.type === "model-usage") {
51445
+ telemetry.modelCalls += 1;
51446
+ telemetry.inputTokens += event.usage?.inputTokens ?? 0;
51447
+ telemetry.outputTokens += event.usage?.outputTokens ?? 0;
51448
+ }
51449
+ if (tui) {
51450
+ tui.updateActivity(statusLabelForEvent(event));
51451
+ tui.pushEvent(renderLiveExecutionEvent(event, liveBoard, startedAt, {
51452
+ includeProgress: false
51453
+ }));
51454
+ } else {
51455
+ activity?.update(statusLabelForEvent(event));
51456
+ activity?.suspend();
51457
+ for (const line of renderLiveExecutionEvent(event, liveBoard, startedAt)) {
51458
+ console.log(line);
51459
+ }
51460
+ activity?.resume();
51461
+ }
51462
+ }
51463
+ });
51464
+ } finally {
51465
+ tui?.stop();
51466
+ activity?.stop();
51467
+ runtime.activeExecution = null;
51468
+ }
51469
+ const sessionPath = await saveSession(snapshot, request.cwd);
51470
+ await recordAdaptiveSessionOutcome(request.cwd, snapshot);
51471
+ runtime.currentCwd = request.cwd;
51472
+ const answer = await synthesizeWorkspaceAnalysisAnswer(request.cwd, hydrated.prompt, runtime, snapshot);
51473
+ const conversation = trimConversation([
51474
+ ...getConversation(runtime, runtime.focusRole),
51475
+ {
51476
+ role: "user",
51477
+ content: hydrated.prompt
51478
+ },
51479
+ {
51480
+ role: "assistant",
51481
+ content: answer
51482
+ }
51483
+ ]);
51484
+ runtime.conversations[runtime.focusRole] = conversation;
51485
+ console.log("");
51486
+ console.log(color(DIM, `elapsed: ${((Date.now() - startedAt) / 1e3).toFixed(1)}s`));
51487
+ console.log(color(DIM, renderExecutionTelemetry(telemetry, startedAt)));
51488
+ console.log(renderShellSessionSummary(snapshot, planPath, sessionPath));
51489
+ console.log("");
51490
+ console.log(color(DIM, "[analysis answer]"));
51491
+ console.log(renderTerminalMarkdown(answer));
51492
+ void drainQueuedWork(runtime);
51493
+ }
51150
51494
  async function resumeGoalExecution(cwd, runtime) {
51151
51495
  const session = await loadLatestSession(cwd);
51152
51496
  if (!session) {
@@ -52848,6 +53192,10 @@ async function startPendingRunExecution(cwd, runtime) {
52848
53192
  console.log(color(DIM, "Waiting on a hook question before starting the plan."));
52849
53193
  return;
52850
53194
  }
53195
+ if (!proposal) {
53196
+ console.log("No pending run proposal. Start with a build/change request or /run <goal>.");
53197
+ return;
53198
+ }
52851
53199
  if (proposal && planRequiresOperatorReview(proposal.plan)) {
52852
53200
  await emitShellHookEvent(
52853
53201
  proposal.request.cwd,
@@ -52864,6 +53212,15 @@ async function startPendingRunExecution(cwd, runtime) {
52864
53212
  }
52865
53213
  );
52866
53214
  }
53215
+ const confirmedProposal = runtime.pendingRunProposal;
53216
+ if (runtime.pendingHookElicitation) {
53217
+ console.log(color(DIM, "Waiting on a hook question before starting the plan."));
53218
+ return;
53219
+ }
53220
+ if (!confirmedProposal) {
53221
+ console.log(color(DIM, "Pending plan was cleared before execution could start."));
53222
+ return;
53223
+ }
52867
53224
  console.log(color(DIM, "Starting the approved plan..."));
52868
53225
  void executePendingRunProposal(runtime).catch((error2) => {
52869
53226
  console.error(error2 instanceof Error ? error2.message : String(error2));
@@ -52971,6 +53328,14 @@ async function handleShellCommand(cwd, input, state, runtime, execute) {
52971
53328
  return cwd;
52972
53329
  }
52973
53330
  if (!firstToken?.startsWith("/") && !TOP_LEVEL_COMMANDS.has(head) && !head.startsWith("-")) {
53331
+ if (looksLikeWorkspaceInfoPrompt(trimmed)) {
53332
+ if (runtime.activeExecution || runtime.queueDrainPromise || runtime.pendingRunProposal) {
53333
+ queuePlannerRequest(cwd, `Analyze the current repository and answer: ${trimmed}`, runtime, "run");
53334
+ return cwd;
53335
+ }
53336
+ await handleWorkspaceAnalysisPrompt(cwd, trimmed, runtime);
53337
+ return cwd;
53338
+ }
52974
53339
  const intent = inferPromptIntent(trimmed);
52975
53340
  if (intent === "run") {
52976
53341
  if (runtime.activeExecution || runtime.queueDrainPromise || runtime.pendingRunProposal) {