@nk070281sjv/cli 2.3.4 → 2.3.5

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.
Files changed (2) hide show
  1. package/dist/index.js +326 -90
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3553,14 +3553,14 @@ var require_emoji_regex = __commonJS({
3553
3553
  var require_string_width = __commonJS({
3554
3554
  "../../node_modules/.pnpm/string-width@4.2.3/node_modules/string-width/index.js"(exports, module) {
3555
3555
  "use strict";
3556
- var stripAnsi2 = require_strip_ansi();
3556
+ var stripAnsi3 = require_strip_ansi();
3557
3557
  var isFullwidthCodePoint2 = require_is_fullwidth_code_point();
3558
3558
  var emojiRegex2 = require_emoji_regex();
3559
3559
  var stringWidth2 = (string) => {
3560
3560
  if (typeof string !== "string" || string.length === 0) {
3561
3561
  return 0;
3562
3562
  }
3563
- string = stripAnsi2(string);
3563
+ string = stripAnsi3(string);
3564
3564
  if (string.length === 0) {
3565
3565
  return 0;
3566
3566
  }
@@ -4695,7 +4695,7 @@ var require_wrap_ansi = __commonJS({
4695
4695
  "../../node_modules/.pnpm/wrap-ansi@6.2.0/node_modules/wrap-ansi/index.js"(exports, module) {
4696
4696
  "use strict";
4697
4697
  var stringWidth2 = require_string_width();
4698
- var stripAnsi2 = require_strip_ansi();
4698
+ var stripAnsi3 = require_strip_ansi();
4699
4699
  var ansiStyles3 = require_ansi_styles();
4700
4700
  var ESCAPES3 = /* @__PURE__ */ new Set([
4701
4701
  "\x1B",
@@ -4707,7 +4707,7 @@ var require_wrap_ansi = __commonJS({
4707
4707
  var wrapWord2 = (rows, word, columns) => {
4708
4708
  const characters = [...word];
4709
4709
  let isInsideEscape = false;
4710
- let visible = stringWidth2(stripAnsi2(rows[rows.length - 1]));
4710
+ let visible = stringWidth2(stripAnsi3(rows[rows.length - 1]));
4711
4711
  for (const [index, character] of characters.entries()) {
4712
4712
  const characterLength = stringWidth2(character);
4713
4713
  if (visible + characterLength <= columns) {
@@ -30779,7 +30779,7 @@ ${hint}
30779
30779
  }
30780
30780
 
30781
30781
  // src/lib/version.ts
30782
- var CLI_VERSION = true ? "2.3.4" : createRequire(import.meta.url)("../../package.json").version;
30782
+ var CLI_VERSION = true ? "2.3.5" : createRequire(import.meta.url)("../../package.json").version;
30783
30783
 
30784
30784
  // src/lib/deps.ts
30785
30785
  init_src();
@@ -37513,6 +37513,9 @@ function buildOpenCodeRunArgs(input) {
37513
37513
  "--model",
37514
37514
  input.model
37515
37515
  ];
37516
+ if (input.attachUrl) {
37517
+ base.push("--attach", input.attachUrl, "--dir", input.cwd);
37518
+ }
37516
37519
  const limit = input.promptArgvLimit ?? DEFAULT_PROMPT_ARGV_LIMIT;
37517
37520
  if (input.prompt.length <= limit) {
37518
37521
  return [...base, input.prompt];
@@ -37534,17 +37537,34 @@ function parseOpenCodeJsonEvents(stdout) {
37534
37537
  var OpenCodeProcessRunner = class {
37535
37538
  spawn;
37536
37539
  promptArgvLimit;
37540
+ serveReadyTimeoutMs;
37541
+ server;
37537
37542
  constructor(options = {}) {
37538
37543
  this.spawn = options.spawn ?? spawnBinary;
37539
37544
  this.promptArgvLimit = options.promptArgvLimit ?? DEFAULT_PROMPT_ARGV_LIMIT;
37545
+ this.serveReadyTimeoutMs = options.serveReadyTimeoutMs ?? 15e3;
37540
37546
  }
37541
37547
  async run(request, signal) {
37542
37548
  const prompt = await readFile2(request.promptPath, "utf-8");
37549
+ let attachUrl;
37550
+ try {
37551
+ attachUrl = await this.ensureServer(request.cwd, signal);
37552
+ } catch (error) {
37553
+ return {
37554
+ id: request.id,
37555
+ exitCode: -1,
37556
+ stdout: "",
37557
+ stderr: error instanceof Error ? error.message : "Failed to start OpenCode server.",
37558
+ ndjsonEvents: []
37559
+ };
37560
+ }
37543
37561
  const args = buildOpenCodeRunArgs({
37544
37562
  agent: request.agent ?? "plan",
37545
37563
  model: request.model,
37546
37564
  prompt,
37547
37565
  promptPath: request.promptPath,
37566
+ cwd: request.cwd,
37567
+ attachUrl,
37548
37568
  promptArgvLimit: this.promptArgvLimit
37549
37569
  });
37550
37570
  return new Promise((resolve4) => {
@@ -37603,7 +37623,114 @@ var OpenCodeProcessRunner = class {
37603
37623
  });
37604
37624
  });
37605
37625
  }
37626
+ async close() {
37627
+ if (!this.server) return;
37628
+ const { child } = this.server;
37629
+ this.server = void 0;
37630
+ if (child.exitCode !== null || child.killed) return;
37631
+ await new Promise((resolve4) => {
37632
+ const timer = setTimeout(() => {
37633
+ child.kill("SIGKILL");
37634
+ resolve4();
37635
+ }, 1500);
37636
+ child.once("close", () => {
37637
+ clearTimeout(timer);
37638
+ resolve4();
37639
+ });
37640
+ child.kill();
37641
+ });
37642
+ }
37643
+ ensureServer(cwd, signal) {
37644
+ if (signal.aborted) {
37645
+ return Promise.reject(new Error("OpenCode server start aborted before spawn."));
37646
+ }
37647
+ if (this.server) {
37648
+ if (this.server.cwd !== cwd) {
37649
+ return Promise.reject(
37650
+ new Error(
37651
+ `OpenCode process runner cannot reuse one server across cwd values: ${this.server.cwd} vs ${cwd}.`
37652
+ )
37653
+ );
37654
+ }
37655
+ return this.server.ready;
37656
+ }
37657
+ const child = this.spawn(
37658
+ "opencode",
37659
+ ["serve", "--hostname", "127.0.0.1", "--port", "0"],
37660
+ {
37661
+ cwd,
37662
+ stdio: ["ignore", "pipe", "pipe"]
37663
+ }
37664
+ );
37665
+ const server = {
37666
+ cwd,
37667
+ child,
37668
+ ready: Promise.resolve(""),
37669
+ stdout: "",
37670
+ stderr: ""
37671
+ };
37672
+ this.server = server;
37673
+ server.ready = new Promise((resolve4, reject) => {
37674
+ let settled = false;
37675
+ const cleanup = () => {
37676
+ clearTimeout(timer);
37677
+ signal.removeEventListener("abort", abort);
37678
+ };
37679
+ const fail5 = (error) => {
37680
+ if (settled) return;
37681
+ settled = true;
37682
+ cleanup();
37683
+ this.server = void 0;
37684
+ child.kill();
37685
+ reject(error);
37686
+ };
37687
+ const succeed = (url) => {
37688
+ if (settled) return;
37689
+ settled = true;
37690
+ cleanup();
37691
+ resolve4(url);
37692
+ };
37693
+ const inspect = () => {
37694
+ const url = parseServeUrl(`${server.stdout}
37695
+ ${server.stderr}`);
37696
+ if (url) succeed(url);
37697
+ };
37698
+ const abort = () => fail5(new Error("OpenCode server start aborted."));
37699
+ const timer = setTimeout(() => {
37700
+ fail5(
37701
+ new Error(
37702
+ `Timed out waiting for OpenCode server to start. stdout: ${server.stdout.trim()} stderr: ${server.stderr.trim()}`
37703
+ )
37704
+ );
37705
+ }, this.serveReadyTimeoutMs);
37706
+ signal.addEventListener("abort", abort, { once: true });
37707
+ child.stdout?.setEncoding("utf-8");
37708
+ child.stderr?.setEncoding("utf-8");
37709
+ child.stdout?.on("data", (chunk) => {
37710
+ server.stdout += chunk;
37711
+ inspect();
37712
+ });
37713
+ child.stderr?.on("data", (chunk) => {
37714
+ server.stderr += chunk;
37715
+ inspect();
37716
+ });
37717
+ child.on("error", (error) => fail5(error));
37718
+ child.on("close", (code) => {
37719
+ if (!settled) {
37720
+ fail5(
37721
+ new Error(
37722
+ `OpenCode server exited before it was ready with code ${code ?? -1}. stdout: ${server.stdout.trim()} stderr: ${server.stderr.trim()}`
37723
+ )
37724
+ );
37725
+ }
37726
+ });
37727
+ });
37728
+ return server.ready;
37729
+ }
37606
37730
  };
37731
+ function parseServeUrl(output) {
37732
+ return output.match(/opencode server listening on (https?:\/\/\S+)/)?.[1];
37733
+ }
37607
37734
 
37608
37735
  // src/lib/agent-orchestrator/prompt-writer.ts
37609
37736
  import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
@@ -37675,11 +37802,21 @@ function reviewerPrompt(context, reviewer, promptPath, reviewPath) {
37675
37802
  `Round number: ${context.round}`,
37676
37803
  "Phase: reviews",
37677
37804
  "",
37678
- "Input files:",
37679
- `- ${relative3(context.roundDir, context.discoveredStandardsPath)}`,
37680
- `- ${relative3(context.roundDir, context.contextPath)}`,
37681
- `- ${relative3(context.roundDir, context.reviewBriefPath)}`,
37682
- ...context.requirementsPath ? [`- ${relative3(context.roundDir, context.requirementsPath)}`] : [],
37805
+ "Input files to read before reviewing:",
37806
+ `- ${context.discoveredStandardsPath}`,
37807
+ `- ${context.contextPath}`,
37808
+ `- ${context.reviewBriefPath}`,
37809
+ ...context.requirementsPath ? [`- ${context.requirementsPath}`] : [],
37810
+ "",
37811
+ "Input path rules:",
37812
+ "- Read exactly the files listed above; they are session-scoped snapshots.",
37813
+ "- Do not look for legacy root files such as .ocr/context.md, .ocr/discovered-standards.md, or .ocr/review-brief.md.",
37814
+ "- If a listed file is missing, report that failure in stdout instead of searching for substitutes.",
37815
+ "- Relative equivalents from the round directory:",
37816
+ ` - ${relative3(context.roundDir, context.discoveredStandardsPath)}`,
37817
+ ` - ${relative3(context.roundDir, context.contextPath)}`,
37818
+ ` - ${relative3(context.roundDir, context.reviewBriefPath)}`,
37819
+ ...context.requirementsPath ? [` - ${relative3(context.roundDir, context.requirementsPath)}`] : [],
37683
37820
  "",
37684
37821
  "Output contract:",
37685
37822
  `- Return review markdown through stdout for ${reviewPath}.`,
@@ -37705,13 +37842,25 @@ function pipelinePrompt(context, stage, agent, promptPath, artifactPath) {
37705
37842
  `Round number: ${context.round}`,
37706
37843
  `Phase: ${stage}`,
37707
37844
  "",
37708
- "Input files:",
37709
- `- ${relative3(context.roundDir, context.discoveredStandardsPath)}`,
37710
- `- ${relative3(context.roundDir, context.contextPath)}`,
37711
- `- ${relative3(context.roundDir, context.reviewBriefPath)}`,
37712
- "- reviews/*.md",
37713
- ...stage !== "aggregation" ? ["- aggregation.md"] : [],
37714
- ...stage === "synthesis" ? ["- validation.md"] : [],
37845
+ "Input files to read before processing:",
37846
+ `- ${context.discoveredStandardsPath}`,
37847
+ `- ${context.contextPath}`,
37848
+ `- ${context.reviewBriefPath}`,
37849
+ `- ${join26(context.roundDir, "reviews")}`,
37850
+ ...stage !== "aggregation" ? [join26(context.roundDir, "aggregation.md")] : [],
37851
+ ...stage === "synthesis" ? [join26(context.roundDir, "validation.md")] : [],
37852
+ "",
37853
+ "Input path rules:",
37854
+ "- Read exactly the files and directories listed above; they are session-scoped snapshots.",
37855
+ "- Do not look for legacy root files such as .ocr/context.md, .ocr/discovered-standards.md, or .ocr/review-brief.md.",
37856
+ "- If a listed file is missing, report that failure in stdout instead of searching for substitutes.",
37857
+ "- Relative equivalents from the round directory:",
37858
+ ` - ${relative3(context.roundDir, context.discoveredStandardsPath)}`,
37859
+ ` - ${relative3(context.roundDir, context.contextPath)}`,
37860
+ ` - ${relative3(context.roundDir, context.reviewBriefPath)}`,
37861
+ " - reviews/",
37862
+ ...stage !== "aggregation" ? [" - aggregation.md"] : [],
37863
+ ...stage === "synthesis" ? [" - validation.md"] : [],
37715
37864
  "",
37716
37865
  "Output contract:",
37717
37866
  `- Return markdown through stdout for ${artifactPath}.`,
@@ -37789,6 +37938,8 @@ async function runReviewerPhase(input) {
37789
37938
  );
37790
37939
  const failed = results.find((result) => result.exitCode !== 0);
37791
37940
  if (failed) {
37941
+ const failedRequest = requests.find((request) => request.id === failed.id);
37942
+ const diagnostic = buildFailureDiagnostic(failed, failedRequest);
37792
37943
  await writeRoundArtifact(
37793
37944
  input.context.roundDir,
37794
37945
  "failure-summary.json",
@@ -37798,7 +37949,12 @@ async function runReviewerPhase(input) {
37798
37949
  phase: "reviews",
37799
37950
  failed_id: failed.id,
37800
37951
  exit_code: failed.exitCode,
37952
+ model: failedRequest?.model,
37953
+ prompt_path: failedRequest?.promptPath,
37954
+ cwd: failedRequest?.cwd,
37955
+ diagnostic,
37801
37956
  stderr: failed.stderr,
37957
+ stdout_excerpt: excerpt(failed.stdout),
37802
37958
  results: sortResults(results, requests).map((result) => ({
37803
37959
  id: result.id,
37804
37960
  exit_code: result.exitCode
@@ -37813,7 +37969,14 @@ async function runReviewerPhase(input) {
37813
37969
  failedId: failed.id,
37814
37970
  errors: [
37815
37971
  `Reviewer ${failed.id} failed with exit code ${failed.exitCode}.`,
37816
- ...failed.stderr.trim() ? [failed.stderr.trim()] : []
37972
+ diagnostic.summary,
37973
+ ...diagnostic.hint ? [diagnostic.hint] : [],
37974
+ ...failedRequest ? [
37975
+ `Model: ${failedRequest.model}`,
37976
+ `Prompt: ${failedRequest.promptPath}`
37977
+ ] : [],
37978
+ ...diagnostic.stderr_excerpt ? [`stderr: ${diagnostic.stderr_excerpt}`] : [],
37979
+ ...diagnostic.stdout_excerpt ? [`stdout: ${diagnostic.stdout_excerpt}`] : []
37817
37980
  ],
37818
37981
  results: sortResults(results, requests)
37819
37982
  };
@@ -37833,72 +37996,76 @@ async function runOpenCodeProcessAgentReview(input) {
37833
37996
  const runner = input.runner ?? new OpenCodeProcessRunner();
37834
37997
  const journal = input.journal ?? new NoopAgentLifecycleJournal();
37835
37998
  const emit = input.emit ?? (() => void 0);
37836
- emit({ event: "context:start", session_id: input.sessionId, round: input.round });
37837
- const context = await prepareReviewContext({
37838
- sessionId: input.sessionId,
37839
- sessionDir: input.sessionDir,
37840
- round: input.round,
37841
- requirements: input.requirements
37842
- });
37843
- const snapshot = await writePromptSnapshots({
37844
- context,
37845
- reviewers: input.reviewers,
37846
- pipelineAgents: input.pipelineAgents
37847
- });
37848
- emit({
37849
- event: "prompts:written",
37850
- session_id: input.sessionId,
37851
- round: input.round,
37852
- reviewers: snapshot.resolvedTeam.reviewers.length
37853
- });
37854
- emit({ event: "reviews:start", session_id: input.sessionId, round: input.round });
37855
- const reviewerResult = await runReviewerPhase({
37856
- context,
37857
- resolvedTeam: snapshot.resolvedTeam,
37858
- cwd: input.cwd,
37859
- runner,
37860
- journal
37861
- });
37862
- if (!reviewerResult.ok) {
37999
+ try {
38000
+ emit({ event: "context:start", session_id: input.sessionId, round: input.round });
38001
+ const context = await prepareReviewContext({
38002
+ sessionId: input.sessionId,
38003
+ sessionDir: input.sessionDir,
38004
+ round: input.round,
38005
+ requirements: input.requirements
38006
+ });
38007
+ const snapshot = await writePromptSnapshots({
38008
+ context,
38009
+ reviewers: input.reviewers,
38010
+ pipelineAgents: input.pipelineAgents
38011
+ });
37863
38012
  emit({
37864
- event: "reviews:failed",
38013
+ event: "prompts:written",
37865
38014
  session_id: input.sessionId,
37866
- failed_id: reviewerResult.failedId
38015
+ round: input.round,
38016
+ reviewers: snapshot.resolvedTeam.reviewers.length
37867
38017
  });
37868
- return {
37869
- ok: false,
38018
+ emit({ event: "reviews:start", session_id: input.sessionId, round: input.round });
38019
+ const reviewerResult = await runReviewerPhase({
38020
+ context,
37870
38021
  resolvedTeam: snapshot.resolvedTeam,
37871
- errors: reviewerResult.errors
37872
- };
37873
- }
37874
- emit({ event: "reviews:complete", session_id: input.sessionId });
37875
- emit({ event: "pipeline:start", session_id: input.sessionId, round: input.round });
37876
- const pipelineResult = await runPipelineStages({
37877
- context,
37878
- resolvedTeam: snapshot.resolvedTeam,
37879
- cwd: input.cwd,
37880
- runner,
37881
- journal
37882
- });
37883
- if (!pipelineResult.ok) {
37884
- emit({
37885
- event: "pipeline:failed",
37886
- session_id: input.sessionId,
37887
- failed_stage: pipelineResult.failedStage
38022
+ cwd: input.cwd,
38023
+ runner,
38024
+ journal
37888
38025
  });
38026
+ if (!reviewerResult.ok) {
38027
+ emit({
38028
+ event: "reviews:failed",
38029
+ session_id: input.sessionId,
38030
+ failed_id: reviewerResult.failedId
38031
+ });
38032
+ return {
38033
+ ok: false,
38034
+ resolvedTeam: snapshot.resolvedTeam,
38035
+ errors: reviewerResult.errors
38036
+ };
38037
+ }
38038
+ emit({ event: "reviews:complete", session_id: input.sessionId });
38039
+ emit({ event: "pipeline:start", session_id: input.sessionId, round: input.round });
38040
+ const pipelineResult = await runPipelineStages({
38041
+ context,
38042
+ resolvedTeam: snapshot.resolvedTeam,
38043
+ cwd: input.cwd,
38044
+ runner,
38045
+ journal
38046
+ });
38047
+ if (!pipelineResult.ok) {
38048
+ emit({
38049
+ event: "pipeline:failed",
38050
+ session_id: input.sessionId,
38051
+ failed_stage: pipelineResult.failedStage
38052
+ });
38053
+ return {
38054
+ ok: false,
38055
+ resolvedTeam: snapshot.resolvedTeam,
38056
+ errors: pipelineResult.errors
38057
+ };
38058
+ }
38059
+ emit({ event: "pipeline:complete", session_id: input.sessionId });
37889
38060
  return {
37890
- ok: false,
38061
+ ok: true,
37891
38062
  resolvedTeam: snapshot.resolvedTeam,
37892
- errors: pipelineResult.errors
38063
+ reviewerResults: reviewerResult.results,
38064
+ pipelineResults: pipelineResult.results
37893
38065
  };
38066
+ } finally {
38067
+ await runner.close?.();
37894
38068
  }
37895
- emit({ event: "pipeline:complete", session_id: input.sessionId });
37896
- return {
37897
- ok: true,
37898
- resolvedTeam: snapshot.resolvedTeam,
37899
- reviewerResults: reviewerResult.results,
37900
- pipelineResults: pipelineResult.results
37901
- };
37902
38069
  }
37903
38070
  async function runPipelineStages(input) {
37904
38071
  const journal = input.journal ?? new NoopAgentLifecycleJournal();
@@ -37923,31 +38090,35 @@ async function runPipelineStages(input) {
37923
38090
  phase: stage
37924
38091
  });
37925
38092
  await journal.beat(agentSessionId);
37926
- const result = await input.runner.run(
37927
- {
37928
- id: stage,
37929
- model: ref.model,
37930
- promptPath: join27(input.context.roundDir, ref.promptPath),
37931
- cwd: input.cwd,
37932
- phase: stage
37933
- },
37934
- new AbortController().signal
37935
- );
38093
+ const request = {
38094
+ id: stage,
38095
+ model: ref.model,
38096
+ promptPath: join27(input.context.roundDir, ref.promptPath),
38097
+ cwd: input.cwd,
38098
+ phase: stage
38099
+ };
38100
+ const result = await input.runner.run(request, new AbortController().signal);
37936
38101
  results.push(result);
37937
38102
  const vendorId = extractVendorSessionId(result.ndjsonEvents);
37938
38103
  if (vendorId) await journal.bindVendorId(agentSessionId, vendorId);
37939
38104
  if (result.exitCode !== 0) {
38105
+ const diagnostic = buildFailureDiagnostic(result, request);
37940
38106
  await journal.endInstance(agentSessionId, {
37941
38107
  exitCode: result.exitCode,
37942
- note: result.stderr.trim() || void 0
38108
+ note: diagnostic.summary
37943
38109
  });
37944
- await writePipelineFailure(input.context.roundDir, stage, result, results);
38110
+ await writePipelineFailure(input.context.roundDir, stage, result, results, [], diagnostic);
37945
38111
  return {
37946
38112
  ok: false,
37947
38113
  failedStage: stage,
37948
38114
  errors: [
37949
38115
  `Pipeline stage ${stage} failed with exit code ${result.exitCode}.`,
37950
- ...result.stderr.trim() ? [result.stderr.trim()] : []
38116
+ diagnostic.summary,
38117
+ ...diagnostic.hint ? [diagnostic.hint] : [],
38118
+ `Model: ${request.model}`,
38119
+ `Prompt: ${request.promptPath}`,
38120
+ ...diagnostic.stderr_excerpt ? [`stderr: ${diagnostic.stderr_excerpt}`] : [],
38121
+ ...diagnostic.stdout_excerpt ? [`stdout: ${diagnostic.stdout_excerpt}`] : []
37951
38122
  ],
37952
38123
  results
37953
38124
  };
@@ -37985,7 +38156,7 @@ function validateStageArtifact(roundDir, stage, artifactPath) {
37985
38156
  function isNonEmptyFile(path2) {
37986
38157
  return existsSync22(path2) && statSync5(path2).isFile() && statSync5(path2).size > 0;
37987
38158
  }
37988
- async function writePipelineFailure(roundDir, stage, result, results, validationErrors = []) {
38159
+ async function writePipelineFailure(roundDir, stage, result, results, validationErrors = [], diagnostic = buildFailureDiagnostic(result)) {
37989
38160
  await writeRoundArtifact(
37990
38161
  roundDir,
37991
38162
  "failure-summary.json",
@@ -37995,7 +38166,9 @@ async function writePipelineFailure(roundDir, stage, result, results, validation
37995
38166
  phase: stage,
37996
38167
  failed_id: result.id,
37997
38168
  exit_code: result.exitCode,
38169
+ diagnostic,
37998
38170
  stderr: result.stderr,
38171
+ stdout_excerpt: excerpt(result.stdout),
37999
38172
  validation_errors: validationErrors,
38000
38173
  results: results.map((item) => ({
38001
38174
  id: item.id,
@@ -38007,6 +38180,63 @@ async function writePipelineFailure(roundDir, stage, result, results, validation
38007
38180
  )
38008
38181
  );
38009
38182
  }
38183
+ function buildFailureDiagnostic(result, request) {
38184
+ const stderr = stripAnsi2(result.stderr).trim();
38185
+ const stdoutError = extractOpenCodeErrorMessage(result.ndjsonEvents);
38186
+ const combined = [stderr, stdoutError].filter(Boolean).join("\n");
38187
+ const model = request?.model ?? "";
38188
+ if (/session not found/i.test(combined)) {
38189
+ return {
38190
+ summary: 'OpenCode reported "Session not found" while running this process agent. This is an OpenCode subprocess/session error, not an OCR decision to simulate or skip reviewers.',
38191
+ hint: "OCR uses a persistent `opencode serve` process and runs agents through `opencode run --attach`. Inspect the saved prompt path and OpenCode logs to isolate the OpenCode session failure.",
38192
+ stderr_excerpt: excerpt(stderr),
38193
+ stdout_excerpt: excerpt(result.stdout)
38194
+ };
38195
+ }
38196
+ if (/Unexpected server error/i.test(combined)) {
38197
+ const hint = model && !model.includes("/") ? `The configured model "${model}" does not include an OpenCode provider prefix. Use the exact id from \`opencode models\`, for example \`lumi-deep-seek/DeepSeek-V4-Pro\`.` : `Verify that the configured model "${model}" exists in \`opencode models\` and that the provider is available.`;
38198
+ return {
38199
+ summary: stdoutError || "OpenCode returned an unexpected server error.",
38200
+ hint,
38201
+ stderr_excerpt: excerpt(stderr),
38202
+ stdout_excerpt: excerpt(result.stdout)
38203
+ };
38204
+ }
38205
+ if (combined) {
38206
+ return {
38207
+ summary: combined,
38208
+ stderr_excerpt: excerpt(stderr),
38209
+ stdout_excerpt: excerpt(result.stdout)
38210
+ };
38211
+ }
38212
+ return {
38213
+ summary: "The OpenCode subprocess exited without a useful error message.",
38214
+ hint: "Inspect failure-summary.json and rerun the saved prompt manually with the recorded model to reproduce the failure.",
38215
+ stdout_excerpt: excerpt(result.stdout)
38216
+ };
38217
+ }
38218
+ function extractOpenCodeErrorMessage(events) {
38219
+ for (const event of events) {
38220
+ if (!isRecord(event) || event.type !== "error") continue;
38221
+ const error = event.error;
38222
+ if (!isRecord(error)) continue;
38223
+ const data = error.data;
38224
+ if (isRecord(data) && typeof data.message === "string") return data.message;
38225
+ if (typeof error.message === "string") return error.message;
38226
+ }
38227
+ return void 0;
38228
+ }
38229
+ function excerpt(value, limit = 1200) {
38230
+ const stripped = stripAnsi2(value ?? "").trim();
38231
+ if (!stripped) return void 0;
38232
+ return stripped.length > limit ? `${stripped.slice(0, limit)}...` : stripped;
38233
+ }
38234
+ function stripAnsi2(value) {
38235
+ return value.replace(/\u001b\[[0-9;]*m/g, "");
38236
+ }
38237
+ function isRecord(value) {
38238
+ return typeof value === "object" && value !== null;
38239
+ }
38010
38240
 
38011
38241
  // src/commands/review.ts
38012
38242
  function fail3(message) {
@@ -38075,7 +38305,13 @@ var runAgentsSubcommand = new Command("run-agents").description("Run OCR review
38075
38305
  });
38076
38306
  } else {
38077
38307
  if (!sessionId) {
38078
- throw new Error("run-agents requires --session-id unless --fresh is set");
38308
+ throw new Error(
38309
+ [
38310
+ "run-agents requires either --fresh or --session-id.",
38311
+ "Start a new process-agent review with: ocr review run-agents --fresh",
38312
+ "Resume a prepared workflow round with: ocr review run-agents --session-id <id> --round <n>"
38313
+ ].join("\n")
38314
+ );
38079
38315
  }
38080
38316
  const session = getSession(db, sessionId);
38081
38317
  if (!session) throw new Error(`Workflow session not found: ${sessionId}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nk070281sjv/cli",
3
- "version": "2.3.4",
3
+ "version": "2.3.5",
4
4
  "description": "CLI for Open Code Review - Multi-environment setup and progress tracking",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@inquirer/prompts": "^7.2.0",
40
- "@nk070281sjv/agents": "2.3.4",
40
+ "@nk070281sjv/agents": "2.3.5",
41
41
  "chalk": "^5.4.1",
42
42
  "chokidar": "^4.0.3",
43
43
  "commander": "^13.0.0",