@nk070281sjv/cli 2.3.5 → 2.3.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.js +291 -31
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -30779,7 +30779,7 @@ ${hint}
|
|
|
30779
30779
|
}
|
|
30780
30780
|
|
|
30781
30781
|
// src/lib/version.ts
|
|
30782
|
-
var CLI_VERSION = true ? "2.3.
|
|
30782
|
+
var CLI_VERSION = true ? "2.3.7" : createRequire(import.meta.url)("../../package.json").version;
|
|
30783
30783
|
|
|
30784
30784
|
// src/lib/deps.ts
|
|
30785
30785
|
init_src();
|
|
@@ -31821,9 +31821,9 @@ var NodeFsHandler = class {
|
|
|
31821
31821
|
if (this.fsw.closed) {
|
|
31822
31822
|
return;
|
|
31823
31823
|
}
|
|
31824
|
-
const
|
|
31824
|
+
const dirname13 = sysPath.dirname(file);
|
|
31825
31825
|
const basename9 = sysPath.basename(file);
|
|
31826
|
-
const parent = this.fsw._getWatchedDir(
|
|
31826
|
+
const parent = this.fsw._getWatchedDir(dirname13);
|
|
31827
31827
|
let prevStats = stats;
|
|
31828
31828
|
if (parent.has(basename9))
|
|
31829
31829
|
return;
|
|
@@ -31850,7 +31850,7 @@ var NodeFsHandler = class {
|
|
|
31850
31850
|
prevStats = newStats2;
|
|
31851
31851
|
}
|
|
31852
31852
|
} catch (error) {
|
|
31853
|
-
this.fsw._remove(
|
|
31853
|
+
this.fsw._remove(dirname13, basename9);
|
|
31854
31854
|
}
|
|
31855
31855
|
} else if (parent.has(basename9)) {
|
|
31856
31856
|
const at = newStats.atimeMs;
|
|
@@ -37409,6 +37409,10 @@ async function writeRoundArtifact(roundDir, relativePath, content) {
|
|
|
37409
37409
|
import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "node:fs/promises";
|
|
37410
37410
|
import { existsSync as existsSync21 } from "node:fs";
|
|
37411
37411
|
import { dirname as dirname10, join as join25 } from "node:path";
|
|
37412
|
+
import { execFile } from "node:child_process";
|
|
37413
|
+
import { promisify } from "node:util";
|
|
37414
|
+
var execFileAsync = promisify(execFile);
|
|
37415
|
+
var GIT_SNAPSHOT_LIMIT = 2e4;
|
|
37412
37416
|
async function prepareReviewContext(input) {
|
|
37413
37417
|
const roundDir = join25(input.sessionDir, "rounds", `round-${input.round}`);
|
|
37414
37418
|
await mkdir2(roundDir, { recursive: true });
|
|
@@ -37425,6 +37429,7 @@ async function prepareReviewContext(input) {
|
|
|
37425
37429
|
"# Review Context\n\nNo shared review context has been recorded yet.\n"
|
|
37426
37430
|
);
|
|
37427
37431
|
const reviewBriefPath = join25(roundDir, "review-brief.md");
|
|
37432
|
+
const gitSnapshot = input.cwd ? await collectGitSnapshot(input.cwd) : void 0;
|
|
37428
37433
|
await writeFile2(
|
|
37429
37434
|
reviewBriefPath,
|
|
37430
37435
|
[
|
|
@@ -37434,6 +37439,37 @@ async function prepareReviewContext(input) {
|
|
|
37434
37439
|
`Round: ${input.round}`,
|
|
37435
37440
|
`Discovered standards: ${discoveredStandardsPath}`,
|
|
37436
37441
|
`Shared context: ${contextPath}`,
|
|
37442
|
+
...input.cwd ? [`Repository root: ${input.cwd}`] : [],
|
|
37443
|
+
"",
|
|
37444
|
+
"## Scope",
|
|
37445
|
+
"",
|
|
37446
|
+
"Review the current working tree changes. Focus on changed files and the unchanged code needed to validate those changes.",
|
|
37447
|
+
"Do not review generated OCR session artifacts under `.ocr/sessions/` unless they are explicitly part of the requested change.",
|
|
37448
|
+
"",
|
|
37449
|
+
...gitSnapshot ? [
|
|
37450
|
+
"## Git Status",
|
|
37451
|
+
"",
|
|
37452
|
+
fenced("text", gitSnapshot.status || "No working tree changes reported."),
|
|
37453
|
+
"",
|
|
37454
|
+
"## Changed Files",
|
|
37455
|
+
"",
|
|
37456
|
+
fenced("text", gitSnapshot.changedFiles || "No changed files reported."),
|
|
37457
|
+
"",
|
|
37458
|
+
"## Diff Stat",
|
|
37459
|
+
"",
|
|
37460
|
+
fenced("text", gitSnapshot.diffStat || "No diff stat available."),
|
|
37461
|
+
""
|
|
37462
|
+
] : [
|
|
37463
|
+
"## Git Snapshot",
|
|
37464
|
+
"",
|
|
37465
|
+
"No repository snapshot was captured by the CLI.",
|
|
37466
|
+
""
|
|
37467
|
+
],
|
|
37468
|
+
"## Reviewer Guidance",
|
|
37469
|
+
"",
|
|
37470
|
+
"- Read this brief first, then inspect relevant changed files and surrounding code as needed.",
|
|
37471
|
+
"- Prefer targeted `git diff -- <path>` and file reads over broad repository scans.",
|
|
37472
|
+
"- Report only findings supported by code evidence and concrete file references.",
|
|
37437
37473
|
""
|
|
37438
37474
|
].join("\n"),
|
|
37439
37475
|
"utf-8"
|
|
@@ -37478,6 +37514,49 @@ async function prepareReviewContext(input) {
|
|
|
37478
37514
|
requirementsExpected: Boolean(requirementsPath)
|
|
37479
37515
|
};
|
|
37480
37516
|
}
|
|
37517
|
+
async function collectGitSnapshot(cwd) {
|
|
37518
|
+
const [status, cachedFiles, worktreeFiles, diffStat] = await Promise.all([
|
|
37519
|
+
runGit(cwd, ["status", "--short"]),
|
|
37520
|
+
runGit(cwd, ["diff", "--cached", "--name-only"]),
|
|
37521
|
+
runGit(cwd, ["diff", "--name-only", "HEAD"]),
|
|
37522
|
+
runGit(cwd, ["diff", "--stat", "HEAD"])
|
|
37523
|
+
]);
|
|
37524
|
+
return {
|
|
37525
|
+
status,
|
|
37526
|
+
changedFiles: uniqueLines(`${cachedFiles}
|
|
37527
|
+
${worktreeFiles}`),
|
|
37528
|
+
diffStat
|
|
37529
|
+
};
|
|
37530
|
+
}
|
|
37531
|
+
async function runGit(cwd, args) {
|
|
37532
|
+
try {
|
|
37533
|
+
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
37534
|
+
cwd,
|
|
37535
|
+
maxBuffer: GIT_SNAPSHOT_LIMIT * 2
|
|
37536
|
+
});
|
|
37537
|
+
return truncate(`${stdout}${stderr ? `
|
|
37538
|
+
${stderr}` : ""}`.trim());
|
|
37539
|
+
} catch (error) {
|
|
37540
|
+
if (error && typeof error === "object" && "stdout" in error && typeof error.stdout === "string") {
|
|
37541
|
+
return truncate(error.stdout.trim());
|
|
37542
|
+
}
|
|
37543
|
+
return "";
|
|
37544
|
+
}
|
|
37545
|
+
}
|
|
37546
|
+
function uniqueLines(value) {
|
|
37547
|
+
const lines = value.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
37548
|
+
return [...new Set(lines)].join("\n");
|
|
37549
|
+
}
|
|
37550
|
+
function truncate(value) {
|
|
37551
|
+
if (value.length <= GIT_SNAPSHOT_LIMIT) return value;
|
|
37552
|
+
return `${value.slice(0, GIT_SNAPSHOT_LIMIT)}
|
|
37553
|
+
[truncated by OCR CLI]`;
|
|
37554
|
+
}
|
|
37555
|
+
function fenced(language, value) {
|
|
37556
|
+
return `\`\`\`${language}
|
|
37557
|
+
${value}
|
|
37558
|
+
\`\`\``;
|
|
37559
|
+
}
|
|
37481
37560
|
async function ensureMarkdownFile(path2, fallbackContent) {
|
|
37482
37561
|
if (existsSync21(path2)) {
|
|
37483
37562
|
await readFile(path2, "utf-8");
|
|
@@ -37516,6 +37595,9 @@ function buildOpenCodeRunArgs(input) {
|
|
|
37516
37595
|
if (input.attachUrl) {
|
|
37517
37596
|
base.push("--attach", input.attachUrl, "--dir", input.cwd);
|
|
37518
37597
|
}
|
|
37598
|
+
if (input.pure) {
|
|
37599
|
+
base.push("--pure");
|
|
37600
|
+
}
|
|
37519
37601
|
const limit = input.promptArgvLimit ?? DEFAULT_PROMPT_ARGV_LIMIT;
|
|
37520
37602
|
if (input.prompt.length <= limit) {
|
|
37521
37603
|
return [...base, input.prompt];
|
|
@@ -37534,15 +37616,25 @@ function parseOpenCodeJsonEvents(stdout) {
|
|
|
37534
37616
|
}
|
|
37535
37617
|
return events;
|
|
37536
37618
|
}
|
|
37619
|
+
function extractOpenCodeText(events) {
|
|
37620
|
+
return events.map((event) => {
|
|
37621
|
+
if (!isRecord(event)) return "";
|
|
37622
|
+
const part = event.part;
|
|
37623
|
+
if (!isRecord(part) || part.type !== "text") return "";
|
|
37624
|
+
return typeof part.text === "string" ? part.text : "";
|
|
37625
|
+
}).join("").trimStart();
|
|
37626
|
+
}
|
|
37537
37627
|
var OpenCodeProcessRunner = class {
|
|
37538
37628
|
spawn;
|
|
37539
37629
|
promptArgvLimit;
|
|
37540
37630
|
serveReadyTimeoutMs;
|
|
37631
|
+
pure;
|
|
37541
37632
|
server;
|
|
37542
37633
|
constructor(options = {}) {
|
|
37543
37634
|
this.spawn = options.spawn ?? spawnBinary;
|
|
37544
37635
|
this.promptArgvLimit = options.promptArgvLimit ?? DEFAULT_PROMPT_ARGV_LIMIT;
|
|
37545
|
-
this.serveReadyTimeoutMs = options.serveReadyTimeoutMs ??
|
|
37636
|
+
this.serveReadyTimeoutMs = options.serveReadyTimeoutMs ?? 12e4;
|
|
37637
|
+
this.pure = options.pure ?? true;
|
|
37546
37638
|
}
|
|
37547
37639
|
async run(request, signal) {
|
|
37548
37640
|
const prompt = await readFile2(request.promptPath, "utf-8");
|
|
@@ -37555,7 +37647,8 @@ var OpenCodeProcessRunner = class {
|
|
|
37555
37647
|
exitCode: -1,
|
|
37556
37648
|
stdout: "",
|
|
37557
37649
|
stderr: error instanceof Error ? error.message : "Failed to start OpenCode server.",
|
|
37558
|
-
ndjsonEvents: []
|
|
37650
|
+
ndjsonEvents: [],
|
|
37651
|
+
outputText: ""
|
|
37559
37652
|
};
|
|
37560
37653
|
}
|
|
37561
37654
|
const args = buildOpenCodeRunArgs({
|
|
@@ -37565,6 +37658,7 @@ var OpenCodeProcessRunner = class {
|
|
|
37565
37658
|
promptPath: request.promptPath,
|
|
37566
37659
|
cwd: request.cwd,
|
|
37567
37660
|
attachUrl,
|
|
37661
|
+
pure: this.pure,
|
|
37568
37662
|
promptArgvLimit: this.promptArgvLimit
|
|
37569
37663
|
});
|
|
37570
37664
|
return new Promise((resolve4) => {
|
|
@@ -37574,7 +37668,8 @@ var OpenCodeProcessRunner = class {
|
|
|
37574
37668
|
exitCode: -1,
|
|
37575
37669
|
stdout: "",
|
|
37576
37670
|
stderr: "OpenCode process aborted before spawn.",
|
|
37577
|
-
ndjsonEvents: []
|
|
37671
|
+
ndjsonEvents: [],
|
|
37672
|
+
outputText: ""
|
|
37578
37673
|
});
|
|
37579
37674
|
return;
|
|
37580
37675
|
}
|
|
@@ -37604,21 +37699,25 @@ var OpenCodeProcessRunner = class {
|
|
|
37604
37699
|
stderr += chunk;
|
|
37605
37700
|
});
|
|
37606
37701
|
child.on("error", (err) => {
|
|
37702
|
+
const ndjsonEvents = parseOpenCodeJsonEvents(stdout);
|
|
37607
37703
|
settle({
|
|
37608
37704
|
id: request.id,
|
|
37609
37705
|
exitCode: -1,
|
|
37610
37706
|
stdout,
|
|
37611
37707
|
stderr: stderr || err.message,
|
|
37612
|
-
ndjsonEvents
|
|
37708
|
+
ndjsonEvents,
|
|
37709
|
+
outputText: extractOpenCodeText(ndjsonEvents)
|
|
37613
37710
|
});
|
|
37614
37711
|
});
|
|
37615
37712
|
child.on("close", (code) => {
|
|
37713
|
+
const ndjsonEvents = parseOpenCodeJsonEvents(stdout);
|
|
37616
37714
|
settle({
|
|
37617
37715
|
id: request.id,
|
|
37618
37716
|
exitCode: code ?? -1,
|
|
37619
37717
|
stdout,
|
|
37620
37718
|
stderr,
|
|
37621
|
-
ndjsonEvents
|
|
37719
|
+
ndjsonEvents,
|
|
37720
|
+
outputText: extractOpenCodeText(ndjsonEvents)
|
|
37622
37721
|
});
|
|
37623
37722
|
});
|
|
37624
37723
|
});
|
|
@@ -37656,7 +37755,14 @@ var OpenCodeProcessRunner = class {
|
|
|
37656
37755
|
}
|
|
37657
37756
|
const child = this.spawn(
|
|
37658
37757
|
"opencode",
|
|
37659
|
-
[
|
|
37758
|
+
[
|
|
37759
|
+
"serve",
|
|
37760
|
+
"--hostname",
|
|
37761
|
+
"127.0.0.1",
|
|
37762
|
+
"--port",
|
|
37763
|
+
"0",
|
|
37764
|
+
...this.pure ? ["--pure"] : []
|
|
37765
|
+
],
|
|
37660
37766
|
{
|
|
37661
37767
|
cwd,
|
|
37662
37768
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -37731,10 +37837,13 @@ ${server.stderr}`);
|
|
|
37731
37837
|
function parseServeUrl(output) {
|
|
37732
37838
|
return output.match(/opencode server listening on (https?:\/\/\S+)/)?.[1];
|
|
37733
37839
|
}
|
|
37840
|
+
function isRecord(value) {
|
|
37841
|
+
return typeof value === "object" && value !== null;
|
|
37842
|
+
}
|
|
37734
37843
|
|
|
37735
37844
|
// src/lib/agent-orchestrator/prompt-writer.ts
|
|
37736
37845
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
37737
|
-
import { join as join26, relative as relative3 } from "node:path";
|
|
37846
|
+
import { dirname as dirname11, join as join26, relative as relative3 } from "node:path";
|
|
37738
37847
|
async function writePromptSnapshots(input) {
|
|
37739
37848
|
const promptsDir = join26(input.context.roundDir, "prompts");
|
|
37740
37849
|
const reviewsDir = join26(input.context.roundDir, "reviews");
|
|
@@ -37803,6 +37912,7 @@ function reviewerPrompt(context, reviewer, promptPath, reviewPath) {
|
|
|
37803
37912
|
"Phase: reviews",
|
|
37804
37913
|
"",
|
|
37805
37914
|
"Input files to read before reviewing:",
|
|
37915
|
+
`- ${reviewerPersonaPath(context, reviewer)}`,
|
|
37806
37916
|
`- ${context.discoveredStandardsPath}`,
|
|
37807
37917
|
`- ${context.contextPath}`,
|
|
37808
37918
|
`- ${context.reviewBriefPath}`,
|
|
@@ -37816,8 +37926,14 @@ function reviewerPrompt(context, reviewer, promptPath, reviewPath) {
|
|
|
37816
37926
|
` - ${relative3(context.roundDir, context.discoveredStandardsPath)}`,
|
|
37817
37927
|
` - ${relative3(context.roundDir, context.contextPath)}`,
|
|
37818
37928
|
` - ${relative3(context.roundDir, context.reviewBriefPath)}`,
|
|
37929
|
+
` - ${relative3(context.roundDir, reviewerPersonaPath(context, reviewer))}`,
|
|
37819
37930
|
...context.requirementsPath ? [` - ${relative3(context.roundDir, context.requirementsPath)}`] : [],
|
|
37820
37931
|
"",
|
|
37932
|
+
"Review focus:",
|
|
37933
|
+
"- Apply the reviewer persona file as your primary review lens.",
|
|
37934
|
+
"- Use the review brief as the source of truth for changed files and scope.",
|
|
37935
|
+
"- Inspect surrounding source code only when needed to validate a finding.",
|
|
37936
|
+
"",
|
|
37821
37937
|
"Output contract:",
|
|
37822
37938
|
`- Return review markdown through stdout for ${reviewPath}.`,
|
|
37823
37939
|
"- Do not write files directly.",
|
|
@@ -37832,6 +37948,10 @@ function reviewerPrompt(context, reviewer, promptPath, reviewPath) {
|
|
|
37832
37948
|
""
|
|
37833
37949
|
].join("\n");
|
|
37834
37950
|
}
|
|
37951
|
+
function reviewerPersonaPath(context, reviewer) {
|
|
37952
|
+
const ocrDir = dirname11(dirname11(context.sessionDir));
|
|
37953
|
+
return join26(ocrDir, "skills", "references", "reviewers", `${reviewer.persona}.md`);
|
|
37954
|
+
}
|
|
37835
37955
|
function pipelinePrompt(context, stage, agent, promptPath, artifactPath) {
|
|
37836
37956
|
return [
|
|
37837
37957
|
`# Pipeline Agent: ${stage}`,
|
|
@@ -37889,6 +38009,7 @@ function schemaHint(stage) {
|
|
|
37889
38009
|
// src/lib/agent-orchestrator/review-orchestrator.ts
|
|
37890
38010
|
async function runReviewerPhase(input) {
|
|
37891
38011
|
const journal = input.journal ?? new NoopAgentLifecycleJournal();
|
|
38012
|
+
const emit = input.emit ?? (() => void 0);
|
|
37892
38013
|
const controller = new AbortController();
|
|
37893
38014
|
const reviewerRequests = input.resolvedTeam.reviewers.map((reviewer) => {
|
|
37894
38015
|
if (!reviewer.model.trim()) {
|
|
@@ -37907,6 +38028,18 @@ async function runReviewerPhase(input) {
|
|
|
37907
38028
|
};
|
|
37908
38029
|
});
|
|
37909
38030
|
const requests = reviewerRequests.map((item) => item.request);
|
|
38031
|
+
const running = new Set(requests.map((request) => request.id));
|
|
38032
|
+
const completed = /* @__PURE__ */ new Set();
|
|
38033
|
+
const progressTimer = setInterval(() => {
|
|
38034
|
+
emit({
|
|
38035
|
+
event: "reviews:progress",
|
|
38036
|
+
phase: "reviews",
|
|
38037
|
+
completed: completed.size,
|
|
38038
|
+
total: requests.length,
|
|
38039
|
+
running: [...running].sort()
|
|
38040
|
+
});
|
|
38041
|
+
}, 3e4);
|
|
38042
|
+
progressTimer.unref?.();
|
|
37910
38043
|
const promises = reviewerRequests.map(async ({ reviewer, request }) => {
|
|
37911
38044
|
const agentSessionId = await journal.startInstance({
|
|
37912
38045
|
workflowId: input.context.sessionId,
|
|
@@ -37917,7 +38050,28 @@ async function runReviewerPhase(input) {
|
|
|
37917
38050
|
phase: "reviews"
|
|
37918
38051
|
});
|
|
37919
38052
|
await journal.beat(agentSessionId);
|
|
38053
|
+
const startLogPaths = await writeProcessStartLog(input.context.roundDir, request);
|
|
38054
|
+
emit({
|
|
38055
|
+
event: "agent:start",
|
|
38056
|
+
phase: "reviews",
|
|
38057
|
+
id: request.id,
|
|
38058
|
+
model: request.model,
|
|
38059
|
+
prompt_path: request.promptPath,
|
|
38060
|
+
meta_log: startLogPaths.meta
|
|
38061
|
+
});
|
|
37920
38062
|
const result = await input.runner.run(request, controller.signal);
|
|
38063
|
+
const logPaths = await writeProcessLogs(input.context.roundDir, request, result);
|
|
38064
|
+
running.delete(request.id);
|
|
38065
|
+
completed.add(request.id);
|
|
38066
|
+
emit({
|
|
38067
|
+
event: "agent:complete",
|
|
38068
|
+
phase: "reviews",
|
|
38069
|
+
id: request.id,
|
|
38070
|
+
exit_code: result.exitCode,
|
|
38071
|
+
stdout_log: logPaths.stdout,
|
|
38072
|
+
stderr_log: logPaths.stderr,
|
|
38073
|
+
meta_log: logPaths.meta
|
|
38074
|
+
});
|
|
37921
38075
|
const vendorId = extractVendorSessionId(result.ndjsonEvents);
|
|
37922
38076
|
if (vendorId) await journal.bindVendorId(agentSessionId, vendorId);
|
|
37923
38077
|
await journal.endInstance(agentSessionId, {
|
|
@@ -37927,15 +38081,19 @@ async function runReviewerPhase(input) {
|
|
|
37927
38081
|
return result;
|
|
37928
38082
|
});
|
|
37929
38083
|
const results = [];
|
|
37930
|
-
|
|
37931
|
-
|
|
37932
|
-
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
|
|
37937
|
-
|
|
37938
|
-
|
|
38084
|
+
try {
|
|
38085
|
+
await Promise.all(
|
|
38086
|
+
promises.map(async (promise) => {
|
|
38087
|
+
const result = await promise;
|
|
38088
|
+
results.push(result);
|
|
38089
|
+
if (result.exitCode !== 0) {
|
|
38090
|
+
controller.abort();
|
|
38091
|
+
}
|
|
38092
|
+
})
|
|
38093
|
+
);
|
|
38094
|
+
} finally {
|
|
38095
|
+
clearInterval(progressTimer);
|
|
38096
|
+
}
|
|
37939
38097
|
const failed = results.find((result) => result.exitCode !== 0);
|
|
37940
38098
|
if (failed) {
|
|
37941
38099
|
const failedRequest = requests.find((request) => request.id === failed.id);
|
|
@@ -37988,7 +38146,7 @@ async function runReviewerPhase(input) {
|
|
|
37988
38146
|
for (const result of sortResults(results, requests)) {
|
|
37989
38147
|
const reviewer = byId.get(result.id);
|
|
37990
38148
|
if (!reviewer) continue;
|
|
37991
|
-
await writeRoundArtifact(input.context.roundDir, reviewer.reviewPath, result
|
|
38149
|
+
await writeRoundArtifact(input.context.roundDir, reviewer.reviewPath, processOutput(result));
|
|
37992
38150
|
}
|
|
37993
38151
|
return { ok: true, results: sortResults(results, requests) };
|
|
37994
38152
|
}
|
|
@@ -38002,7 +38160,8 @@ async function runOpenCodeProcessAgentReview(input) {
|
|
|
38002
38160
|
sessionId: input.sessionId,
|
|
38003
38161
|
sessionDir: input.sessionDir,
|
|
38004
38162
|
round: input.round,
|
|
38005
|
-
requirements: input.requirements
|
|
38163
|
+
requirements: input.requirements,
|
|
38164
|
+
cwd: input.cwd
|
|
38006
38165
|
});
|
|
38007
38166
|
const snapshot = await writePromptSnapshots({
|
|
38008
38167
|
context,
|
|
@@ -38021,7 +38180,8 @@ async function runOpenCodeProcessAgentReview(input) {
|
|
|
38021
38180
|
resolvedTeam: snapshot.resolvedTeam,
|
|
38022
38181
|
cwd: input.cwd,
|
|
38023
38182
|
runner,
|
|
38024
|
-
journal
|
|
38183
|
+
journal,
|
|
38184
|
+
emit
|
|
38025
38185
|
});
|
|
38026
38186
|
if (!reviewerResult.ok) {
|
|
38027
38187
|
emit({
|
|
@@ -38042,7 +38202,8 @@ async function runOpenCodeProcessAgentReview(input) {
|
|
|
38042
38202
|
resolvedTeam: snapshot.resolvedTeam,
|
|
38043
38203
|
cwd: input.cwd,
|
|
38044
38204
|
runner,
|
|
38045
|
-
journal
|
|
38205
|
+
journal,
|
|
38206
|
+
emit
|
|
38046
38207
|
});
|
|
38047
38208
|
if (!pipelineResult.ok) {
|
|
38048
38209
|
emit({
|
|
@@ -38069,6 +38230,7 @@ async function runOpenCodeProcessAgentReview(input) {
|
|
|
38069
38230
|
}
|
|
38070
38231
|
async function runPipelineStages(input) {
|
|
38071
38232
|
const journal = input.journal ?? new NoopAgentLifecycleJournal();
|
|
38233
|
+
const emit = input.emit ?? (() => void 0);
|
|
38072
38234
|
const missingReviews = input.resolvedTeam.reviewers.map((reviewer) => reviewer.reviewPath).filter((reviewPath) => !isNonEmptyFile(join27(input.context.roundDir, reviewPath)));
|
|
38073
38235
|
if (missingReviews.length > 0) {
|
|
38074
38236
|
return {
|
|
@@ -38097,7 +38259,26 @@ async function runPipelineStages(input) {
|
|
|
38097
38259
|
cwd: input.cwd,
|
|
38098
38260
|
phase: stage
|
|
38099
38261
|
};
|
|
38262
|
+
const startLogPaths = await writeProcessStartLog(input.context.roundDir, request);
|
|
38263
|
+
emit({
|
|
38264
|
+
event: "agent:start",
|
|
38265
|
+
phase: stage,
|
|
38266
|
+
id: request.id,
|
|
38267
|
+
model: request.model,
|
|
38268
|
+
prompt_path: request.promptPath,
|
|
38269
|
+
meta_log: startLogPaths.meta
|
|
38270
|
+
});
|
|
38100
38271
|
const result = await input.runner.run(request, new AbortController().signal);
|
|
38272
|
+
const logPaths = await writeProcessLogs(input.context.roundDir, request, result);
|
|
38273
|
+
emit({
|
|
38274
|
+
event: "agent:complete",
|
|
38275
|
+
phase: stage,
|
|
38276
|
+
id: request.id,
|
|
38277
|
+
exit_code: result.exitCode,
|
|
38278
|
+
stdout_log: logPaths.stdout,
|
|
38279
|
+
stderr_log: logPaths.stderr,
|
|
38280
|
+
meta_log: logPaths.meta
|
|
38281
|
+
});
|
|
38101
38282
|
results.push(result);
|
|
38102
38283
|
const vendorId = extractVendorSessionId(result.ndjsonEvents);
|
|
38103
38284
|
if (vendorId) await journal.bindVendorId(agentSessionId, vendorId);
|
|
@@ -38123,7 +38304,7 @@ async function runPipelineStages(input) {
|
|
|
38123
38304
|
results
|
|
38124
38305
|
};
|
|
38125
38306
|
}
|
|
38126
|
-
await writeRoundArtifact(input.context.roundDir, ref.artifactPath, result
|
|
38307
|
+
await writeRoundArtifact(input.context.roundDir, ref.artifactPath, processOutput(result));
|
|
38127
38308
|
const validationErrors = validateStageArtifact(input.context.roundDir, stage, ref.artifactPath);
|
|
38128
38309
|
if (validationErrors.length > 0) {
|
|
38129
38310
|
await journal.endInstance(agentSessionId, {
|
|
@@ -38156,6 +38337,85 @@ function validateStageArtifact(roundDir, stage, artifactPath) {
|
|
|
38156
38337
|
function isNonEmptyFile(path2) {
|
|
38157
38338
|
return existsSync22(path2) && statSync5(path2).isFile() && statSync5(path2).size > 0;
|
|
38158
38339
|
}
|
|
38340
|
+
async function writeProcessStartLog(roundDir, request) {
|
|
38341
|
+
const metaPath = `process-logs/${request.id}.meta.json`;
|
|
38342
|
+
await writeRoundArtifact(
|
|
38343
|
+
roundDir,
|
|
38344
|
+
metaPath,
|
|
38345
|
+
JSON.stringify(
|
|
38346
|
+
{
|
|
38347
|
+
schema_version: 1,
|
|
38348
|
+
id: request.id,
|
|
38349
|
+
phase: request.phase,
|
|
38350
|
+
model: request.model,
|
|
38351
|
+
prompt_path: request.promptPath,
|
|
38352
|
+
status: "running",
|
|
38353
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
38354
|
+
},
|
|
38355
|
+
null,
|
|
38356
|
+
2
|
|
38357
|
+
)
|
|
38358
|
+
);
|
|
38359
|
+
return { meta: metaPath };
|
|
38360
|
+
}
|
|
38361
|
+
async function writeProcessLogs(roundDir, request, result) {
|
|
38362
|
+
const prefix = `process-logs/${request.id}`;
|
|
38363
|
+
const stdoutPath = `${prefix}.stdout.ndjson`;
|
|
38364
|
+
const stderrPath = `${prefix}.stderr.log`;
|
|
38365
|
+
const metaPath = `${prefix}.meta.json`;
|
|
38366
|
+
const absoluteMetaPath = join27(roundDir, metaPath);
|
|
38367
|
+
const startedAt = readStartedAt(absoluteMetaPath) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
38368
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
38369
|
+
await writeRoundArtifact(roundDir, stdoutPath, result.stdout);
|
|
38370
|
+
await writeRoundArtifact(roundDir, stderrPath, result.stderr);
|
|
38371
|
+
await writeRoundArtifact(
|
|
38372
|
+
roundDir,
|
|
38373
|
+
metaPath,
|
|
38374
|
+
JSON.stringify(
|
|
38375
|
+
{
|
|
38376
|
+
schema_version: 1,
|
|
38377
|
+
id: request.id,
|
|
38378
|
+
phase: request.phase,
|
|
38379
|
+
model: request.model,
|
|
38380
|
+
prompt_path: request.promptPath,
|
|
38381
|
+
status: "completed",
|
|
38382
|
+
started_at: startedAt,
|
|
38383
|
+
completed_at: completedAt,
|
|
38384
|
+
duration_ms: Math.max(0, Date.parse(completedAt) - Date.parse(startedAt)),
|
|
38385
|
+
exit_code: result.exitCode,
|
|
38386
|
+
output_text_bytes: Buffer.byteLength(processOutput(result), "utf-8"),
|
|
38387
|
+
stdout_bytes: Buffer.byteLength(result.stdout, "utf-8"),
|
|
38388
|
+
stderr_bytes: Buffer.byteLength(result.stderr, "utf-8"),
|
|
38389
|
+
opencode_session_ids: extractUniqueSessionIds(result.ndjsonEvents)
|
|
38390
|
+
},
|
|
38391
|
+
null,
|
|
38392
|
+
2
|
|
38393
|
+
)
|
|
38394
|
+
);
|
|
38395
|
+
return { stdout: stdoutPath, stderr: stderrPath, meta: metaPath };
|
|
38396
|
+
}
|
|
38397
|
+
function readStartedAt(path2) {
|
|
38398
|
+
if (!existsSync22(path2)) return void 0;
|
|
38399
|
+
try {
|
|
38400
|
+
const parsed = JSON.parse(readFileSync16(path2, "utf-8"));
|
|
38401
|
+
if (!isRecord2(parsed)) return void 0;
|
|
38402
|
+
return typeof parsed.started_at === "string" ? parsed.started_at : void 0;
|
|
38403
|
+
} catch {
|
|
38404
|
+
return void 0;
|
|
38405
|
+
}
|
|
38406
|
+
}
|
|
38407
|
+
function processOutput(result) {
|
|
38408
|
+
return result.outputText ?? result.stdout;
|
|
38409
|
+
}
|
|
38410
|
+
function extractUniqueSessionIds(events) {
|
|
38411
|
+
const ids = /* @__PURE__ */ new Set();
|
|
38412
|
+
for (const event of events) {
|
|
38413
|
+
if (!isRecord2(event)) continue;
|
|
38414
|
+
const sessionID = event.sessionID;
|
|
38415
|
+
if (typeof sessionID === "string") ids.add(sessionID);
|
|
38416
|
+
}
|
|
38417
|
+
return [...ids];
|
|
38418
|
+
}
|
|
38159
38419
|
async function writePipelineFailure(roundDir, stage, result, results, validationErrors = [], diagnostic = buildFailureDiagnostic(result)) {
|
|
38160
38420
|
await writeRoundArtifact(
|
|
38161
38421
|
roundDir,
|
|
@@ -38217,11 +38477,11 @@ function buildFailureDiagnostic(result, request) {
|
|
|
38217
38477
|
}
|
|
38218
38478
|
function extractOpenCodeErrorMessage(events) {
|
|
38219
38479
|
for (const event of events) {
|
|
38220
|
-
if (!
|
|
38480
|
+
if (!isRecord2(event) || event.type !== "error") continue;
|
|
38221
38481
|
const error = event.error;
|
|
38222
|
-
if (!
|
|
38482
|
+
if (!isRecord2(error)) continue;
|
|
38223
38483
|
const data = error.data;
|
|
38224
|
-
if (
|
|
38484
|
+
if (isRecord2(data) && typeof data.message === "string") return data.message;
|
|
38225
38485
|
if (typeof error.message === "string") return error.message;
|
|
38226
38486
|
}
|
|
38227
38487
|
return void 0;
|
|
@@ -38234,7 +38494,7 @@ function excerpt(value, limit = 1200) {
|
|
|
38234
38494
|
function stripAnsi2(value) {
|
|
38235
38495
|
return value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
38236
38496
|
}
|
|
38237
|
-
function
|
|
38497
|
+
function isRecord2(value) {
|
|
38238
38498
|
return typeof value === "object" && value !== null;
|
|
38239
38499
|
}
|
|
38240
38500
|
|
|
@@ -38624,12 +38884,12 @@ var updateCommand = new Command("update").description("Update OCR assets after p
|
|
|
38624
38884
|
|
|
38625
38885
|
// src/commands/dashboard.ts
|
|
38626
38886
|
import { existsSync as existsSync25 } from "node:fs";
|
|
38627
|
-
import { join as join30, dirname as
|
|
38887
|
+
import { join as join30, dirname as dirname12 } from "node:path";
|
|
38628
38888
|
import { fileURLToPath } from "node:url";
|
|
38629
38889
|
init_src();
|
|
38630
38890
|
init_db();
|
|
38631
38891
|
var __filename = fileURLToPath(import.meta.url);
|
|
38632
|
-
var __dirname =
|
|
38892
|
+
var __dirname = dirname12(__filename);
|
|
38633
38893
|
function resolveServerPath() {
|
|
38634
38894
|
return join30(__dirname, "dashboard", "server.js");
|
|
38635
38895
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nk070281sjv/cli",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.7",
|
|
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.
|
|
40
|
+
"@nk070281sjv/agents": "2.3.7",
|
|
41
41
|
"chalk": "^5.4.1",
|
|
42
42
|
"chokidar": "^4.0.3",
|
|
43
43
|
"commander": "^13.0.0",
|