@momentiq/dark-factory-cli 1.0.0 → 2.0.0
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 +121 -40
- package/dist/adapters/codex-sdk.d.ts +66 -0
- package/dist/adapters/codex-sdk.d.ts.map +1 -1
- package/dist/adapters/codex-sdk.js +321 -4
- package/dist/adapters/codex-sdk.js.map +1 -1
- package/dist/adapters/critic-result-schema.d.ts +15 -10
- package/dist/adapters/critic-result-schema.d.ts.map +1 -1
- package/dist/adapters/critic-result-schema.js +10 -0
- package/dist/adapters/critic-result-schema.js.map +1 -1
- package/dist/adapters/cursor-cli.d.ts +19 -1
- package/dist/adapters/cursor-cli.d.ts.map +1 -1
- package/dist/adapters/cursor-cli.js +96 -14
- package/dist/adapters/cursor-cli.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +7 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/static-schema-lint.d.ts +68 -0
- package/dist/adapters/static-schema-lint.d.ts.map +1 -0
- package/dist/adapters/static-schema-lint.js +813 -0
- package/dist/adapters/static-schema-lint.js.map +1 -0
- package/dist/branch-protection/spec-default.yaml +2 -2
- package/dist/cli.d.ts +17 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +414 -113
- package/dist/cli.js.map +1 -1
- package/dist/commands/findings.d.ts +6 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +196 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/show.d.ts +11 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +78 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/skills.d.ts +6 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +248 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +85 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/compact/index.d.ts +2 -0
- package/dist/compact/index.d.ts.map +1 -0
- package/dist/compact/index.js +3 -0
- package/dist/compact/index.js.map +1 -0
- package/dist/compact/lockfile.d.ts +53 -0
- package/dist/compact/lockfile.d.ts.map +1 -0
- package/dist/compact/lockfile.js +626 -0
- package/dist/compact/lockfile.js.map +1 -0
- package/dist/cycle-doc-validator/validate_cycle_doc.py +39 -9
- package/dist/doctor.d.ts +51 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +497 -1
- package/dist/doctor.js.map +1 -1
- package/dist/evidence/docker-build.d.ts +6 -0
- package/dist/evidence/docker-build.d.ts.map +1 -0
- package/dist/evidence/docker-build.js +199 -0
- package/dist/evidence/docker-build.js.map +1 -0
- package/dist/evidence/index.d.ts +1 -0
- package/dist/evidence/index.d.ts.map +1 -1
- package/dist/evidence/index.js +6 -0
- package/dist/evidence/index.js.map +1 -1
- package/dist/handoff/handoff-verb.d.ts.map +1 -1
- package/dist/handoff/handoff-verb.js +6 -3
- package/dist/handoff/handoff-verb.js.map +1 -1
- package/dist/handoff/ports.d.ts +8 -0
- package/dist/handoff/ports.d.ts.map +1 -1
- package/dist/handoff/real-clients.js +1 -1
- package/dist/handoff/real-clients.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/show-status-core.d.ts +63 -0
- package/dist/lib/show-status-core.d.ts.map +1 -0
- package/dist/lib/show-status-core.js +156 -0
- package/dist/lib/show-status-core.js.map +1 -0
- package/dist/mcp/prompts.d.ts.map +1 -1
- package/dist/mcp/prompts.js +113 -78
- package/dist/mcp/prompts.js.map +1 -1
- package/dist/mcp/resources.js +1 -1
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/doctor.d.ts.map +1 -1
- package/dist/mcp/tools/doctor.js +5 -0
- package/dist/mcp/tools/doctor.js.map +1 -1
- package/dist/mcp/tools/findings.d.ts +0 -22
- package/dist/mcp/tools/findings.d.ts.map +1 -1
- package/dist/mcp/tools/findings.js +5 -73
- package/dist/mcp/tools/findings.js.map +1 -1
- package/dist/mcp/tools/handoff.d.ts.map +1 -1
- package/dist/mcp/tools/handoff.js +10 -17
- package/dist/mcp/tools/handoff.js.map +1 -1
- package/dist/mcp/tools/review-bypass.d.ts.map +1 -1
- package/dist/mcp/tools/review-bypass.js +22 -1
- package/dist/mcp/tools/review-bypass.js.map +1 -1
- package/dist/mcp/tools/skills-install.d.ts +6 -0
- package/dist/mcp/tools/skills-install.d.ts.map +1 -0
- package/dist/mcp/tools/skills-install.js +260 -0
- package/dist/mcp/tools/skills-install.js.map +1 -0
- package/dist/mcp/tools/stats-gate.d.ts.map +1 -1
- package/dist/mcp/tools/stats-gate.js +63 -15
- package/dist/mcp/tools/stats-gate.js.map +1 -1
- package/dist/policy/baseline.d.ts.map +1 -1
- package/dist/policy/baseline.js +11 -3
- package/dist/policy/baseline.js.map +1 -1
- package/dist/policy/gate.d.ts +1 -1
- package/dist/policy/gate.d.ts.map +1 -1
- package/dist/policy/gate.js +96 -12
- package/dist/policy/gate.js.map +1 -1
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +123 -4
- package/dist/prompt.js.map +1 -1
- package/dist/report.d.ts +130 -3
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +367 -10
- package/dist/report.js.map +1 -1
- package/dist/runner.d.ts +6 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +213 -4
- package/dist/runner.js.map +1 -1
- package/dist/self-consistency.d.ts +144 -0
- package/dist/self-consistency.d.ts.map +1 -0
- package/dist/self-consistency.js +368 -0
- package/dist/self-consistency.js.map +1 -0
- package/dist/skills/config.d.ts +176 -0
- package/dist/skills/config.d.ts.map +1 -0
- package/dist/skills/config.js +251 -0
- package/dist/skills/config.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +8 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/install.d.ts +62 -0
- package/dist/skills/install.d.ts.map +1 -0
- package/dist/skills/install.js +315 -0
- package/dist/skills/install.js.map +1 -0
- package/dist/skills/template.d.ts +42 -0
- package/dist/skills/template.d.ts.map +1 -0
- package/dist/skills/template.js +95 -0
- package/dist/skills/template.js.map +1 -0
- package/dist/trusted-surface/rebind.d.ts.map +1 -1
- package/dist/trusted-surface/rebind.js +199 -2
- package/dist/trusted-surface/rebind.js.map +1 -1
- package/package.json +36 -2
- package/skills/README.md +89 -0
- package/skills/chief-engineer-blitz/SKILL.md.tmpl +443 -0
- package/skills/chief-engineer-blitz/skill.json +53 -0
- package/skills/chief-engineer-review/SKILL.md.tmpl +184 -0
- package/skills/chief-engineer-review/references/review-examples.md.tmpl +130 -0
- package/skills/chief-engineer-review/skill.json +67 -0
- package/skills/chief-engineer-review/templates/code-review-prompt.md.tmpl +111 -0
- package/skills/chief-engineer-review/templates/escalation-prompt.md.tmpl +56 -0
- package/skills/chief-engineer-review/templates/plan-review-prompt.md.tmpl +74 -0
- package/skills/skill-schema.json +73 -0
package/dist/cli.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
// subcommands accept their own flags directly (see `cmdAudit` and
|
|
35
35
|
// `cmdAdmitPr` below).
|
|
36
36
|
import { spawnSync } from "node:child_process";
|
|
37
|
-
import { accessSync, appendFileSync, constants as fsConstants, existsSync, readFileSync, } from "node:fs";
|
|
37
|
+
import { accessSync, appendFileSync, constants as fsConstants, existsSync, readFileSync, realpathSync, } from "node:fs";
|
|
38
38
|
import { dirname, resolve } from "node:path";
|
|
39
39
|
import { fileURLToPath } from "node:url";
|
|
40
40
|
import { runValidateCycleDoc } from "./cycle-doc-validator/index.js";
|
|
@@ -52,12 +52,13 @@ import { AdapterRegistry, collectRequiredEnvVars, } from "./adapters/critic.js";
|
|
|
52
52
|
// path). Phase B-PUBLISH-pkg (cycle 331.1, alpha.5): see
|
|
53
53
|
// https://github.com/momentiq-ai/dark-factory/pull/<this-pr>.
|
|
54
54
|
import { loadAgentReviewConfig } from "./policy/config.js";
|
|
55
|
-
import { buildCriticReport } from "./report.js";
|
|
55
|
+
import { buildCriticReport, buildZeroEvidenceDiagnostic } from "./report.js";
|
|
56
56
|
import { runReview, runCommitGate } from "./runner.js";
|
|
57
|
+
import { buildDefaultSelfConsistencyProbe, } from "./self-consistency.js";
|
|
57
58
|
import { resolveArtifactDir, telemetryPath } from "./paths.js";
|
|
58
59
|
// Phase F-LOCAL — hook-facing subcommand support.
|
|
59
60
|
import { loadDopplerBootstrapEnv, DEFAULT_BOOTSTRAP_ALLOWLIST, } from "./doppler-bootstrap.js";
|
|
60
|
-
import { runDoctor } from "./doctor.js";
|
|
61
|
+
import { classifyDoctorState, detectCloudEnv, runDoctor, } from "./doctor.js";
|
|
61
62
|
import { resolveProfile } from "./policy/profile.js";
|
|
62
63
|
import { commitsForPushUpdate, parsePrePushUpdates, resolveCommit, commitParent, changedFiles, } from "./git.js";
|
|
63
64
|
import { runQualityGates } from "./evidence/quality-gates.js";
|
|
@@ -69,10 +70,21 @@ import { summarizeGate } from "./policy/gate.js";
|
|
|
69
70
|
// stderr-only diagnostics) is structurally distinct from the other
|
|
70
71
|
// subcommands. See docs/roadmap/cycles/cycle5-mcp-server.md.
|
|
71
72
|
import { cmdMcp } from "./mcp/cli.js";
|
|
73
|
+
// DFP #192 — `df skills install/list` subcommand. The skills module is the
|
|
74
|
+
// install engine (consumer-config-driven template rendering); cmdSkills
|
|
75
|
+
// in commands/skills.ts is the CLI wrapper around it.
|
|
76
|
+
import { cmdSkills } from "./commands/skills.js";
|
|
72
77
|
// Cycle 11 Phase 11.1 — `df flow` namespace surfacing the PR Flow
|
|
73
78
|
// Assessor's records from momentiq-ai/df-assessments. See
|
|
74
79
|
// docs/roadmap/cycles/cycle11-flow-assessor-surfacing-and-tools.md.
|
|
75
80
|
import { cmdFlow } from "./commands/flow/index.js";
|
|
81
|
+
import { cmdShow } from "./commands/show.js";
|
|
82
|
+
import { cmdStatus } from "./commands/status.js";
|
|
83
|
+
// Cycle 13 (dark-factory-platform#149) — `df findings --range` surfaces
|
|
84
|
+
// the per-commit iteration-receipt artifacts that the new (default)
|
|
85
|
+
// final-commit-only `df gate-push` semantic leaves un-gated. See
|
|
86
|
+
// src/commands/findings.ts for the rationale.
|
|
87
|
+
import { cmdFindings } from "./commands/findings.js";
|
|
76
88
|
// Cycle 12 Phase 12.2 — agent handoff protocol (v2 — Issue-anchored, native-
|
|
77
89
|
// baton). The four cmd* functions below wrap the verb orchestrators exported
|
|
78
90
|
// from src/handoff/index.ts and route to them at the bottom of main(). v1
|
|
@@ -106,58 +118,79 @@ function printHelp(meta) {
|
|
|
106
118
|
" df --version Print version and exit",
|
|
107
119
|
" df --help Print this help and exit",
|
|
108
120
|
"",
|
|
109
|
-
"
|
|
110
|
-
" df
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
" df
|
|
121
|
+
"Local critic workflow:",
|
|
122
|
+
" df review Run the local critic against a commit",
|
|
123
|
+
" (subscription auth — Cursor / Codex /",
|
|
124
|
+
" Claude logins, NOT pay-per-token keys).",
|
|
125
|
+
" df gate-push Pre-push gate — block a push when HEAD's",
|
|
126
|
+
" critic verdict has unresolved blockers.",
|
|
127
|
+
" Default since Cycle 13 (dark-factory-",
|
|
128
|
+
" platform#149): gates the HEAD commit",
|
|
129
|
+
" only; intermediate commits are iteration",
|
|
130
|
+
" receipts (see `df findings --range`).",
|
|
131
|
+
" Legacy per-commit gating via",
|
|
132
|
+
" `--full-range` / `DF_GATE_FULL_RANGE=1`.",
|
|
133
|
+
" df show Render the per-commit review artifact",
|
|
134
|
+
" (with --json for the structured form).",
|
|
135
|
+
" df status Terse verdict + per-critic status for",
|
|
136
|
+
" a commit (with --json for shell pipes).",
|
|
137
|
+
" df findings --range BASE..HEAD",
|
|
138
|
+
" Audit-inspect per-commit findings for the",
|
|
139
|
+
" iteration-receipt artifacts the default",
|
|
140
|
+
" final-commit-only `df gate-push` leaves",
|
|
141
|
+
" un-gated (Cycle 13). NOT a gate.",
|
|
142
|
+
" df gates Run configured quality gates and",
|
|
143
|
+
" triggered verification routes.",
|
|
144
|
+
" df stats Pretty-print critic call stats + bypass",
|
|
145
|
+
" audit (alias for `df audit stats`).",
|
|
146
|
+
" df doctor Verify env: node, hooks, artifact dir,",
|
|
147
|
+
" per-adapter auth.",
|
|
114
148
|
"",
|
|
115
|
-
"
|
|
116
|
-
" df
|
|
117
|
-
"
|
|
149
|
+
"CI / reusable-workflow gates:",
|
|
150
|
+
" df status-check Sentinel aggregator (pr-status-check).",
|
|
151
|
+
" Exits 0 — merge queue is the real",
|
|
152
|
+
" aggregator.",
|
|
153
|
+
" df critic Real Critic Orchestrator wiring (agent-",
|
|
154
|
+
" critic gate). Loads .agent-review/",
|
|
155
|
+
" config.json, runs configured vendor",
|
|
156
|
+
" adapters via runReview, writes aggregate",
|
|
157
|
+
" verdict to .git/agent-reviews/<sha>.",
|
|
158
|
+
" Degrades-and-passes on any error.",
|
|
118
159
|
"",
|
|
119
|
-
"
|
|
120
|
-
" df
|
|
121
|
-
"
|
|
122
|
-
" df
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
160
|
+
"Cycle-doc + project services:",
|
|
161
|
+
" df validate-cycle-doc Validate a PR's Cycle:/Issue:/",
|
|
162
|
+
" ProjectItem: trailers.",
|
|
163
|
+
" df audit-branch-protection Detect drift between spec.yaml and the",
|
|
164
|
+
" live GitHub ruleset.",
|
|
165
|
+
" df sync-trackers Reconcile GitHub tracker issues with",
|
|
166
|
+
" cycle docs.",
|
|
167
|
+
" df attribute-pr Write PR's Cycle: trailer into the",
|
|
168
|
+
" project board's Cycle Ref field.",
|
|
169
|
+
" df audit stats Summarize the _runs.ndjson audit trail.",
|
|
170
|
+
" df admit-pr Classify a PR as plan vs code.",
|
|
127
171
|
"",
|
|
128
|
-
"
|
|
129
|
-
" df review Background-friendly local critic invocation",
|
|
130
|
-
" for .husky/post-commit. Honors profile auth",
|
|
131
|
-
" (chatgpt/composer/subscription) so Cursor +",
|
|
132
|
-
" Codex + Claude SUBSCRIPTIONS are consumed,",
|
|
133
|
-
" NOT pay-per-token API keys.",
|
|
134
|
-
" df gate-push Read pre-push stdin + gate every commit in",
|
|
135
|
-
" the pushed range. CI mode: --commit SHA --ci.",
|
|
136
|
-
" Honors AGENT_REVIEW_BYPASS for emergencies.",
|
|
137
|
-
" df doctor Verify env: node, hooks, hookPath, artifact",
|
|
138
|
-
" dir, Doppler bootstrap, per-adapter auth.",
|
|
139
|
-
" df gates Run configured `requiredQualityGates` and",
|
|
140
|
-
" triggered verification routes. No LLM calls.",
|
|
141
|
-
" df stats Pretty-print critic call stats + bypass audit.",
|
|
142
|
-
" Alias for `df audit stats`. No LLM calls.",
|
|
143
|
-
"",
|
|
144
|
-
"Subcommands (Phase G — agentic MCP surface, cycle5):",
|
|
172
|
+
"Agentic surface (MCP server):",
|
|
145
173
|
" df mcp Start the local stdio Model Context Protocol",
|
|
146
174
|
" server. Exposes the CLI surface to any MCP-",
|
|
147
|
-
" speaking agent
|
|
148
|
-
"
|
|
149
|
-
" prompt catalog. Run `df mcp --help` for the",
|
|
175
|
+
" speaking agent as a structured tool + resource",
|
|
176
|
+
" + prompt catalog. Run `df mcp --help` for the",
|
|
150
177
|
" .mcp.json wiring snippet.",
|
|
151
178
|
"",
|
|
152
|
-
"
|
|
153
|
-
" df
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
179
|
+
"Bundled skills (consumer-shape):",
|
|
180
|
+
" df skills install <name> Render + install a bundled skill into",
|
|
181
|
+
" .claude/skills/<name>/, driven by the consumer's",
|
|
182
|
+
" darkfactory.yaml. --all installs every skill",
|
|
183
|
+
" flagged enabled: true.",
|
|
184
|
+
" df skills list List bundled skill names + summaries.",
|
|
185
|
+
"",
|
|
186
|
+
"PR Flow Assessor surfacing:",
|
|
187
|
+
" df flow Surface the PR Flow Assessor's records.",
|
|
188
|
+
" Six sub-subcommands: show / agent /",
|
|
189
|
+
" patterns / cost / trends / rollup.",
|
|
190
|
+
" Each carries --json. Run `df flow",
|
|
191
|
+
" --help` for the list.",
|
|
159
192
|
"",
|
|
160
|
-
"
|
|
193
|
+
"Agent handoff protocol (v2 Issue-anchored):",
|
|
161
194
|
" df handoff [issue] Put a work-stream on the handoff stack:",
|
|
162
195
|
" [--link <ref>]... upsert the marker-bounded rehydration",
|
|
163
196
|
" [--unlink <ref>]... note (read from stdin) as the dedicated",
|
|
@@ -168,7 +201,7 @@ function printHelp(meta) {
|
|
|
168
201
|
" df handoffs List the stack of handed-off Issues (open,",
|
|
169
202
|
" labeled `handoff`, unassigned).",
|
|
170
203
|
" df accept <issue> Claim a handoff Issue (assign you), rehydrate,",
|
|
171
|
-
" then close it
|
|
204
|
+
" then close it.",
|
|
172
205
|
" df rehydrate [issue] Read-only catch-up on a handoff Issue's note —",
|
|
173
206
|
" derives LIVE state first, changes no ownership.",
|
|
174
207
|
" No-arg: 2-tier (open+@me → closed+@me ≤7d).",
|
|
@@ -177,42 +210,25 @@ function printHelp(meta) {
|
|
|
177
210
|
" The local hook path (review/gate-push) consumes Cursor / Codex / Claude",
|
|
178
211
|
" SUBSCRIPTIONS via the developer's existing CLI logins. Vendor API keys",
|
|
179
212
|
" (CURSOR_API_KEY / CODEX_API_KEY / GEMINI_API_KEY / XAI_API_KEY) are the",
|
|
180
|
-
" CI cold-path fallback only.
|
|
181
|
-
" codex critic) enforce subscription-only with no env-presence fallback",
|
|
182
|
-
" (cycle 322.7 issue #2103).",
|
|
213
|
+
" CI cold-path fallback only.",
|
|
183
214
|
"",
|
|
184
|
-
"Each
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"(
|
|
189
|
-
"exit 0 even on failure so the reusable workflows do not block the merge",
|
|
190
|
-
"queue on a single vendor flake — vendor errors register in the artifact",
|
|
191
|
-
"as `status=error` and the configured min-complete-quorum policy decides",
|
|
192
|
-
"the aggregate verdict.",
|
|
215
|
+
"Each subcommand carries its own `--help` for full flag documentation.",
|
|
216
|
+
"Critic and status-check exit 0 even on failure so the reusable workflows",
|
|
217
|
+
"do not block the merge queue on a single vendor flake — vendor errors",
|
|
218
|
+
"register in the artifact as `status=error` and the configured aggregation",
|
|
219
|
+
"policy (min-complete-quorum, block-if-any) decides the aggregate verdict.",
|
|
193
220
|
"",
|
|
194
221
|
"System requirements:",
|
|
195
222
|
" Node.js >=20",
|
|
196
223
|
" python3 (>=3.11)",
|
|
197
|
-
" gh CLI (authenticated) for
|
|
224
|
+
" gh CLI (authenticated) for project/cycle-doc GitHub API calls",
|
|
198
225
|
" git on PATH",
|
|
199
226
|
"",
|
|
200
227
|
"Library usage today:",
|
|
201
228
|
" import { runReview, evaluateCommitGate, buildReviewPacket }",
|
|
202
229
|
` from \"${name}\";`,
|
|
203
230
|
"",
|
|
204
|
-
|
|
205
|
-
" subcommands (review/gate-push/doctor/gates/stats) so consumer",
|
|
206
|
-
" repos can wire .husky hooks to the subscription-auth local",
|
|
207
|
-
" critic path. Also: Phase B-PUBLISH-pkg fixes CLI loadability",
|
|
208
|
-
" under `npm install --ignore-scripts` for all non-`df critic`",
|
|
209
|
-
" subcommands. Phase F wires the real Critic Orchestrator and",
|
|
210
|
-
" dogfoods dark-factory on its own PRs. Reusable workflows from",
|
|
211
|
-
" Phase E now invoke real critic logic. The substrate validates",
|
|
212
|
-
" itself end-to-end. Pure-TS port of #5/#7/#9 tracked as Phase",
|
|
213
|
-
" C-PORT follow-up.",
|
|
214
|
-
"",
|
|
215
|
-
"Docs: https://github.com/momentiq-ai/dark-factory",
|
|
231
|
+
`Version: ${version}. Docs: https://github.com/momentiq-ai/dark-factory`,
|
|
216
232
|
"",
|
|
217
233
|
].join("\n"));
|
|
218
234
|
}
|
|
@@ -220,11 +236,9 @@ function printVersion(meta) {
|
|
|
220
236
|
process.stdout.write(`${meta.version ?? "unknown"}\n`);
|
|
221
237
|
}
|
|
222
238
|
function notImplemented(sub) {
|
|
223
|
-
process.stderr.write(`df: subcommand "${sub}" is not implemented in this
|
|
224
|
-
`
|
|
225
|
-
`
|
|
226
|
-
` Phase F upgraded both to real implementations;\n` +
|
|
227
|
-
` "${sub}" lands in Phase G or later (see \`df --help\`).\n`);
|
|
239
|
+
process.stderr.write(`df: subcommand "${sub}" is not implemented in this build.\n` +
|
|
240
|
+
` Run \`df --help\` for the list of available subcommands.\n` +
|
|
241
|
+
` Track / request at: https://github.com/momentiq-ai/dark-factory/issues\n`);
|
|
228
242
|
return 2;
|
|
229
243
|
}
|
|
230
244
|
async function runPhaseCSubcommand(sub, rest) {
|
|
@@ -264,7 +278,7 @@ const PHASE_D_SUBCOMMANDS = new Set(["audit", "admit-pr"]);
|
|
|
264
278
|
//
|
|
265
279
|
// - `status-check` stays thin: a sentinel aggregator. The merge queue's
|
|
266
280
|
// ALLGREEN rule already does the real cross-check aggregation; this
|
|
267
|
-
// subcommand exists for the `
|
|
281
|
+
// subcommand exists for the `pr-status-check` ruleset context.
|
|
268
282
|
//
|
|
269
283
|
// - `critic` is now wired to the real Critic Orchestrator (Phase B
|
|
270
284
|
// extraction). It loads `.agent-review/config.json`, instantiates
|
|
@@ -308,10 +322,27 @@ const CYCLE12_SUBCOMMANDS = new Set([
|
|
|
308
322
|
// not at the top level, so the surface stays grouped (`df flow --help` is
|
|
309
323
|
// authoritative) and we don't pollute the top-level `df` namespace.
|
|
310
324
|
const CYCLE11_SUBCOMMANDS = new Set(["flow"]);
|
|
325
|
+
// `df show` / `df status` / `df findings` — CLI artifact-inspection
|
|
326
|
+
// subcommands. `show` + `status` mirror the df_show_run / df_findings MCP
|
|
327
|
+
// tools per-commit (cycle 5); `findings --range <base>..<head>` (Cycle
|
|
328
|
+
// 13, dark-factory-platform#149) walks the iteration-receipt artifacts
|
|
329
|
+
// the new default final-commit-only `df gate-push` semantic leaves
|
|
330
|
+
// un-gated. All three share the loader + mappers in
|
|
331
|
+
// src/lib/show-status-core.ts so the CLI's `--json` output stays
|
|
332
|
+
// byte-equivalent with the MCP tool's structuredContent envelope (cycle 5
|
|
333
|
+
// spec requirement). Registered here so the early --help interception
|
|
334
|
+
// forwards each `df <cmd> --help` to its per-subcommand help printer.
|
|
335
|
+
const SHOW_STATUS_SUBCOMMANDS = new Set(["show", "status", "findings"]);
|
|
336
|
+
// `df skills` — bundled-skill installer (Cycle: DFP #192). The namespace
|
|
337
|
+
// fans out into `install/list` sub-subcommands handled inside cmdSkills;
|
|
338
|
+
// registered here so the early --help interception above forwards
|
|
339
|
+
// `df skills --help` to cmdSkills' own help printer instead of the
|
|
340
|
+
// top-level printHelp.
|
|
341
|
+
const SKILLS_SUBCOMMANDS = new Set(["skills"]);
|
|
311
342
|
function cmdStatusCheck(_rest) {
|
|
312
|
-
//
|
|
343
|
+
// pr-status-check is a sentinel aggregator. As cycle 331.1 Phase E
|
|
313
344
|
// documents in `.github/workflows/pr-status-check.yml`, this gate is
|
|
314
|
-
// present specifically to satisfy the `
|
|
345
|
+
// present specifically to satisfy the `pr-status-check` ruleset context
|
|
315
346
|
// — its passage means "the workflow itself reached this step", which
|
|
316
347
|
// is the contract every other status check separately enforces. There
|
|
317
348
|
// is no useful aggregation work to do here that the merge queue's
|
|
@@ -351,6 +382,12 @@ const ADAPTER_LOADERS = [
|
|
|
351
382
|
{ id: "codex-sdk", modulePath: "./adapters/codex-sdk.js", className: "CodexSdkAdapter" },
|
|
352
383
|
{ id: "gemini-sdk", modulePath: "./adapters/gemini-sdk.js", className: "GeminiSdkAdapter" },
|
|
353
384
|
{ id: "grok-direct-sdk", modulePath: "./adapters/grok-direct-sdk.js", className: "GrokDirectSdkAdapter" },
|
|
385
|
+
// Consumer dark-factory-platform#107 — deterministic schema-lint adapter.
|
|
386
|
+
// No vendor SDK, no env vars, no network; pure ajv-backed validation of
|
|
387
|
+
// schema-annotated code blocks in changed markdown / config files.
|
|
388
|
+
// Loads alongside the LLM adapters so the local profile can include it
|
|
389
|
+
// without any additional toolchain.
|
|
390
|
+
{ id: "static-schema-lint", modulePath: "./adapters/static-schema-lint.js", className: "StaticSchemaLintAdapter" },
|
|
354
391
|
];
|
|
355
392
|
async function buildDefaultAdapterRegistry() {
|
|
356
393
|
// Each adapter reads its API key from the corresponding env var by
|
|
@@ -403,6 +440,31 @@ function appendStepSummary(markdown) {
|
|
|
403
440
|
// best-effort — never let a summary-write failure change the gate outcome.
|
|
404
441
|
}
|
|
405
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Cursor finding (cli.ts:560) — resolve the production
|
|
445
|
+
* self-consistency probe so the policy `unilateralVetoRules.requireCorroborationFor`
|
|
446
|
+
* actually demotes findings on the default OSS CLI path. Returns the
|
|
447
|
+
* probe callable when (a) the loaded policy lists `self_inconsistent`
|
|
448
|
+
* AND (b) `buildDefaultSelfConsistencyProbe()` returns a non-null
|
|
449
|
+
* factory (i.e. the env has a `GEMINI_API_KEY`). Otherwise returns
|
|
450
|
+
* undefined, which makes the runner skip the probe pass entirely
|
|
451
|
+
* (byte-identical pre-#112 behavior).
|
|
452
|
+
*
|
|
453
|
+
* Operators who want a non-Gemini probe (or the hosted worker's
|
|
454
|
+
* proprietary calibrated probe) replace the callable by importing
|
|
455
|
+
* `runReview` directly with their own `selfConsistencyProbe`.
|
|
456
|
+
*/
|
|
457
|
+
export function resolveProductionSelfConsistencyProbe(loaded) {
|
|
458
|
+
const flags = loaded.config.aggregation.unilateralVetoRules?.requireCorroborationFor ?? [];
|
|
459
|
+
if (!flags.includes("self_inconsistent"))
|
|
460
|
+
return undefined;
|
|
461
|
+
const probe = buildDefaultSelfConsistencyProbe();
|
|
462
|
+
if (probe === null) {
|
|
463
|
+
process.stderr.write("[critic] self-consistency probe disabled — GEMINI_API_KEY unset; policy `unilateralVetoRules.requireCorroborationFor: [self_inconsistent]` is configured but probe-flagged findings will not be demoted on this run.\n");
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
return probe;
|
|
467
|
+
}
|
|
406
468
|
async function cmdCritic(rest) {
|
|
407
469
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
408
470
|
process.stdout.write([
|
|
@@ -443,11 +505,13 @@ async function cmdCritic(rest) {
|
|
|
443
505
|
// audit trail on its own PRs.
|
|
444
506
|
const artifactDir = await resolveArtifactDir(loaded);
|
|
445
507
|
const sink = new FileTelemetrySink(telemetryPath(artifactDir));
|
|
508
|
+
const selfConsistencyProbe = resolveProductionSelfConsistencyProbe(loaded);
|
|
446
509
|
const outcome = await runReview({
|
|
447
510
|
loaded,
|
|
448
511
|
registry,
|
|
449
512
|
ref: opts.ref,
|
|
450
513
|
telemetry: sink,
|
|
514
|
+
...(selfConsistencyProbe !== undefined ? { selfConsistencyProbe } : {}),
|
|
451
515
|
});
|
|
452
516
|
// sage3c#2213 — surface per-critic errors + a loud degradation
|
|
453
517
|
// warning. `buildCriticReport` is pure; it turns the artifact's
|
|
@@ -631,13 +695,28 @@ async function cmdReview(rest) {
|
|
|
631
695
|
const ref = flags["commit"] ?? "HEAD";
|
|
632
696
|
const foreground = flags["foreground"] === true || flags["foreground"] === "true";
|
|
633
697
|
try {
|
|
698
|
+
const selfConsistencyProbe = resolveProductionSelfConsistencyProbe(loaded);
|
|
634
699
|
const outcome = await runReview({
|
|
635
700
|
loaded,
|
|
636
701
|
registry,
|
|
637
702
|
ref,
|
|
638
703
|
telemetry: sink,
|
|
639
704
|
profileName,
|
|
705
|
+
...(selfConsistencyProbe !== undefined ? { selfConsistencyProbe } : {}),
|
|
640
706
|
});
|
|
707
|
+
// Issue #51 — loud post-completion diagnostic for zero-evidence
|
|
708
|
+
// reviews. When every critic errored (no completed verdicts), the
|
|
709
|
+
// gate will silently block at push time and operators reach for
|
|
710
|
+
// AGENT_REVIEW_BYPASS as the workaround. Surface the failure here
|
|
711
|
+
// so the operator sees it at commit time, with a specific
|
|
712
|
+
// remediation per critic and a pointer to the artifact JSON for
|
|
713
|
+
// deeper triage. The helper is pure (no I/O); the only side effect
|
|
714
|
+
// is the stderr write below.
|
|
715
|
+
const configHasProfiles = hasProfileEntry(loaded.config, profileName);
|
|
716
|
+
const diagnostic = buildZeroEvidenceDiagnostic(outcome.artifact, outcome.paths.jsonPath, { configHasProfiles });
|
|
717
|
+
if (diagnostic.isZeroEvidence) {
|
|
718
|
+
process.stderr.write(diagnostic.stderr);
|
|
719
|
+
}
|
|
641
720
|
if (foreground) {
|
|
642
721
|
process.stdout.write(`df review: ${outcome.artifact.gateVerdict ?? "complete"} for ${outcome.artifact.commit.slice(0, 12)}\n`);
|
|
643
722
|
process.stdout.write(` json: ${outcome.paths.jsonPath}\n`);
|
|
@@ -650,20 +729,108 @@ async function cmdReview(rest) {
|
|
|
650
729
|
return 1;
|
|
651
730
|
}
|
|
652
731
|
}
|
|
732
|
+
// Issue #51 — the post-completion diagnostic needs to know whether
|
|
733
|
+
// the loaded config has the requested profile so it can prepend a
|
|
734
|
+
// "add 'profiles' to .agent-review/config.json" remediation when the
|
|
735
|
+
// seed config is the broken sage-blueprint shape (`profiles: {}`).
|
|
736
|
+
// Returns `true` when both a profiles map exists AND the requested
|
|
737
|
+
// name is present; `false` otherwise. (Back-compat configs with no
|
|
738
|
+
// profiles map AT ALL also return `false`, which is the correct
|
|
739
|
+
// signal — the diagnostic surfaces the seed remediation in both
|
|
740
|
+
// cases.)
|
|
741
|
+
function hasProfileEntry(config, profileName) {
|
|
742
|
+
if (!config.profiles)
|
|
743
|
+
return false;
|
|
744
|
+
return config.profiles[profileName] !== undefined;
|
|
745
|
+
}
|
|
653
746
|
// ----- df gate-push -----
|
|
747
|
+
//
|
|
748
|
+
// Cycle 13 (dark-factory-platform#149) — semantic flip:
|
|
749
|
+
//
|
|
750
|
+
// DEFAULT MODE: final-commit-only. The HEAD (last) commit of each
|
|
751
|
+
// push update is the only one whose verdict decides the gate. The
|
|
752
|
+
// intermediate commits' per-SHA artifacts still exist on disk
|
|
753
|
+
// (`.git/agent-reviews/<sha>.json`) as iteration receipts and stay
|
|
754
|
+
// visible via `df findings --range <base>..<head>`, but they do
|
|
755
|
+
// NOT influence the push outcome. This matches the documented
|
|
756
|
+
// find-fix-new-commit pattern: commit B fixes commit A's blockers,
|
|
757
|
+
// B's verdict is what gates the push.
|
|
758
|
+
//
|
|
759
|
+
// Soundness caveat: each per-SHA artifact reviews `parent..commit`
|
|
760
|
+
// only, NOT `base..tip`. HEAD's APPROVED verdict therefore proves
|
|
761
|
+
// the LAST incremental change is safe; it does NOT prove the
|
|
762
|
+
// cumulative `base..tip` diff is safe. The banner + help text
|
|
763
|
+
// surface this so operators can choose `--full-range` (or rely on
|
|
764
|
+
// the CI cold-path agent-critic workflow that reviews the full PR
|
|
765
|
+
// diff) when cumulative soundness matters more than termination of
|
|
766
|
+
// the find-fix loop.
|
|
767
|
+
//
|
|
768
|
+
// LEGACY MODE: full-range — gate every commit in the push update,
|
|
769
|
+
// block on any single commit's blockers. Opt-in via `--full-range`
|
|
770
|
+
// OR `DF_GATE_FULL_RANGE=1`. Use cases: forensic replay, per-commit
|
|
771
|
+
// audit (each commit is independently reviewed in a deploy log),
|
|
772
|
+
// cumulative-state evidence requirements.
|
|
773
|
+
//
|
|
774
|
+
// The legacy mode was the implicit default before Cycle 13. It
|
|
775
|
+
// makes the find-fix-new-commit pattern non-terminating in practice
|
|
776
|
+
// (any iteration round produces stale intermediate `CHANGES_REQUESTED`
|
|
777
|
+
// verdicts that block the push and force a squash, which spawns a
|
|
778
|
+
// fresh full-diff review that surfaces new findings — see
|
|
779
|
+
// consumer-side dark-factory-platform#149 for the full failure mode).
|
|
780
|
+
//
|
|
781
|
+
// The mode banner — `GATE MODE: final-commit-only (HEAD=<sha>)` or
|
|
782
|
+
// `GATE MODE: full-range (N commits)` — prints once per push update so
|
|
783
|
+
// operators see which semantic is active without needing to remember
|
|
784
|
+
// the env-var / flag state.
|
|
785
|
+
//
|
|
786
|
+
// `--commit SHA --ci` mode is a single-commit replay path (CI cold-
|
|
787
|
+
// path); the legacy/default split does not apply to it (one commit
|
|
788
|
+
// only).
|
|
789
|
+
function isFullRangeRequested(flags) {
|
|
790
|
+
if (flags["full-range"] === true || flags["full-range"] === "true") {
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
const env = process.env["DF_GATE_FULL_RANGE"];
|
|
794
|
+
if (env !== undefined && env !== "" && env !== "0" && env.toLowerCase() !== "false") {
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
654
799
|
async function cmdGatePush(rest) {
|
|
655
800
|
if (rest.includes("--help") || rest.includes("-h")) {
|
|
656
801
|
process.stdout.write([
|
|
657
|
-
"df gate-push —
|
|
802
|
+
"df gate-push — gate a push on the local-critic verdict.",
|
|
658
803
|
"",
|
|
659
804
|
"Usage:",
|
|
660
|
-
" df gate-push [--profile NAME]
|
|
661
|
-
" df gate-push --
|
|
805
|
+
" df gate-push [--profile NAME] # local pre-push",
|
|
806
|
+
" df gate-push --full-range [--profile NAME] # legacy: gate every commit",
|
|
807
|
+
" df gate-push --commit SHA --ci [--profile NAME] # CI replay (single commit)",
|
|
808
|
+
"",
|
|
809
|
+
"Designed for .husky/pre-push. Reads git's pre-push protocol on stdin.",
|
|
810
|
+
"",
|
|
811
|
+
"Default mode (Cycle 13 — dark-factory-platform#149):",
|
|
812
|
+
" Gates ONLY the HEAD (final) commit of each push update. Intermediate",
|
|
813
|
+
" commits' per-SHA artifacts at .git/agent-reviews/<sha>.json are still",
|
|
814
|
+
" written (iteration audit), but they DO NOT influence the gate.",
|
|
815
|
+
" Inspect them with: `df findings --range <base>..HEAD`.",
|
|
816
|
+
"",
|
|
817
|
+
"Legacy mode — opt in via `--full-range` OR `DF_GATE_FULL_RANGE=1`:",
|
|
818
|
+
" Gates EVERY commit in the push range; any single commit's blocker",
|
|
819
|
+
" blocks the push. Use for forensic replay or per-commit deploy-log",
|
|
820
|
+
" audit. Note: this was the implicit default before Cycle 13 and is",
|
|
821
|
+
" what produced the find-fix-new-commit termination failure.",
|
|
662
822
|
"",
|
|
663
|
-
"
|
|
664
|
-
"
|
|
665
|
-
"
|
|
666
|
-
"
|
|
823
|
+
"Soundness caveat (default mode):",
|
|
824
|
+
" Each per-SHA artifact reviews `parent..commit` only, NOT `base..tip`.",
|
|
825
|
+
" HEAD's APPROVED verdict therefore proves the LAST incremental change",
|
|
826
|
+
" is safe; it does NOT prove the cumulative `base..tip` diff is safe.",
|
|
827
|
+
" For cumulative-state evidence either pass `--full-range` (or set",
|
|
828
|
+
" `DF_GATE_FULL_RANGE=1`) for per-commit gating, or rely on the CI",
|
|
829
|
+
" cold-path agent-critic workflow (which reviews the full PR diff).",
|
|
830
|
+
"",
|
|
831
|
+
"Exit code:",
|
|
832
|
+
" 1 if the gating verdict (HEAD-only or full-range, per mode) blocks.",
|
|
833
|
+
" 0 otherwise.",
|
|
667
834
|
"",
|
|
668
835
|
"Bypass:",
|
|
669
836
|
" AGENT_REVIEW_BYPASS=\"<reason>\" git push # logged to _runs.ndjson",
|
|
@@ -702,6 +869,7 @@ async function cmdGatePush(rest) {
|
|
|
702
869
|
process.stdout.write(`-- ${sha.slice(0, 12)}\n${summarizeGate(result)}\n`);
|
|
703
870
|
return result.blocked ? 1 : 0;
|
|
704
871
|
}
|
|
872
|
+
const fullRange = isFullRangeRequested(flags);
|
|
705
873
|
const stdin = await readStdinUtf8FromTtyOrStream();
|
|
706
874
|
const updates = parsePrePushUpdates(stdin);
|
|
707
875
|
if (updates.length === 0) {
|
|
@@ -715,18 +883,45 @@ async function cmdGatePush(rest) {
|
|
|
715
883
|
const commits = await commitsForPushUpdate(update);
|
|
716
884
|
if (commits.length === 0)
|
|
717
885
|
continue;
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
886
|
+
if (fullRange) {
|
|
887
|
+
// Legacy mode — gate every commit in the range. Pre-Cycle-13
|
|
888
|
+
// behavior, kept for forensic replay and per-commit audit
|
|
889
|
+
// contexts (e.g. production-deploy push where each commit is
|
|
890
|
+
// independently reviewed in the deploy log).
|
|
891
|
+
process.stdout.write(`GATE MODE: full-range (${commits.length} commits) — ${update.localRef} -> ${update.remoteRef}\n`);
|
|
892
|
+
for (const sha of commits) {
|
|
893
|
+
const result = await runCommitGate({
|
|
894
|
+
loaded,
|
|
895
|
+
commit: sha,
|
|
896
|
+
telemetry: sink,
|
|
897
|
+
profileName,
|
|
898
|
+
});
|
|
899
|
+
process.stdout.write(`-- ${sha.slice(0, 12)}\n${summarizeGate(result)}\n`);
|
|
900
|
+
if (result.blocked)
|
|
901
|
+
blockedAny = true;
|
|
902
|
+
}
|
|
903
|
+
continue;
|
|
729
904
|
}
|
|
905
|
+
// Default mode (Cycle 13) — final-commit-only. The last commit in
|
|
906
|
+
// the range (`commitsForPushUpdate` returns the rev-list reverse,
|
|
907
|
+
// so HEAD is the last element) is the only one whose verdict
|
|
908
|
+
// gates the push. Intermediate commits' artifacts are iteration
|
|
909
|
+
// receipts; inspect them with `df findings --range`.
|
|
910
|
+
const headSha = commits[commits.length - 1] ?? "";
|
|
911
|
+
process.stdout.write(`GATE MODE: final-commit-only (HEAD=${headSha.slice(0, 12)}) — ${update.localRef} -> ${update.remoteRef}\n`);
|
|
912
|
+
if (commits.length > 1) {
|
|
913
|
+
process.stdout.write(` intermediate commits (${commits.length - 1}) are iteration receipts; inspect with: df findings --range ${update.remoteRef === "" ? "<base>" : `${update.remoteSha.slice(0, 12)}..${headSha.slice(0, 12)}`}\n`);
|
|
914
|
+
process.stdout.write(` soundness caveat: HEAD's verdict covers parent..HEAD only, NOT base..HEAD; use --full-range (or the CI agent-critic on the full PR diff) when cumulative-state evidence is required.\n`);
|
|
915
|
+
}
|
|
916
|
+
const result = await runCommitGate({
|
|
917
|
+
loaded,
|
|
918
|
+
commit: headSha,
|
|
919
|
+
telemetry: sink,
|
|
920
|
+
profileName,
|
|
921
|
+
});
|
|
922
|
+
process.stdout.write(`-- ${headSha.slice(0, 12)}\n${summarizeGate(result)}\n`);
|
|
923
|
+
if (result.blocked)
|
|
924
|
+
blockedAny = true;
|
|
730
925
|
}
|
|
731
926
|
return blockedAny ? 1 : 0;
|
|
732
927
|
}
|
|
@@ -737,11 +932,18 @@ async function cmdDoctor(rest) {
|
|
|
737
932
|
"df doctor — verify the environment for hook-facing critic invocation.",
|
|
738
933
|
"",
|
|
739
934
|
"Usage:",
|
|
740
|
-
" df doctor [--profile NAME]",
|
|
935
|
+
" df doctor [--profile NAME] [--json]",
|
|
741
936
|
"",
|
|
742
937
|
"Checks: node version, hooks dir, hook executable, core.hooksPath,",
|
|
743
|
-
"artifact dir, doppler bootstrap, per-adapter
|
|
744
|
-
"auth lives here).",
|
|
938
|
+
"artifact dir, doppler bootstrap, cloud-env detection, per-adapter",
|
|
939
|
+
"doctor() (subscription auth lives here; skipped in cloud envs).",
|
|
940
|
+
"",
|
|
941
|
+
"Flags:",
|
|
942
|
+
" --json Emit a machine-readable JSON",
|
|
943
|
+
" report on stdout instead of the",
|
|
944
|
+
" human-readable INFO/OK/FAIL lines",
|
|
945
|
+
" (for consumer-side pre-push hooks",
|
|
946
|
+
" that fail-fast on auth_pending).",
|
|
745
947
|
"",
|
|
746
948
|
"Environment:",
|
|
747
949
|
" AGENT_REVIEW_PROFILE=<name> Profile to validate (default: local)",
|
|
@@ -749,6 +951,12 @@ async function cmdDoctor(rest) {
|
|
|
749
951
|
" DF_DOCTOR_SKIP_HOOKS=1 Skip git-core-hookspath check",
|
|
750
952
|
" DF_DOCTOR_SKIP_DOPPLER=1 Skip doppler-cli-on-path check",
|
|
751
953
|
"",
|
|
954
|
+
"Cloud-env markers (any-of triggers subscription-auth skip):",
|
|
955
|
+
" CODESPACES=true GitHub Codespaces",
|
|
956
|
+
" REMOTE_CONTAINERS=true VS Code Dev Containers",
|
|
957
|
+
" CLAUDE_CODE_SANDBOX=true Claude Code web sandbox",
|
|
958
|
+
" DEVCONTAINER=true generic devcontainer images",
|
|
959
|
+
"",
|
|
752
960
|
].join("\n"));
|
|
753
961
|
return 0;
|
|
754
962
|
}
|
|
@@ -770,15 +978,55 @@ async function cmdDoctor(rest) {
|
|
|
770
978
|
bootstrap,
|
|
771
979
|
profileName,
|
|
772
980
|
});
|
|
773
|
-
|
|
981
|
+
// Issue #51 — surface the 3-state triage classification FIRST, before
|
|
982
|
+
// the per-check INFO/OK/FAIL block. The headline tells the operator
|
|
983
|
+
// whether they're in config_missing / auth_pending / ok before they
|
|
984
|
+
// have to parse per-critic noise.
|
|
985
|
+
const triage = classifyDoctorState({
|
|
986
|
+
config: loaded.config,
|
|
987
|
+
profileName,
|
|
988
|
+
perCriticChecks: checks,
|
|
989
|
+
});
|
|
990
|
+
const cloudEnv = detectCloudEnv();
|
|
991
|
+
const allOk = checks.every((c) => c.passed || c.optional);
|
|
992
|
+
// --json — machine-readable output for consumer-side pre-push hooks
|
|
993
|
+
// (DFP issue #56). Shape is stable and documented in
|
|
994
|
+
// packages/schemas/src/index.ts (DoctorReport). Exit code semantics
|
|
995
|
+
// are the same as the human path: 0 = all required checks passed,
|
|
996
|
+
// 1 = at least one required check failed.
|
|
997
|
+
if (flags["json"]) {
|
|
998
|
+
const report = {
|
|
999
|
+
version: 1,
|
|
1000
|
+
schema: "df-doctor-report-v1",
|
|
1001
|
+
triage: { state: triage.state, line: triage.line },
|
|
1002
|
+
cloudEnv: {
|
|
1003
|
+
detected: cloudEnv.detected,
|
|
1004
|
+
markers: cloudEnv.markers,
|
|
1005
|
+
},
|
|
1006
|
+
// `profile` is part of the stable field set; `JSON.stringify`
|
|
1007
|
+
// omits `undefined`, so we default explicitly here even though
|
|
1008
|
+
// `resolveProfile` already returns `"local"` on the no-flag /
|
|
1009
|
+
// no-env path. Belt-and-braces — keeps the contract intact if the
|
|
1010
|
+
// resolver signature ever loosens.
|
|
1011
|
+
profile: profileName || "local",
|
|
1012
|
+
ok: allOk,
|
|
1013
|
+
checks,
|
|
1014
|
+
};
|
|
1015
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
1016
|
+
return allOk ? 0 : 1;
|
|
1017
|
+
}
|
|
1018
|
+
process.stdout.write(`${triage.line}\n`);
|
|
774
1019
|
for (const c of checks) {
|
|
775
|
-
const label = c.passed ? "OK" : c.optional ? "INFO" : "FAIL";
|
|
1020
|
+
const label = c.passed ? (c.optional ? "INFO" : "OK") : c.optional ? "INFO" : "FAIL";
|
|
776
1021
|
process.stdout.write(`[${label}] ${c.name}: ${c.detail}\n`);
|
|
777
1022
|
if (!c.passed && c.remediation) {
|
|
778
1023
|
process.stdout.write(` fix: ${c.remediation}\n`);
|
|
779
1024
|
}
|
|
780
|
-
if (
|
|
781
|
-
|
|
1025
|
+
else if (c.passed && c.optional && c.remediation) {
|
|
1026
|
+
// INFO-with-remediation surfaces the cloud-env bypass hint loudly
|
|
1027
|
+
// even though the check itself "passed" (it's informational).
|
|
1028
|
+
process.stdout.write(` note: ${c.remediation}\n`);
|
|
1029
|
+
}
|
|
782
1030
|
}
|
|
783
1031
|
return allOk ? 0 : 1;
|
|
784
1032
|
}
|
|
@@ -1468,7 +1716,9 @@ async function main(argv) {
|
|
|
1468
1716
|
!PHASE_F_LOCAL_SUBCOMMANDS.has(sub0) &&
|
|
1469
1717
|
!PHASE_G_SUBCOMMANDS.has(sub0) &&
|
|
1470
1718
|
!CYCLE12_SUBCOMMANDS.has(sub0) &&
|
|
1471
|
-
!CYCLE11_SUBCOMMANDS.has(sub0)
|
|
1719
|
+
!CYCLE11_SUBCOMMANDS.has(sub0) &&
|
|
1720
|
+
!SHOW_STATUS_SUBCOMMANDS.has(sub0) &&
|
|
1721
|
+
!SKILLS_SUBCOMMANDS.has(sub0)) {
|
|
1472
1722
|
printHelp(meta);
|
|
1473
1723
|
return 0;
|
|
1474
1724
|
}
|
|
@@ -1539,12 +1789,63 @@ async function main(argv) {
|
|
|
1539
1789
|
parseFlags,
|
|
1540
1790
|
});
|
|
1541
1791
|
}
|
|
1792
|
+
// `df show` / `df status` — CLI mirrors of the df_show_run / df_findings
|
|
1793
|
+
// MCP tools. See src/lib/show-status-core.ts for the shared backend.
|
|
1794
|
+
if (sub === "show") {
|
|
1795
|
+
return await cmdShow(rest, {
|
|
1796
|
+
stdout: (s) => process.stdout.write(s),
|
|
1797
|
+
stderr: (s) => process.stderr.write(s),
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
if (sub === "status") {
|
|
1801
|
+
return await cmdStatus(rest, {
|
|
1802
|
+
stdout: (s) => process.stdout.write(s),
|
|
1803
|
+
stderr: (s) => process.stderr.write(s),
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
// Cycle 13 (dark-factory-platform#149) — `df findings --range` audit
|
|
1807
|
+
// surface for the iteration-receipt artifacts the new default
|
|
1808
|
+
// final-commit-only `df gate-push` semantic intentionally leaves
|
|
1809
|
+
// un-gated. NOT a gate; opt-in inspection only.
|
|
1810
|
+
if (sub === "findings") {
|
|
1811
|
+
return await cmdFindings(rest, {
|
|
1812
|
+
stdout: (s) => process.stdout.write(s),
|
|
1813
|
+
stderr: (s) => process.stderr.write(s),
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
// DFP #192 — bundled-skill installer surface. The subcommand fans out
|
|
1817
|
+
// into `install/list` inside cmdSkills, mirroring the `df flow` pattern.
|
|
1818
|
+
if (sub === "skills") {
|
|
1819
|
+
return await cmdSkills(rest);
|
|
1820
|
+
}
|
|
1542
1821
|
return notImplemented(sub);
|
|
1543
1822
|
}
|
|
1544
|
-
main(
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1823
|
+
// Only run main() when this module is the program entry point.
|
|
1824
|
+
// Tests and other modules can `import` cli.ts to reuse helpers
|
|
1825
|
+
// (e.g. `resolveProductionSelfConsistencyProbe`) without triggering
|
|
1826
|
+
// `main(process.argv)` as a side-effect of the import. The realpath
|
|
1827
|
+
// dance handles symlinked installs (npm bin shims often symlink
|
|
1828
|
+
// `dist/cli.js` into `node_modules/.bin/df`). Same posture as
|
|
1829
|
+
// sage-cli (see commit f0f945e). Cursor finding (cli.ts:560) tests
|
|
1830
|
+
// reach `resolveProductionSelfConsistencyProbe` by importing this
|
|
1831
|
+
// module; without the guard they'd trigger the help path on import.
|
|
1832
|
+
function isInvokedAsMain() {
|
|
1833
|
+
const entry = process.argv[1];
|
|
1834
|
+
if (!entry)
|
|
1835
|
+
return false;
|
|
1836
|
+
try {
|
|
1837
|
+
return realpathSync(entry) === realpathSync(fileURLToPath(import.meta.url));
|
|
1838
|
+
}
|
|
1839
|
+
catch {
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (isInvokedAsMain()) {
|
|
1844
|
+
main(process.argv).then((code) => {
|
|
1845
|
+
process.exitCode = code;
|
|
1846
|
+
}, (err) => {
|
|
1847
|
+
process.stderr.write(`df: fatal: ${err.message}\n`);
|
|
1848
|
+
process.exitCode = 1;
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1550
1851
|
//# sourceMappingURL=cli.js.map
|