@gluecharm-lab/easyspecs-cli 0.0.16 → 0.0.17
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/README.md +1 -1
- package/commands.md +19 -1
- package/dist/main.cjs +448 -13
- package/dist/main.cjs.map +4 -4
- package/error-code.md +113 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -219,7 +219,7 @@ These appear **before** the subcommand (everything after the first non-flag toke
|
|
|
219
219
|
| ---- | ------ |
|
|
220
220
|
| `--cwd <dir>` | Repository root for git resolution and paths (default: current working directory). |
|
|
221
221
|
| `--ci` | Non-interactive mode; affects merged settings (e.g. Factory outer-iteration default). **`EASYSPECS_CI` is not read** — use this flag. |
|
|
222
|
-
| `--json` | On supported exits, one JSON summary line on stdout. |
|
|
222
|
+
| `--json` | On supported exits, one JSON summary line on stdout. On **non-zero** exits, the line includes **`exitCode`** (number) and **`exitMeaning`** (one-line classification) alongside any command-specific fields. **`analysis`** validation failures also include **`factoryFailures`** (per failed factory phase), **`failurePhase`**, and **`validationExitId`**. |
|
|
223
223
|
| `--verbose` | Extra stderr logging where implemented. |
|
|
224
224
|
| `--api-base-url <url>` | System Manager API origin for this process (overrides `easyspecs.apiBaseUrl`). |
|
|
225
225
|
| `--session-path <file>` | Session JSON path for **this process only** (overrides **`easyspecs.cliSessionPath`**); does not rewrite **`config.json`** unless you use **`auth login`** tail **`--session-path`** as documented under **Auth**. |
|
package/commands.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# EasySpecs CLI — commands, flags, and configuration
|
|
2
2
|
|
|
3
|
+
**Error codes:** OS exit codes, structured **`failureExitId`** values, and JSON field semantics are catalogued in **[`error-code.md`](./error-code.md)** (maintained with CLI changes).
|
|
4
|
+
|
|
3
5
|
Published package: **`@gluecharm-lab/easyspecs-cli`** (`easyspecs-cli`). Source of truth for routing and option registration: [`src/cli/cliProgram.ts`](../../src/cli/cliProgram.ts), command dispatch and business flow wiring: [`src/cli/main.ts`](../../src/cli/main.ts), merged settings: [`mergeEasyspecsCliSettings`](../../src/cli/cliSettings.ts), per-repo config: [`src/config/easyspecsConfigFile.ts`](../../src/config/easyspecsConfigFile.ts), CLI API URL resolution: [`src/easyspecsApiBaseUrlCli.ts`](../../src/easyspecsApiBaseUrlCli.ts). The VS Code extension resolves API URLs via [`src/apiBaseUrlResolve.ts`](../../src/apiBaseUrlResolve.ts); **`easyspecs-cli` does not use that chain** for System Manager origin (**SRS-43 R30 / R34**).
|
|
4
6
|
|
|
5
7
|
### Vocabulary (SRS-53)
|
|
@@ -20,6 +22,22 @@ Quick usage:
|
|
|
20
22
|
easyspecs-cli help
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
### Factory validation reporting (**SRS-57**)
|
|
26
|
+
|
|
27
|
+
When **`easyspecs-cli analysis`** exits with **validation** (**`5`**), human stderr prints a short banner and **one bullet per failed factory phase** (readable label, **`validationExitId`** **`5.1`–`5.9`**, normative **`title`**, optional **`detail`**). **`--json`** adds:
|
|
28
|
+
|
|
29
|
+
| JSON key | Meaning |
|
|
30
|
+
| -------- | ------- |
|
|
31
|
+
| **`factoryFailures`** | Array of **`{ factory, phase, exitCode, failureExitId, title, validationExitId? (when exit 5), detail?, validationSubcode? }`** — **`failureExitId`** is the canonical **`major.minor`** id (SRS-58); see [`error-code.md`](./error-code.md). |
|
|
32
|
+
| **`failurePhase`** | Last failed phase in macro order (alias for scripts). |
|
|
33
|
+
| **`validationExitId`** | Matching **`5.x`** for **`failurePhase`**. |
|
|
34
|
+
| **`exitMeaning`** | First failure **`title`**, or that title plus “and *N* other pipeline phases failed”. |
|
|
35
|
+
| **`error`** | Orchestrator message; if several phases failed, a short appendix lists extra **`validationExitId`**s (see [`factoryValidationFailures.ts`](../../src/factory/factoryValidationFailures.ts)). |
|
|
36
|
+
|
|
37
|
+
**`validationExitId` → `phase` map:** **`5.1`** `create_analysis_worktree`, **`5.2`** `materialize_opencode_agents`, **`5.3`** `synthesis_convergence`, **`5.4`** `reference_coverage`, **`5.5`** `zero_reference_remediation_convergence`, **`5.6`** `reference_coverage_execution_report`, **`5.7`** `link_mapping_pipeline`, **`5.8`** `assemble_application_context_index`, **`5.9`** `backend_context_sync`, **`5.0`** unknown / unattributed. Normative **`title`** strings and readable labels: [`.gluecharm/docs/srs/srs-57.md`](../../.gluecharm/docs/srs/srs-57.md).
|
|
38
|
+
|
|
39
|
+
**Optional `validationSubcode` (v1):** **`R5_MACRO_PING_PONG`** when synthesis **`detail`** notes **R5** unstable convergence; **`COVERAGE_PERCENT_THRESHOLD`** when reference-coverage **`detail`** matches the strict percent gate pattern.
|
|
40
|
+
|
|
23
41
|
---
|
|
24
42
|
|
|
25
43
|
## Global options
|
|
@@ -30,7 +48,7 @@ These flags may appear **before** the subcommand. Parsing and command registrati
|
|
|
30
48
|
|------|--------|
|
|
31
49
|
| `--cwd <dir>` | Repository root for git resolution and file paths (default: current working directory). |
|
|
32
50
|
| `--ci` | Non-interactive mode; feeds into merged settings (e.g. Factory outer-iteration default when unlimited). **`EASYSPECS_CI` is not read** — use this flag in CI. |
|
|
33
|
-
| `--json` | On supported exits, prints one JSON summary line on stdout; suppresses some human-oriented stderr unless `--verbose`. |
|
|
51
|
+
| `--json` | On supported exits, prints one JSON summary line on stdout; suppresses some human-oriented stderr unless `--verbose`. On **`analysis`** validation (**exit code 5**), the JSON line includes **`factoryFailures`** (**SRS-57** — one object per **`failed`** factory phase) plus **`failurePhase`**, **`validationExitId`**, composed **`error`**, and **`exitMeaning`** tailored to the first failure. |
|
|
34
52
|
| `--verbose` | Extra stderr logging where implemented. |
|
|
35
53
|
| `--api-base-url <url>` | System Manager API origin for this process (overrides `easyspecs.apiBaseUrl` in config). |
|
|
36
54
|
| `--environment production` \| `--environment staging` | Alias: **`--env`**. Overrides `easyspecs.deploymentEnvironment` for built-in URL selection when no explicit URL is set. |
|
package/dist/main.cjs
CHANGED
|
@@ -10651,6 +10651,30 @@ var ExitCode = {
|
|
|
10651
10651
|
cancelled: 8,
|
|
10652
10652
|
internal: 99
|
|
10653
10653
|
};
|
|
10654
|
+
function describeExitCode(code) {
|
|
10655
|
+
switch (code) {
|
|
10656
|
+
case ExitCode.ok:
|
|
10657
|
+
return "Success.";
|
|
10658
|
+
case ExitCode.usage:
|
|
10659
|
+
return "Invalid arguments, unknown command, or help was not applicable \u2014 see message above or run `easyspecs-cli help`.";
|
|
10660
|
+
case ExitCode.misconfiguration:
|
|
10661
|
+
return "Configuration or repo layout problem \u2014 fix `.easyspecs/config.json`, paths, or prerequisites, then retry.";
|
|
10662
|
+
case ExitCode.opencode:
|
|
10663
|
+
return "OpenCode (agent runner) failed \u2014 check `opencode` install, credentials, and stderr from the tool run.";
|
|
10664
|
+
case ExitCode.validation:
|
|
10665
|
+
return "Validation or factory pipeline did not succeed \u2014 see `error` above and logs for the failing phase (synthesis, coverage, remediation, index, etc.).";
|
|
10666
|
+
case ExitCode.auth:
|
|
10667
|
+
return "Authentication failed or session missing \u2014 run `easyspecs-cli auth login` or fix CI credentials in config.";
|
|
10668
|
+
case ExitCode.upload:
|
|
10669
|
+
return "Upload or cloud sync failed \u2014 check network, project id, and session; see `error` above.";
|
|
10670
|
+
case ExitCode.cancelled:
|
|
10671
|
+
return "Operation was cancelled (abort/stop).";
|
|
10672
|
+
case ExitCode.internal:
|
|
10673
|
+
return "Unexpected internal CLI error \u2014 retry; if it persists, report with full stderr and `--verbose` output.";
|
|
10674
|
+
default:
|
|
10675
|
+
return `Non-zero exit (${String(code)}) \u2014 see stderr and any JSON \`error\` field for detail.`;
|
|
10676
|
+
}
|
|
10677
|
+
}
|
|
10654
10678
|
|
|
10655
10679
|
// src/cli/jsonReporter.ts
|
|
10656
10680
|
function printJsonLine(envelope) {
|
|
@@ -20779,6 +20803,180 @@ var FACTORY_PIPELINE_EXIT_CONDITIONS = {
|
|
|
20779
20803
|
backend_context_sync: "Context upload finished with no failures (quiet SRS-13 path; or cancel)."
|
|
20780
20804
|
};
|
|
20781
20805
|
|
|
20806
|
+
// src/factory/factoryValidationFailures.ts
|
|
20807
|
+
function factoryFailureDisplayId(row2) {
|
|
20808
|
+
return row2.failureExitId;
|
|
20809
|
+
}
|
|
20810
|
+
function normalizeFactoryFailureRow(r) {
|
|
20811
|
+
const failureExitId = r.failureExitId ?? r.validationExitId ?? "5.0";
|
|
20812
|
+
const exitCode = typeof r.exitCode === "number" ? r.exitCode : ExitCode.validation;
|
|
20813
|
+
const factory = r.factory ?? "generate_context";
|
|
20814
|
+
const base = {
|
|
20815
|
+
factory,
|
|
20816
|
+
phase: r.phase,
|
|
20817
|
+
exitCode,
|
|
20818
|
+
failureExitId,
|
|
20819
|
+
title: r.title,
|
|
20820
|
+
...typeof r.detail === "string" ? { detail: r.detail } : {},
|
|
20821
|
+
...typeof r.validationSubcode === "string" ? { validationSubcode: r.validationSubcode } : {}
|
|
20822
|
+
};
|
|
20823
|
+
if (exitCode === ExitCode.validation) {
|
|
20824
|
+
return { ...base, validationExitId: r.validationExitId ?? failureExitId };
|
|
20825
|
+
}
|
|
20826
|
+
return base;
|
|
20827
|
+
}
|
|
20828
|
+
var FACTORY_VALIDATION_PHASE_ORDER = [
|
|
20829
|
+
"create_analysis_worktree",
|
|
20830
|
+
"materialize_opencode_agents",
|
|
20831
|
+
"synthesis_convergence",
|
|
20832
|
+
"reference_coverage",
|
|
20833
|
+
"zero_reference_remediation_convergence",
|
|
20834
|
+
"reference_coverage_execution_report",
|
|
20835
|
+
"link_mapping_pipeline",
|
|
20836
|
+
"assemble_application_context_index",
|
|
20837
|
+
"backend_context_sync"
|
|
20838
|
+
];
|
|
20839
|
+
var DETAIL_MAX = 600;
|
|
20840
|
+
var ERROR_JOIN_MAX = 900;
|
|
20841
|
+
var ADDITIONAL_APPEND_MAX = 200;
|
|
20842
|
+
var PHASE_TO_EXIT_ID = {
|
|
20843
|
+
unknown_factory_phase: "5.0",
|
|
20844
|
+
create_analysis_worktree: "5.1",
|
|
20845
|
+
materialize_opencode_agents: "5.2",
|
|
20846
|
+
synthesis_convergence: "5.3",
|
|
20847
|
+
reference_coverage: "5.4",
|
|
20848
|
+
zero_reference_remediation_convergence: "5.5",
|
|
20849
|
+
reference_coverage_execution_report: "5.6",
|
|
20850
|
+
link_mapping_pipeline: "5.7",
|
|
20851
|
+
assemble_application_context_index: "5.8",
|
|
20852
|
+
backend_context_sync: "5.9"
|
|
20853
|
+
};
|
|
20854
|
+
var EXIT_ID_TITLES = {
|
|
20855
|
+
"5.0": "EasySpecs stopped: the failing pipeline phase could not be identified. Retry with --verbose; report this outcome if it persists.",
|
|
20856
|
+
"5.1": "Could not prepare the analysis Git worktree (temporary checkout). Check disk space, repo permissions, and that the project is a valid Git checkout.",
|
|
20857
|
+
"5.2": "Could not install OpenCode agent files into the analysis worktree. Check bundled extension assets and disk permissions under the worktree path.",
|
|
20858
|
+
"5.3": "Context synthesis did not finish: required context files are still missing or invalid after retries. Inspect OpenCode errors and workstation logs for this phase.",
|
|
20859
|
+
"5.4": "Reference coverage could not be computed or written. Check coverage-reference-validation.json prerequisites and disk/schema errors.",
|
|
20860
|
+
"5.5": "Zero-reference remediation did not complete successfully. Inspect remediation logs and zero-reference outputs for this checkout.",
|
|
20861
|
+
"5.6": "Reference coverage execution report could not be generated. Ensure coverage inputs exist from the coverage phase; see coverage-reference-validation.json.",
|
|
20862
|
+
"5.7": "Context link graph (navigation links) could not be updated. Check coordination JSON and markdown under .gluecharm/context.",
|
|
20863
|
+
"5.8": "Application context index could not be assembled or failed schema validation. Inspect index-application-context.json and preceding context files.",
|
|
20864
|
+
"5.9": "Upload / cloud sync after analysis did not succeed. Check auth, project id, network, and upload error details."
|
|
20865
|
+
};
|
|
20866
|
+
var READABLE_PHASE_LABEL = {
|
|
20867
|
+
create_analysis_worktree: "Analysis worktree",
|
|
20868
|
+
materialize_opencode_agents: "OpenCode agents",
|
|
20869
|
+
synthesis_convergence: "Synthesis",
|
|
20870
|
+
reference_coverage: "Reference coverage",
|
|
20871
|
+
zero_reference_remediation_convergence: "Zero-reference remediation",
|
|
20872
|
+
reference_coverage_execution_report: "Coverage report",
|
|
20873
|
+
link_mapping_pipeline: "Link graph",
|
|
20874
|
+
assemble_application_context_index: "Index assembly",
|
|
20875
|
+
backend_context_sync: "Cloud sync",
|
|
20876
|
+
unknown_factory_phase: "Unknown phase"
|
|
20877
|
+
};
|
|
20878
|
+
function trimDetail(s) {
|
|
20879
|
+
if (!s) {
|
|
20880
|
+
return void 0;
|
|
20881
|
+
}
|
|
20882
|
+
const t = s.trim();
|
|
20883
|
+
if (!t) {
|
|
20884
|
+
return void 0;
|
|
20885
|
+
}
|
|
20886
|
+
return t.length <= DETAIL_MAX ? t : `${t.slice(0, DETAIL_MAX)}\u2026`;
|
|
20887
|
+
}
|
|
20888
|
+
function inferValidationSubcode(phase, detail) {
|
|
20889
|
+
const d = detail ?? "";
|
|
20890
|
+
if (phase === "synthesis_convergence" && d.includes("Unstable convergence (R5)")) {
|
|
20891
|
+
return "R5_MACRO_PING_PONG";
|
|
20892
|
+
}
|
|
20893
|
+
if (phase === "reference_coverage" && /percentNonReferenced\s+\d/.test(d) && d.includes(">")) {
|
|
20894
|
+
return "COVERAGE_PERCENT_THRESHOLD";
|
|
20895
|
+
}
|
|
20896
|
+
return void 0;
|
|
20897
|
+
}
|
|
20898
|
+
function phaseToValidationExitId(phase) {
|
|
20899
|
+
return PHASE_TO_EXIT_ID[phase] ?? "5.0";
|
|
20900
|
+
}
|
|
20901
|
+
function titleForValidationExitId(validationExitId) {
|
|
20902
|
+
return EXIT_ID_TITLES[validationExitId] ?? EXIT_ID_TITLES["5.0"];
|
|
20903
|
+
}
|
|
20904
|
+
function readableLabelForFactoryPhase(phase) {
|
|
20905
|
+
return READABLE_PHASE_LABEL[phase] ?? phase;
|
|
20906
|
+
}
|
|
20907
|
+
function phaseIndex(key) {
|
|
20908
|
+
const i = FACTORY_VALIDATION_PHASE_ORDER.indexOf(key);
|
|
20909
|
+
return i >= 0 ? i : 999;
|
|
20910
|
+
}
|
|
20911
|
+
function buildFactoryFailuresFromRows(phases) {
|
|
20912
|
+
const failed = phases.filter((p) => p.status === "failed");
|
|
20913
|
+
failed.sort((a, b) => phaseIndex(a.key) - phaseIndex(b.key));
|
|
20914
|
+
return failed.map((p) => {
|
|
20915
|
+
const id = phaseToValidationExitId(p.key);
|
|
20916
|
+
const sub = inferValidationSubcode(p.key, p.detail);
|
|
20917
|
+
return {
|
|
20918
|
+
factory: "generate_context",
|
|
20919
|
+
phase: p.key,
|
|
20920
|
+
exitCode: ExitCode.validation,
|
|
20921
|
+
failureExitId: id,
|
|
20922
|
+
validationExitId: id,
|
|
20923
|
+
title: titleForValidationExitId(id),
|
|
20924
|
+
detail: trimDetail(p.detail),
|
|
20925
|
+
...sub !== void 0 ? { validationSubcode: sub } : {}
|
|
20926
|
+
};
|
|
20927
|
+
});
|
|
20928
|
+
}
|
|
20929
|
+
function syntheticUnknownFactoryFailure(message) {
|
|
20930
|
+
return [
|
|
20931
|
+
{
|
|
20932
|
+
factory: "generate_context",
|
|
20933
|
+
phase: "unknown_factory_phase",
|
|
20934
|
+
exitCode: ExitCode.validation,
|
|
20935
|
+
failureExitId: "5.0",
|
|
20936
|
+
validationExitId: "5.0",
|
|
20937
|
+
title: titleForValidationExitId("5.0"),
|
|
20938
|
+
detail: trimDetail(message)
|
|
20939
|
+
}
|
|
20940
|
+
];
|
|
20941
|
+
}
|
|
20942
|
+
function primaryFailureAliases(failures) {
|
|
20943
|
+
const last = failures[failures.length - 1];
|
|
20944
|
+
if (!last) {
|
|
20945
|
+
return { failurePhase: "unknown_factory_phase", validationExitId: "5.0" };
|
|
20946
|
+
}
|
|
20947
|
+
return {
|
|
20948
|
+
failurePhase: last.phase,
|
|
20949
|
+
validationExitId: last.validationExitId ?? factoryFailureDisplayId(last)
|
|
20950
|
+
};
|
|
20951
|
+
}
|
|
20952
|
+
function exitMeaningFromFactoryFailures(failures) {
|
|
20953
|
+
const f0 = failures[0];
|
|
20954
|
+
if (!f0) {
|
|
20955
|
+
return "";
|
|
20956
|
+
}
|
|
20957
|
+
if (failures.length === 1) {
|
|
20958
|
+
return f0.title;
|
|
20959
|
+
}
|
|
20960
|
+
return `${f0.title} (and ${String(failures.length - 1)} other pipeline phases failed)`;
|
|
20961
|
+
}
|
|
20962
|
+
function composeFactoryValidationError(orchestratorMessage, failures) {
|
|
20963
|
+
const m = orchestratorMessage?.trim() ?? "";
|
|
20964
|
+
if (failures.length <= 1) {
|
|
20965
|
+
return m.length > 0 ? m : failures[0]?.title ?? "Validation failed.";
|
|
20966
|
+
}
|
|
20967
|
+
const extraIds = failures.slice(1).map((f) => factoryFailureDisplayId(f)).join(", ");
|
|
20968
|
+
let appendix = `(+${String(failures.length - 1)} additional phase failure(s): ${extraIds})`;
|
|
20969
|
+
if (appendix.length > ADDITIONAL_APPEND_MAX) {
|
|
20970
|
+
appendix = appendix.slice(0, ADDITIONAL_APPEND_MAX).trimEnd() + "\u2026";
|
|
20971
|
+
}
|
|
20972
|
+
if (m.length > 0) {
|
|
20973
|
+
const out = `${m} | ${appendix}`;
|
|
20974
|
+
return out.length <= ERROR_JOIN_MAX ? out : `${m.slice(0, Math.max(0, ERROR_JOIN_MAX - appendix.length - 3))}\u2026 | ${appendix}`;
|
|
20975
|
+
}
|
|
20976
|
+
const joined = failures.map((f) => `[${factoryFailureDisplayId(f)}] ${f.title}`).join(" | ");
|
|
20977
|
+
return joined.length <= ERROR_JOIN_MAX ? joined : `${joined.slice(0, ERROR_JOIN_MAX - 1)}\u2026`;
|
|
20978
|
+
}
|
|
20979
|
+
|
|
20782
20980
|
// src/factory/generateContextFactory.ts
|
|
20783
20981
|
var FACTORY_PIPELINE_KEYS = [
|
|
20784
20982
|
"create_analysis_worktree",
|
|
@@ -20886,7 +21084,11 @@ async function runGenerateContextFactory(deps) {
|
|
|
20886
21084
|
};
|
|
20887
21085
|
const fail = async (message) => {
|
|
20888
21086
|
await post();
|
|
20889
|
-
|
|
21087
|
+
let ff = buildFactoryFailuresFromRows(phases);
|
|
21088
|
+
if (ff.length === 0) {
|
|
21089
|
+
ff = syntheticUnknownFactoryFailure(message);
|
|
21090
|
+
}
|
|
21091
|
+
return { ok: false, message, totalElapsedMs: macroEnd(), factoryFailures: ff };
|
|
20890
21092
|
};
|
|
20891
21093
|
const pipelineCtx = {
|
|
20892
21094
|
signal: deps.signal,
|
|
@@ -24348,6 +24550,155 @@ function buildFactoryDepsHeadless(input) {
|
|
|
24348
24550
|
};
|
|
24349
24551
|
}
|
|
24350
24552
|
|
|
24553
|
+
// src/cli/failureExitRegistry.ts
|
|
24554
|
+
var DRIFT_FACTORY = "context_drift";
|
|
24555
|
+
function failureExitIdFromParts(exitCode, minor) {
|
|
24556
|
+
return `${String(exitCode)}.${String(minor)}`;
|
|
24557
|
+
}
|
|
24558
|
+
var DRIFT_META = {
|
|
24559
|
+
INVALID_REFERENCE_PATH: {
|
|
24560
|
+
phase: "resolve_reference_bundle",
|
|
24561
|
+
exitCode: ExitCode.misconfiguration,
|
|
24562
|
+
minor: 1,
|
|
24563
|
+
title: "Reference path is not valid under the repository root or does not exist. Check the path is relative to --cwd or exists on disk."
|
|
24564
|
+
},
|
|
24565
|
+
WORKTREE_PREP_FAILED: {
|
|
24566
|
+
phase: "prepare_analysis_worktree",
|
|
24567
|
+
exitCode: ExitCode.misconfiguration,
|
|
24568
|
+
minor: 3,
|
|
24569
|
+
title: "Analysis worktree could not be prepared for drift analysis. Check git, disk space, and permissions."
|
|
24570
|
+
},
|
|
24571
|
+
EMPTY_BUNDLE: {
|
|
24572
|
+
phase: "resolve_reference_bundle",
|
|
24573
|
+
exitCode: ExitCode.validation,
|
|
24574
|
+
minor: 10,
|
|
24575
|
+
title: "Reference bundle was empty or could not be read for drift analysis."
|
|
24576
|
+
},
|
|
24577
|
+
UNRESOLVED_REFERENCE_ROOT: {
|
|
24578
|
+
phase: "resolve_reference_root",
|
|
24579
|
+
exitCode: ExitCode.validation,
|
|
24580
|
+
minor: 11,
|
|
24581
|
+
title: "Reference root document could not be resolved. Use --index or ensure an index markdown exists."
|
|
24582
|
+
},
|
|
24583
|
+
MANIFEST_FAILED: {
|
|
24584
|
+
phase: "build_comparison_manifest",
|
|
24585
|
+
exitCode: ExitCode.validation,
|
|
24586
|
+
minor: 12,
|
|
24587
|
+
title: "Comparison manifest could not be built for drift analysis."
|
|
24588
|
+
},
|
|
24589
|
+
AGENT_FAILED: {
|
|
24590
|
+
phase: "run_drift_analysis",
|
|
24591
|
+
exitCode: ExitCode.opencode,
|
|
24592
|
+
minor: 1,
|
|
24593
|
+
title: "OpenCode drift comparison did not complete successfully. Check OpenCode logs and credentials."
|
|
24594
|
+
},
|
|
24595
|
+
INVALID_AGENT_PAYLOAD: {
|
|
24596
|
+
phase: "validate_and_render_report",
|
|
24597
|
+
exitCode: ExitCode.opencode,
|
|
24598
|
+
minor: 2,
|
|
24599
|
+
title: "Drift agent returned a payload that failed validation before the report could be written."
|
|
24600
|
+
},
|
|
24601
|
+
REPORT_WRITE_FAILED: {
|
|
24602
|
+
phase: "validate_and_render_report",
|
|
24603
|
+
exitCode: ExitCode.validation,
|
|
24604
|
+
minor: 13,
|
|
24605
|
+
title: "Drift report markdown could not be written under the analysis worktree."
|
|
24606
|
+
},
|
|
24607
|
+
INDEX_PATCH_FAILED: {
|
|
24608
|
+
phase: "update_reference_index",
|
|
24609
|
+
exitCode: ExitCode.validation,
|
|
24610
|
+
minor: 14,
|
|
24611
|
+
title: "Reference root markdown could not be updated with the drift report link."
|
|
24612
|
+
},
|
|
24613
|
+
PROMOTE_FAILED: {
|
|
24614
|
+
phase: "promote_drift_artefacts",
|
|
24615
|
+
exitCode: ExitCode.upload,
|
|
24616
|
+
minor: 1,
|
|
24617
|
+
title: "Promoting drift artefacts from the analysis worktree to the workspace failed."
|
|
24618
|
+
}
|
|
24619
|
+
};
|
|
24620
|
+
var INDEX_PATH_ERROR_SUBSTRING = "--index";
|
|
24621
|
+
function invalidReferencePathMeta(error) {
|
|
24622
|
+
const isIndex = (error ?? "").includes(INDEX_PATH_ERROR_SUBSTRING);
|
|
24623
|
+
if (isIndex) {
|
|
24624
|
+
return {
|
|
24625
|
+
phase: "resolve_reference_root",
|
|
24626
|
+
exitCode: ExitCode.misconfiguration,
|
|
24627
|
+
minor: 2,
|
|
24628
|
+
title: "Index path (--index) is not under the repository root or does not exist. Fix the path or omit --index."
|
|
24629
|
+
};
|
|
24630
|
+
}
|
|
24631
|
+
return DRIFT_META.INVALID_REFERENCE_PATH;
|
|
24632
|
+
}
|
|
24633
|
+
function contextDriftExitCodeFor(code, error) {
|
|
24634
|
+
if (code === "INVALID_REFERENCE_PATH") {
|
|
24635
|
+
return invalidReferencePathMeta(error).exitCode;
|
|
24636
|
+
}
|
|
24637
|
+
return DRIFT_META[code].exitCode;
|
|
24638
|
+
}
|
|
24639
|
+
var DRIFT_PHASE_LABEL = {
|
|
24640
|
+
parse_cli: "CLI",
|
|
24641
|
+
prepare_analysis_worktree: "Analysis worktree",
|
|
24642
|
+
resolve_reference_bundle: "Reference bundle",
|
|
24643
|
+
resolve_reference_root: "Reference root",
|
|
24644
|
+
build_comparison_manifest: "Comparison manifest",
|
|
24645
|
+
run_drift_analysis: "Drift analysis",
|
|
24646
|
+
validate_and_render_report: "Drift report",
|
|
24647
|
+
update_reference_index: "Reference index",
|
|
24648
|
+
promote_drift_artefacts: "Promotion",
|
|
24649
|
+
finalize: "Finalize"
|
|
24650
|
+
};
|
|
24651
|
+
function readableLabelForDriftFactoryPhase(phase) {
|
|
24652
|
+
return DRIFT_PHASE_LABEL[phase] ?? phase;
|
|
24653
|
+
}
|
|
24654
|
+
function contextDriftFactoryFailureRow(code, error) {
|
|
24655
|
+
const meta = code === "INVALID_REFERENCE_PATH" ? invalidReferencePathMeta(error) : DRIFT_META[code];
|
|
24656
|
+
const failureExitId = failureExitIdFromParts(meta.exitCode, meta.minor);
|
|
24657
|
+
const detail = error.trim().length > 0 ? error.trim() : void 0;
|
|
24658
|
+
const base = {
|
|
24659
|
+
factory: DRIFT_FACTORY,
|
|
24660
|
+
phase: meta.phase,
|
|
24661
|
+
exitCode: meta.exitCode,
|
|
24662
|
+
failureExitId,
|
|
24663
|
+
title: meta.title,
|
|
24664
|
+
detail
|
|
24665
|
+
};
|
|
24666
|
+
if (meta.exitCode === ExitCode.validation) {
|
|
24667
|
+
return { ...base, validationExitId: failureExitId };
|
|
24668
|
+
}
|
|
24669
|
+
return base;
|
|
24670
|
+
}
|
|
24671
|
+
|
|
24672
|
+
// src/cli/factoryValidationStderr.ts
|
|
24673
|
+
function readableLabelForAnyFactory(row2) {
|
|
24674
|
+
return row2.factory === "context_drift" ? readableLabelForDriftFactoryPhase(row2.phase) : readableLabelForFactoryPhase(row2.phase);
|
|
24675
|
+
}
|
|
24676
|
+
function stderrLinesForFactoryFailures(failures, exitCode) {
|
|
24677
|
+
const lines = [`=== Easyspecs factory failure (exit ${String(exitCode)})`];
|
|
24678
|
+
for (const f of failures) {
|
|
24679
|
+
const label = readableLabelForAnyFactory(f);
|
|
24680
|
+
let bullet = ` \u2022 [${factoryFailureDisplayId(f)}] ${label} \u2014 ${f.title}`;
|
|
24681
|
+
if (f.validationSubcode) {
|
|
24682
|
+
bullet += ` (${f.validationSubcode})`;
|
|
24683
|
+
}
|
|
24684
|
+
lines.push(bullet);
|
|
24685
|
+
if (f.detail) {
|
|
24686
|
+
for (const part of f.detail.split(/\r?\n/)) {
|
|
24687
|
+
if (part.length > 0) {
|
|
24688
|
+
lines.push(` ${part}`);
|
|
24689
|
+
}
|
|
24690
|
+
}
|
|
24691
|
+
}
|
|
24692
|
+
}
|
|
24693
|
+
const n = failures.length;
|
|
24694
|
+
if (n === 1 && failures[0]) {
|
|
24695
|
+
lines.push(`Exit ${String(exitCode)} \u2014 ${failures[0].title}`);
|
|
24696
|
+
} else {
|
|
24697
|
+
lines.push(`Exit ${String(exitCode)} \u2014 ${String(n)} pipeline phase(s) failed.`);
|
|
24698
|
+
}
|
|
24699
|
+
return lines;
|
|
24700
|
+
}
|
|
24701
|
+
|
|
24351
24702
|
// src/factory/updateContext/runUpdateContextFactory.ts
|
|
24352
24703
|
var fs49 = __toESM(require("node:fs"));
|
|
24353
24704
|
var path48 = __toESM(require("node:path"));
|
|
@@ -27260,7 +27611,23 @@ function formatCliStderrLine(line, useAnsi) {
|
|
|
27260
27611
|
}
|
|
27261
27612
|
|
|
27262
27613
|
// src/cli/main.ts
|
|
27263
|
-
var PKG_VERSION = "0.0.
|
|
27614
|
+
var PKG_VERSION = "0.0.17";
|
|
27615
|
+
function isNonEmptyFactoryFailureArray(x) {
|
|
27616
|
+
if (!Array.isArray(x) || x.length === 0) {
|
|
27617
|
+
return false;
|
|
27618
|
+
}
|
|
27619
|
+
for (const item of x) {
|
|
27620
|
+
if (typeof item !== "object" || item === null) {
|
|
27621
|
+
return false;
|
|
27622
|
+
}
|
|
27623
|
+
const r = item;
|
|
27624
|
+
const hasId = typeof r.failureExitId === "string" || typeof r.validationExitId === "string";
|
|
27625
|
+
if (typeof r.phase !== "string" || typeof r.title !== "string" || !hasId) {
|
|
27626
|
+
return false;
|
|
27627
|
+
}
|
|
27628
|
+
}
|
|
27629
|
+
return true;
|
|
27630
|
+
}
|
|
27264
27631
|
function isEasyspecsConfigReadError(e) {
|
|
27265
27632
|
return e instanceof EasyspecsConfigInvalidJsonError || e instanceof EasyspecsConfigSchemaError;
|
|
27266
27633
|
}
|
|
@@ -27279,6 +27646,20 @@ function logErr(flags, ...a) {
|
|
|
27279
27646
|
console.error(...a);
|
|
27280
27647
|
}
|
|
27281
27648
|
}
|
|
27649
|
+
function logExitCodeSummary(code, flags) {
|
|
27650
|
+
if (code === ExitCode.ok) {
|
|
27651
|
+
return;
|
|
27652
|
+
}
|
|
27653
|
+
if (flags?.json === true) {
|
|
27654
|
+
return;
|
|
27655
|
+
}
|
|
27656
|
+
const line = `Exit ${String(code)} \u2014 ${describeExitCode(code)}`;
|
|
27657
|
+
if (flags) {
|
|
27658
|
+
logErr(flags, line);
|
|
27659
|
+
} else {
|
|
27660
|
+
console.error(line);
|
|
27661
|
+
}
|
|
27662
|
+
}
|
|
27282
27663
|
function buildDoctorInspectPayload(merged, repoConfig) {
|
|
27283
27664
|
return {
|
|
27284
27665
|
merged: redactMergedCliSettingsForDump(merged),
|
|
@@ -27444,6 +27825,7 @@ async function main() {
|
|
|
27444
27825
|
parsed = parseCliWithCommander(process.argv.slice(2));
|
|
27445
27826
|
} catch (e) {
|
|
27446
27827
|
console.error(e instanceof Error ? e.message : String(e));
|
|
27828
|
+
logExitCodeSummary(ExitCode.usage);
|
|
27447
27829
|
process.exit(ExitCode.usage);
|
|
27448
27830
|
}
|
|
27449
27831
|
const { flags, positionals } = parsed;
|
|
@@ -27465,6 +27847,7 @@ async function main() {
|
|
|
27465
27847
|
const unknown = tail.filter((t) => t !== "--overwrite");
|
|
27466
27848
|
if (unknown.length > 0) {
|
|
27467
27849
|
logErr(flags, `Unknown arguments for config init: ${unknown.join(", ")}`);
|
|
27850
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27468
27851
|
process.exit(ExitCode.usage);
|
|
27469
27852
|
}
|
|
27470
27853
|
try {
|
|
@@ -27488,6 +27871,7 @@ async function main() {
|
|
|
27488
27871
|
} catch (e) {
|
|
27489
27872
|
if (isEasyspecsConfigReadError(e)) {
|
|
27490
27873
|
console.error(e.message);
|
|
27874
|
+
logExitCodeSummary(ExitCode.misconfiguration, flags);
|
|
27491
27875
|
process.exit(ExitCode.misconfiguration);
|
|
27492
27876
|
}
|
|
27493
27877
|
throw e;
|
|
@@ -27499,6 +27883,7 @@ async function main() {
|
|
|
27499
27883
|
const extra = rest.slice(1).filter((t) => t.length > 0);
|
|
27500
27884
|
if (!id || extra.length > 0) {
|
|
27501
27885
|
logErr(flags, "Usage: easyspecs-cli config set-project-id <easyspecsProjectId>");
|
|
27886
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27502
27887
|
process.exit(ExitCode.usage);
|
|
27503
27888
|
}
|
|
27504
27889
|
try {
|
|
@@ -27523,6 +27908,7 @@ async function main() {
|
|
|
27523
27908
|
} catch (e) {
|
|
27524
27909
|
if (isEasyspecsConfigReadError(e)) {
|
|
27525
27910
|
console.error(e.message);
|
|
27911
|
+
logExitCodeSummary(ExitCode.misconfiguration, flags);
|
|
27526
27912
|
process.exit(ExitCode.misconfiguration);
|
|
27527
27913
|
}
|
|
27528
27914
|
throw e;
|
|
@@ -27534,6 +27920,7 @@ async function main() {
|
|
|
27534
27920
|
const extra = rest.slice(1).filter((t) => t.length > 0);
|
|
27535
27921
|
if (!url || extra.length > 0) {
|
|
27536
27922
|
logErr(flags, "Usage: easyspecs-cli config set-git-remote <url>");
|
|
27923
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27537
27924
|
process.exit(ExitCode.usage);
|
|
27538
27925
|
}
|
|
27539
27926
|
try {
|
|
@@ -27558,6 +27945,7 @@ async function main() {
|
|
|
27558
27945
|
} catch (e) {
|
|
27559
27946
|
if (isEasyspecsConfigReadError(e)) {
|
|
27560
27947
|
console.error(e.message);
|
|
27948
|
+
logExitCodeSummary(ExitCode.misconfiguration, flags);
|
|
27561
27949
|
process.exit(ExitCode.misconfiguration);
|
|
27562
27950
|
}
|
|
27563
27951
|
throw e;
|
|
@@ -27576,6 +27964,7 @@ async function main() {
|
|
|
27576
27964
|
} catch (e) {
|
|
27577
27965
|
if (isEasyspecsConfigReadError(e)) {
|
|
27578
27966
|
console.error(e.message);
|
|
27967
|
+
logExitCodeSummary(ExitCode.misconfiguration, flags);
|
|
27579
27968
|
process.exit(ExitCode.misconfiguration);
|
|
27580
27969
|
}
|
|
27581
27970
|
throw e;
|
|
@@ -27594,12 +27983,29 @@ async function main() {
|
|
|
27594
27983
|
repoRoot
|
|
27595
27984
|
});
|
|
27596
27985
|
const finish = (code, envelope) => {
|
|
27986
|
+
const ffRaw = envelope.factoryFailures;
|
|
27987
|
+
const factoryFailuresPack = isNonEmptyFactoryFailureArray(ffRaw) ? ffRaw.map(
|
|
27988
|
+
(r) => normalizeFactoryFailureRow(
|
|
27989
|
+
r
|
|
27990
|
+
)
|
|
27991
|
+
) : void 0;
|
|
27992
|
+
const exitMeaningComputed = code !== ExitCode.ok ? factoryFailuresPack !== void 0 && factoryFailuresPack.length > 0 ? exitMeaningFromFactoryFailures(factoryFailuresPack) : describeExitCode(code) : "";
|
|
27597
27993
|
if (flags.json) {
|
|
27598
27994
|
printJsonLine({
|
|
27599
27995
|
command: cmd,
|
|
27600
27996
|
durationMs: Date.now() - t0,
|
|
27601
|
-
...envelope
|
|
27997
|
+
...envelope,
|
|
27998
|
+
...factoryFailuresPack !== void 0 ? { factoryFailures: factoryFailuresPack } : {},
|
|
27999
|
+
...code !== ExitCode.ok ? { exitCode: code, exitMeaning: exitMeaningComputed } : {}
|
|
27602
28000
|
});
|
|
28001
|
+
} else if (code !== ExitCode.ok) {
|
|
28002
|
+
if (factoryFailuresPack !== void 0 && factoryFailuresPack.length > 0) {
|
|
28003
|
+
for (const ln of stderrLinesForFactoryFailures(factoryFailuresPack, code)) {
|
|
28004
|
+
logErr(flags, ln);
|
|
28005
|
+
}
|
|
28006
|
+
} else {
|
|
28007
|
+
logErr(flags, `Exit ${String(code)} \u2014 ${describeExitCode(code)}`);
|
|
28008
|
+
}
|
|
27603
28009
|
}
|
|
27604
28010
|
process.exit(code);
|
|
27605
28011
|
throw new Error("unreachable");
|
|
@@ -27620,6 +28026,7 @@ async function main() {
|
|
|
27620
28026
|
} catch (e) {
|
|
27621
28027
|
logErr(flags, e instanceof Error ? e.message : String(e));
|
|
27622
28028
|
logErr(flags, "Usage: easyspecs-cli doctor [--readiness] [--inspect-config]");
|
|
28029
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27623
28030
|
process.exit(ExitCode.usage);
|
|
27624
28031
|
}
|
|
27625
28032
|
const agentsDir = resolveOpenCodeAgentsDir(repoRoot, repoConfig);
|
|
@@ -27684,6 +28091,7 @@ async function main() {
|
|
|
27684
28091
|
sessionPathRaw = extracted.sessionPath;
|
|
27685
28092
|
} catch (e) {
|
|
27686
28093
|
logErr(flags, e instanceof Error ? e.message : String(e));
|
|
28094
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27687
28095
|
process.exit(ExitCode.usage);
|
|
27688
28096
|
}
|
|
27689
28097
|
const parsedCli = parseAuthLoginTail(tailForCreds);
|
|
@@ -27709,6 +28117,7 @@ async function main() {
|
|
|
27709
28117
|
flags,
|
|
27710
28118
|
"Non-interactive (--ci): set easyspecs.auth.ciLogin.email and easyspecs.auth.ciLogin.password in .easyspecs/config.json"
|
|
27711
28119
|
);
|
|
28120
|
+
logExitCodeSummary(ExitCode.usage, flags);
|
|
27712
28121
|
process.exit(ExitCode.usage);
|
|
27713
28122
|
}
|
|
27714
28123
|
if (sessionPathRaw !== void 0) {
|
|
@@ -28043,12 +28452,29 @@ async function main() {
|
|
|
28043
28452
|
signal: ctrl.signal,
|
|
28044
28453
|
log: (line) => logErr(flags, line)
|
|
28045
28454
|
});
|
|
28046
|
-
|
|
28047
|
-
|
|
28048
|
-
|
|
28455
|
+
if (dres.exitOk && dres.ok) {
|
|
28456
|
+
if (dres.driftReportPath && !flags.json && !dres.dryRun) {
|
|
28457
|
+
console.log(dres.driftReportPath);
|
|
28458
|
+
}
|
|
28459
|
+
const { exitOk: _driftOkA, ...driftPayload } = dres;
|
|
28460
|
+
finish(ExitCode.ok, driftPayload);
|
|
28461
|
+
}
|
|
28462
|
+
if (dres.ok === false) {
|
|
28463
|
+
const row2 = contextDriftFactoryFailureRow(dres.code, dres.error);
|
|
28464
|
+
const driftExit = contextDriftExitCodeFor(dres.code, dres.error);
|
|
28465
|
+
const { exitOk: _driftOkB, ...driftRest } = dres;
|
|
28466
|
+
const envelope = {
|
|
28467
|
+
...driftRest,
|
|
28468
|
+
ok: false,
|
|
28469
|
+
factoryFailures: [row2],
|
|
28470
|
+
error: composeFactoryValidationError(dres.error, [row2])
|
|
28471
|
+
};
|
|
28472
|
+
if (driftExit === ExitCode.validation) {
|
|
28473
|
+
Object.assign(envelope, primaryFailureAliases([row2]));
|
|
28474
|
+
}
|
|
28475
|
+
finish(driftExit, envelope);
|
|
28049
28476
|
}
|
|
28050
|
-
|
|
28051
|
-
finish(driftExit, driftPayload);
|
|
28477
|
+
finish(ExitCode.internal, { ok: false, error: "Unexpected context drift result shape." });
|
|
28052
28478
|
}
|
|
28053
28479
|
if (pos[0] === "analysis") {
|
|
28054
28480
|
const synthesisOnly = positionals.includes("--synthesis-only");
|
|
@@ -28152,12 +28578,18 @@ async function main() {
|
|
|
28152
28578
|
runBackendSyncImpl
|
|
28153
28579
|
});
|
|
28154
28580
|
const res = await runGenerateContextFactory(deps);
|
|
28155
|
-
|
|
28581
|
+
const analysisEnvelope = {
|
|
28156
28582
|
ok: res.ok,
|
|
28157
|
-
error: res.message,
|
|
28158
28583
|
cancelled: res.cancelled,
|
|
28159
|
-
totalElapsedMs: res.totalElapsedMs
|
|
28160
|
-
|
|
28584
|
+
totalElapsedMs: res.totalElapsedMs,
|
|
28585
|
+
error: res.message
|
|
28586
|
+
};
|
|
28587
|
+
if (!res.ok && !res.cancelled && res.factoryFailures && res.factoryFailures.length > 0) {
|
|
28588
|
+
analysisEnvelope.factoryFailures = res.factoryFailures;
|
|
28589
|
+
Object.assign(analysisEnvelope, primaryFailureAliases(res.factoryFailures));
|
|
28590
|
+
analysisEnvelope.error = composeFactoryValidationError(res.message, res.factoryFailures);
|
|
28591
|
+
}
|
|
28592
|
+
finish(res.ok ? ExitCode.ok : res.cancelled ? ExitCode.cancelled : ExitCode.validation, analysisEnvelope);
|
|
28161
28593
|
}
|
|
28162
28594
|
if (pos[0] === "update" && pos[1] === "context") {
|
|
28163
28595
|
for (const a of pos.slice(2)) {
|
|
@@ -28547,10 +28979,13 @@ async function main() {
|
|
|
28547
28979
|
command: cmd,
|
|
28548
28980
|
durationMs: Date.now() - t0,
|
|
28549
28981
|
ok: false,
|
|
28550
|
-
error: e.message
|
|
28982
|
+
error: e.message,
|
|
28983
|
+
exitCode: ExitCode.misconfiguration,
|
|
28984
|
+
exitMeaning: describeExitCode(ExitCode.misconfiguration)
|
|
28551
28985
|
});
|
|
28552
28986
|
} else {
|
|
28553
28987
|
console.error(e.message);
|
|
28988
|
+
logExitCodeSummary(ExitCode.misconfiguration, flags);
|
|
28554
28989
|
}
|
|
28555
28990
|
process.exit(ExitCode.misconfiguration);
|
|
28556
28991
|
}
|