Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions packages/gittensory-miner/lib/attempt-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// query (attempt-log.js's schema has no repo+issue index, and reenqueue counts aren't tracked anywhere yet).

import { resolveCodingAgentModeFromConfig } from "@jsonbored/gittensory-engine";
import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js";
import { constructProductionCodingAgentDriver } from "./coding-agent-construction.js";
import { runSlopAssessment } from "./slop-assessment.js";
import { fetchLiveIssueSnapshot } from "./live-issue-snapshot.js";
Expand Down Expand Up @@ -139,8 +140,7 @@ export function buildAttemptDeps(env, ledgers) {
export async function runAttempt(args, options = {}) {
const parsed = parseAttemptArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

const env = options.env ?? process.env;
Expand All @@ -149,10 +149,11 @@ export async function runAttempt(args, options = {}) {
const mode = resolveMode({ env, agentDryRun: !parsed.live });

if (mode === "paused") {
console.error(
return reportCliFailure(
parsed.json,
`Coding-agent execution is globally paused (MINER_CODING_AGENT_PAUSED). Not running attempt for ${parsed.repoFullName}#${parsed.issueNumber}.`,
3,
);
return 3;
}

const attemptId = options.attemptId ?? `${parsed.repoFullName.replace("/", "_")}-${parsed.issueNumber}-${nowMs}`;
Expand Down Expand Up @@ -222,9 +223,12 @@ export async function runAttempt(args, options = {}) {
const buildDeps = options.buildAttemptDeps ?? buildAttemptDeps;
deps = buildDeps(env, { claimLedger, eventLedger, attemptLog, governorLedger, nowMs });
} catch (error) {
const reason = error instanceof Error ? error.message : String(error);
console.error(`Attempt for ${parsed.repoFullName}#${parsed.issueNumber} is blocked: ${reason}`);
return 3;
const reason = describeCliError(error);
return reportCliFailure(
parsed.json,
`Attempt for ${parsed.repoFullName}#${parsed.issueNumber} is blocked: ${reason}`,
3,
);
}

// Real worktree preparation (repo-clone.js + attempt-worktree.js, #5237): the allocator above only
Expand Down Expand Up @@ -461,8 +465,7 @@ export async function runAttempt(args, options = {}) {
return 2;
}
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
} finally {
// worktreeResult.attemptOk is set to the REAL runMinerAttempt outcome (submitted = true) once that call
// happens; every earlier blocked path (rejection/worktree-prep-failure/infeasible) never sets it, since
Expand Down
16 changes: 10 additions & 6 deletions packages/gittensory-miner/lib/calibration-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { buildCalibrationReport } from "./calibration.js";
import { initEventLedger, resolveEventLedgerDbPath } from "./event-ledger.js";
import { MINER_PR_OUTCOME_EVENT } from "./pr-outcome.js";
import { initPredictionLedger, resolvePredictionLedgerDbPath } from "./prediction-ledger.js";
import { reportCliFailure, describeCliError } from "./cli-error.js";

const CALIBRATION_USAGE = "Usage: gittensory-miner calibration [--json]";

Expand Down Expand Up @@ -67,22 +68,25 @@ export function runCalibrationCli(args = [], env = process.env) {
const json = args.includes("--json");
const unknown = args.find((token) => token.startsWith("-") && token !== "--json");
if (unknown) {
console.error(`Unknown option: ${unknown}. ${CALIBRATION_USAGE}`);
return 1;
return reportCliFailure(json, `Unknown option: ${unknown}. ${CALIBRATION_USAGE}`, 1);
}

const predictionStore = initPredictionLedger(resolvePredictionLedgerDbPath(env));
const eventLedger = initEventLedger(resolveEventLedgerDbPath(env));
let predictionStore;
let eventLedger;
try {
predictionStore = initPredictionLedger(resolvePredictionLedgerDbPath(env));
eventLedger = initEventLedger(resolveEventLedgerDbPath(env));
const report = buildCalibrationReport(
toPredictionRecords(predictionStore.readPredictions()),
toOutcomeRecords(eventLedger.readEvents()),
);
if (json) console.log(JSON.stringify(report, null, 2));
else renderReportText(report);
return 0;
} catch (error) {
return reportCliFailure(json, describeCliError(error));
} finally {
predictionStore.close();
eventLedger.close();
predictionStore?.close();
eventLedger?.close();
}
}
25 changes: 9 additions & 16 deletions packages/gittensory-miner/lib/claim-ledger-cli.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CLAIM_STATUSES, openClaimLedger } from "./claim-ledger.js";
import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js";

const CLAIM_CLAIM_USAGE =
"Usage: gittensory-miner claim claim <owner/repo> <issue#> [--note <text>] [--json]";
Expand Down Expand Up @@ -183,8 +184,7 @@ function withClaimLedger(options, run) {
export function runClaimClaim(args, options = {}) {
const parsed = parseClaimClaimArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

try {
Expand All @@ -202,24 +202,21 @@ export function runClaimClaim(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
}
}

export function runClaimRelease(args, options = {}) {
const parsed = parseClaimReleaseArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

try {
return withClaimLedger(options, (claimLedger) => {
const claim = claimLedger.releaseClaim(parsed.repoFullName, parsed.issueNumber);
if (!claim) {
console.error("claim_not_found");
return 2;
return reportCliFailure(parsed.json, "claim_not_found");
}
if (parsed.json) {
console.log(JSON.stringify({ claim }, null, 2));
Expand All @@ -229,16 +226,14 @@ export function runClaimRelease(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
}
}

export function runClaimList(args, options = {}) {
const parsed = parseClaimListArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

try {
Expand All @@ -255,15 +250,13 @@ export function runClaimList(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
}
}

export function runClaimCli(subcommand, args, options = {}) {
if (subcommand === "claim") return runClaimClaim(args, options);
if (subcommand === "release") return runClaimRelease(args, options);
if (subcommand === "list") return runClaimList(args, options);
console.error(`Unknown claim subcommand: ${subcommand ?? ""}. ${CLAIM_LIST_USAGE}`);
return 2;
return reportCliFailure(argsWantJson(args), `Unknown claim subcommand: ${subcommand ?? ""}. ${CLAIM_LIST_USAGE}`);
}
3 changes: 3 additions & 0 deletions packages/gittensory-miner/lib/cli-error.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function reportCliFailure(wantsJson: boolean, message: string, exitCode?: number): number;
export function argsWantJson(args: readonly string[]): boolean;
export function describeCliError(error: unknown): string;
27 changes: 27 additions & 0 deletions packages/gittensory-miner/lib/cli-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/** Shared CLI failure output (#4836): when `--json` is set, emit a parseable `{ ok: false, error }` object on
* stdout (matching each command's success-path JSON stream); otherwise log plain text to stderr. */

/**
* @param {boolean} wantsJson
* @param {string} message
* @param {number} [exitCode]
* @returns {number}
*/
export function reportCliFailure(wantsJson, message, exitCode = 2) {
if (wantsJson) {
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
} else {
console.error(message);
}
return exitCode;
}

/** True when argv includes `--json` (used on parse-error paths before a full parse result exists). */
export function argsWantJson(args) {
return args.includes("--json");
}

/** Normalize a thrown value to a safe error string for CLI output. */
export function describeCliError(error) {
return error instanceof Error ? error.message : String(error);
}
6 changes: 4 additions & 2 deletions packages/gittensory-miner/lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { argsWantJson, reportCliFailure } from "./cli-error.js";

export function printVersion(input) {
console.log(`${input.packageName}/${input.packageVersion} (node ${process.version})`);
}
Expand Down Expand Up @@ -56,6 +58,6 @@ export function printHelp(input) {

export function runCli(cliArgs, input) {
const command = cliArgs[0] ?? "";
console.error(`Unknown command: ${command}. Run ${input.packageName} --help.`);
return 1;
const message = `Unknown command: ${command}. Run ${input.packageName} --help.`;
return reportCliFailure(argsWantJson(cliArgs), message, 1);
}
4 changes: 2 additions & 2 deletions packages/gittensory-miner/lib/deny-check.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { evaluateDenyHooks } from "./deny-hooks.js";
import { argsWantJson, reportCliFailure } from "./cli-error.js";

const DENY_CHECK_USAGE =
"Usage: gittensory-miner hooks check --tool <name> --input <json> [--json]";
Expand Down Expand Up @@ -59,8 +60,7 @@ export function parseDenyCheckArgs(args) {
export function runDenyCheck(args) {
const parsed = parseDenyCheckArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

const verdict = evaluateDenyHooks({ name: parsed.tool, input: parsed.input });
Expand Down
7 changes: 3 additions & 4 deletions packages/gittensory-miner/lib/discover-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { initPolicyDocCacheStore } from "./policy-doc-cache.js";
import { initPolicyVerdictCacheStore } from "./policy-verdict-cache.js";
import { enqueueRankedDiscovery } from "./portfolio-discovery.js";
import { initPortfolioQueueStore } from "./portfolio-queue.js";
import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js";

const DISCOVER_USAGE =
"Usage: gittensory-miner discover <owner/repo> [<owner/repo>...] | --search <query> [--json] [--api-base-url <url>] [--token-env <VAR>]";
Expand Down Expand Up @@ -138,8 +139,7 @@ export function renderDiscoverSummary(result) {
export async function runDiscover(args, options = {}) {
const parsed = parseDiscoverArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

// Credential env var is per-tenant (#4784): a `--token-env FORGE_PAT` flag (or `options.tokenEnv`) reads a
Expand Down Expand Up @@ -222,8 +222,7 @@ export async function runDiscover(args, options = {}) {
}
return 0;
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
} finally {
if (ownsPortfolioQueue) portfolioQueue.close();
if (ownsPolicyDocCache && policyDocCache) policyDocCache.close();
Expand Down
16 changes: 6 additions & 10 deletions packages/gittensory-miner/lib/event-ledger-cli.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { initEventLedger } from "./event-ledger.js";
import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js";

const LEDGER_LIST_USAGE =
"Usage: gittensory-miner ledger list [--repo <owner/repo>] [--since <seq>] [--type <eventType>] [--json]";
Expand Down Expand Up @@ -217,8 +218,7 @@ function withEventLedger(options, run) {
export function runLedgerList(args, options = {}) {
const parsed = parseLedgerListArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

try {
Expand All @@ -238,15 +238,13 @@ export function runLedgerList(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
}
}

export function runLedgerMetrics(args, options = {}) {
if (args.length > 0) {
console.error(EVENT_LEDGER_METRICS_USAGE);
return 2;
return reportCliFailure(argsWantJson(args), EVENT_LEDGER_METRICS_USAGE);
}

try {
Expand All @@ -257,14 +255,12 @@ export function runLedgerMetrics(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(argsWantJson(args), describeCliError(error));
}
}

export function runLedgerCli(subcommand, args, options = {}) {
if (subcommand === "list") return runLedgerList(args, options);
if (subcommand === "metrics") return runLedgerMetrics(args, options);
console.error(`Unknown ledger subcommand: ${subcommand ?? ""}. ${LEDGER_LIST_USAGE}`);
return 2;
return reportCliFailure(argsWantJson(args), `Unknown ledger subcommand: ${subcommand ?? ""}. ${LEDGER_LIST_USAGE}`);
}
4 changes: 2 additions & 2 deletions packages/gittensory-miner/lib/feasibility-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* `buildFeasibilityVerdict` composer. Purely local — no network, no filesystem — so it never needs the
* npm-registry update check other subcommands opt into. */
import { buildFeasibilityVerdict } from "@jsonbored/gittensory-engine";
import { argsWantJson, reportCliFailure } from "./cli-error.js";

const CLAIM_STATUSES = ["unclaimed", "claimed", "solved", "unknown"];
const DUPLICATE_CLUSTER_RISKS = ["none", "low", "medium", "high"];
Expand Down Expand Up @@ -59,8 +60,7 @@ export function parseFeasibilityArgs(args) {
export function runFeasibilityCli(args, options = {}) {
const parsed = parseFeasibilityArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

const buildVerdict = options.buildFeasibilityVerdict ?? buildFeasibilityVerdict;
Expand Down
11 changes: 5 additions & 6 deletions packages/gittensory-miner/lib/governor-ledger-cli.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** Must match `GOVERNOR_LEDGER_EVENT_TYPES` in `@jsonbored/gittensory-engine`. */
import { argsWantJson, describeCliError, reportCliFailure } from "./cli-error.js";

const GOVERNOR_LEDGER_EVENT_TYPES = Object.freeze([
"allowed",
"denied",
Expand Down Expand Up @@ -109,8 +111,7 @@ async function withGovernorLedger(options, run) {
export async function runGovernorList(args, options = {}) {
const parsed = parseGovernorListArgs(args);
if ("error" in parsed) {
console.error(parsed.error);
return 2;
return reportCliFailure(argsWantJson(args), parsed.error);
}

try {
Expand All @@ -129,13 +130,11 @@ export async function runGovernorList(args, options = {}) {
return 0;
});
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
return 2;
return reportCliFailure(parsed.json, describeCliError(error));
}
}

export async function runGovernorCli(subcommand, args, options = {}) {
if (subcommand === "list") return runGovernorList(args, options);
console.error(`Unknown governor subcommand: ${subcommand ?? ""}. ${GOVERNOR_LIST_USAGE}`);
return 2;
return reportCliFailure(argsWantJson(args), `Unknown governor subcommand: ${subcommand ?? ""}. ${GOVERNOR_LIST_USAGE}`);
}
4 changes: 2 additions & 2 deletions packages/gittensory-miner/lib/laptop-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { homedir } from "node:os";
import { delimiter, join } from "node:path";
import { DatabaseSync } from "node:sqlite";
import { applySchemaMigrations } from "./schema-version.js";
import { reportCliFailure } from "./cli-error.js";

const githubApiBaseUrl = "https://github.com/ghapi";
const githubApiVersion = "2022-11-28";
Expand Down Expand Up @@ -307,8 +308,7 @@ export async function runInit(args = [], env = process.env) {
if (verifyToken) {
verification = await verifyGithubToken({ githubToken: env.GITHUB_TOKEN ?? "" });
if (!verification.ok) {
console.error(verification.detail);
return 1;
return reportCliFailure(jsonOutput, verification.detail, 1);
}
}

Expand Down
Loading