@davidahmann/mill 0.1.4
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/LICENSE +202 -0
- package/README.md +335 -0
- package/dist/audit/repository.d.ts +8 -0
- package/dist/audit/repository.d.ts.map +1 -0
- package/dist/audit/repository.js +232 -0
- package/dist/audit/repository.js.map +1 -0
- package/dist/cli-program.d.ts +12 -0
- package/dist/cli-program.d.ts.map +1 -0
- package/dist/cli-program.js +1020 -0
- package/dist/cli-program.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/lock.d.ts +12 -0
- package/dist/config/lock.d.ts.map +1 -0
- package/dist/config/lock.js +83 -0
- package/dist/config/lock.js.map +1 -0
- package/dist/contracts/canonical.d.ts +7 -0
- package/dist/contracts/canonical.d.ts.map +1 -0
- package/dist/contracts/canonical.js +30 -0
- package/dist/contracts/canonical.js.map +1 -0
- package/dist/contracts/schemas.d.ts +2705 -0
- package/dist/contracts/schemas.d.ts.map +1 -0
- package/dist/contracts/schemas.js +988 -0
- package/dist/contracts/schemas.js.map +1 -0
- package/dist/doctor.d.ts +21 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +138 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/intake/prd.d.ts +14 -0
- package/dist/intake/prd.d.ts.map +1 -0
- package/dist/intake/prd.js +39 -0
- package/dist/intake/prd.js.map +1 -0
- package/dist/planning/impact.d.ts +59 -0
- package/dist/planning/impact.d.ts.map +1 -0
- package/dist/planning/impact.js +400 -0
- package/dist/planning/impact.js.map +1 -0
- package/dist/planning/specification.d.ts +42 -0
- package/dist/planning/specification.d.ts.map +1 -0
- package/dist/planning/specification.js +235 -0
- package/dist/planning/specification.js.map +1 -0
- package/dist/qualification/public-alpha.d.ts +16 -0
- package/dist/qualification/public-alpha.d.ts.map +1 -0
- package/dist/qualification/public-alpha.js +128 -0
- package/dist/qualification/public-alpha.js.map +1 -0
- package/dist/recipes/node-typescript-next-web.d.ts +19 -0
- package/dist/recipes/node-typescript-next-web.d.ts.map +1 -0
- package/dist/recipes/node-typescript-next-web.js +136 -0
- package/dist/recipes/node-typescript-next-web.js.map +1 -0
- package/dist/repository/integration.d.ts +58 -0
- package/dist/repository/integration.d.ts.map +1 -0
- package/dist/repository/integration.js +1385 -0
- package/dist/repository/integration.js.map +1 -0
- package/dist/repository/scan.d.ts +27 -0
- package/dist/repository/scan.d.ts.map +1 -0
- package/dist/repository/scan.js +388 -0
- package/dist/repository/scan.js.map +1 -0
- package/dist/result.d.ts +23 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +24 -0
- package/dist/result.js.map +1 -0
- package/dist/runtime/codex.d.ts +28 -0
- package/dist/runtime/codex.d.ts.map +1 -0
- package/dist/runtime/codex.js +439 -0
- package/dist/runtime/codex.js.map +1 -0
- package/dist/runtime/context.d.ts +10 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +151 -0
- package/dist/runtime/context.js.map +1 -0
- package/dist/runtime/delivery.d.ts +58 -0
- package/dist/runtime/delivery.d.ts.map +1 -0
- package/dist/runtime/delivery.js +1073 -0
- package/dist/runtime/delivery.js.map +1 -0
- package/dist/runtime/dependencies.d.ts +19 -0
- package/dist/runtime/dependencies.d.ts.map +1 -0
- package/dist/runtime/dependencies.js +495 -0
- package/dist/runtime/dependencies.js.map +1 -0
- package/dist/runtime/github.d.ts +112 -0
- package/dist/runtime/github.d.ts.map +1 -0
- package/dist/runtime/github.js +510 -0
- package/dist/runtime/github.js.map +1 -0
- package/dist/runtime/inputs.d.ts +24 -0
- package/dist/runtime/inputs.d.ts.map +1 -0
- package/dist/runtime/inputs.js +237 -0
- package/dist/runtime/inputs.js.map +1 -0
- package/dist/runtime/lease.d.ts +11 -0
- package/dist/runtime/lease.d.ts.map +1 -0
- package/dist/runtime/lease.js +52 -0
- package/dist/runtime/lease.js.map +1 -0
- package/dist/runtime/lifecycle.d.ts +87 -0
- package/dist/runtime/lifecycle.d.ts.map +1 -0
- package/dist/runtime/lifecycle.js +1024 -0
- package/dist/runtime/lifecycle.js.map +1 -0
- package/dist/runtime/process.d.ts +39 -0
- package/dist/runtime/process.d.ts.map +1 -0
- package/dist/runtime/process.js +304 -0
- package/dist/runtime/process.js.map +1 -0
- package/dist/runtime/repository.d.ts +36 -0
- package/dist/runtime/repository.d.ts.map +1 -0
- package/dist/runtime/repository.js +458 -0
- package/dist/runtime/repository.js.map +1 -0
- package/dist/runtime/state.d.ts +121 -0
- package/dist/runtime/state.d.ts.map +1 -0
- package/dist/runtime/state.js +1017 -0
- package/dist/runtime/state.js.map +1 -0
- package/dist/runtime/verifier.d.ts +31 -0
- package/dist/runtime/verifier.d.ts.map +1 -0
- package/dist/runtime/verifier.js +478 -0
- package/dist/runtime/verifier.js.map +1 -0
- package/dist/runtime/worker.d.ts +67 -0
- package/dist/runtime/worker.d.ts.map +1 -0
- package/dist/runtime/worker.js +34 -0
- package/dist/runtime/worker.js.map +1 -0
- package/dist/security/safe-path.d.ts +3 -0
- package/dist/security/safe-path.d.ts.map +1 -0
- package/dist/security/safe-path.js +102 -0
- package/dist/security/safe-path.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/dist/workflows/founder.d.ts +33 -0
- package/dist/workflows/founder.d.ts.map +1 -0
- package/dist/workflows/founder.js +422 -0
- package/dist/workflows/founder.js.map +1 -0
- package/package.json +79 -0
- package/recipes/node-typescript-next-web/.github/workflows/ci.yml +34 -0
- package/recipes/node-typescript-next-web/.node-version +1 -0
- package/recipes/node-typescript-next-web/.tool-versions +1 -0
- package/recipes/node-typescript-next-web/AGENTS.md +44 -0
- package/recipes/node-typescript-next-web/README.md +16 -0
- package/recipes/node-typescript-next-web/WORKFLOW.md +6 -0
- package/recipes/node-typescript-next-web/app/api/health/route.ts +5 -0
- package/recipes/node-typescript-next-web/app/layout.tsx +19 -0
- package/recipes/node-typescript-next-web/app/page.tsx +12 -0
- package/recipes/node-typescript-next-web/app/styles.css +21 -0
- package/recipes/node-typescript-next-web/docs/development.md +16 -0
- package/recipes/node-typescript-next-web/eslint.config.mjs +9 -0
- package/recipes/node-typescript-next-web/gitignore.template +5 -0
- package/recipes/node-typescript-next-web/next-env.d.ts +7 -0
- package/recipes/node-typescript-next-web/next.config.ts +11 -0
- package/recipes/node-typescript-next-web/package-lock.json +7540 -0
- package/recipes/node-typescript-next-web/package.json +39 -0
- package/recipes/node-typescript-next-web/playwright.config.ts +23 -0
- package/recipes/node-typescript-next-web/recipe.yaml +71 -0
- package/recipes/node-typescript-next-web/scripts/check-package.mjs +3 -0
- package/recipes/node-typescript-next-web/src/health.ts +3 -0
- package/recipes/node-typescript-next-web/test/browser/home.spec.ts +14 -0
- package/recipes/node-typescript-next-web/test/integration/health-route.test.ts +11 -0
- package/recipes/node-typescript-next-web/test/unit/health.test.ts +9 -0
- package/recipes/node-typescript-next-web/tsconfig.json +33 -0
- package/schemas/README.md +64 -0
- package/schemas/audit-report.schema.json +59 -0
- package/schemas/blueprint.schema.json +34 -0
- package/schemas/context-manifest.schema.json +88 -0
- package/schemas/delivery-record.schema.json +266 -0
- package/schemas/impact-manifest.schema.json +146 -0
- package/schemas/managed-repository.schema.json +36 -0
- package/schemas/mill-config.schema.json +212 -0
- package/schemas/mill-lock.schema.json +96 -0
- package/schemas/outcome-plan.schema.json +51 -0
- package/schemas/product-contract.schema.json +181 -0
- package/schemas/public-alpha-qualification.schema.json +391 -0
- package/schemas/recipe-manifest.schema.json +187 -0
- package/schemas/release-evidence.schema.json +170 -0
- package/schemas/repository-integration-plan.schema.json +156 -0
- package/schemas/review-result.schema.json +57 -0
- package/schemas/scenario-set.schema.json +84 -0
- package/schemas/source-manifest.schema.json +96 -0
- package/schemas/specification-proposal.schema.json +406 -0
- package/schemas/support-tuple.schema.json +107 -0
- package/schemas/task-packet.schema.json +395 -0
- package/schemas/validation-evidence.schema.json +125 -0
- package/schemas/worker-invocation.schema.json +119 -0
- package/schemas/worker-profile.schema.json +53 -0
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
3
|
+
import { parse as parseYaml } from "yaml";
|
|
4
|
+
import { auditRepository } from "./audit/repository.js";
|
|
5
|
+
import { findRepositoryRoot, enforceExactVersion } from "./config/lock.js";
|
|
6
|
+
import { contractSchemas } from "./contracts/schemas.js";
|
|
7
|
+
import { doctor, doctorReady } from "./doctor.js";
|
|
8
|
+
import { asMillError, ExitCode, MillError } from "./errors.js";
|
|
9
|
+
import { inspectPrd } from "./intake/prd.js";
|
|
10
|
+
import { scanRepository } from "./repository/scan.js";
|
|
11
|
+
import { applyAdoptionIntegration, applyGreenfieldIntegration, planAdoptionIntegration, planDetach, planGreenfieldIntegration, } from "./repository/integration.js";
|
|
12
|
+
import { assessSpecificationProposal, loadPlanningSources, loadSpecificationProposal, promoteSpecificationProposal, semanticProposalDiff, } from "./planning/specification.js";
|
|
13
|
+
import { assessImpactManifest, loadImpactPlanningInputs, } from "./planning/impact.js";
|
|
14
|
+
import { loadPublicAlphaQualification } from "./qualification/public-alpha.js";
|
|
15
|
+
import { finalizeDraftPr, observeDraftPr, openDraftPr, planDraftPr, reconcileDraftPr, } from "./runtime/delivery.js";
|
|
16
|
+
import { cancelRun, codexAuthStatus, qualifyBaseline, resumeRun, reviewRun, runStatus, startLocalRun, stateBackup, statePurge, stateRestore, supportBundle, verifyRun, } from "./runtime/lifecycle.js";
|
|
17
|
+
import { commandResult, formatHuman } from "./result.js";
|
|
18
|
+
import { safeReadText } from "./security/safe-path.js";
|
|
19
|
+
import { MILL_VERSION } from "./version.js";
|
|
20
|
+
import { prepareRepositoryDependencies, startNextReadyOutcome, startFounderDelivery, } from "./workflows/founder.js";
|
|
21
|
+
function parseMode(value) {
|
|
22
|
+
if (value === "inspect" || value === "build" || value === "propose") {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
throw new InvalidArgumentError("mode must be inspect, build, or propose");
|
|
26
|
+
}
|
|
27
|
+
function emit(io, json, result) {
|
|
28
|
+
io.stdout.write(json ? `${JSON.stringify(result)}\n` : formatHuman(result));
|
|
29
|
+
}
|
|
30
|
+
function globals(program) {
|
|
31
|
+
return program.opts();
|
|
32
|
+
}
|
|
33
|
+
function requiredValue(value, option) {
|
|
34
|
+
if (value === undefined || value.trim().length === 0) {
|
|
35
|
+
throw new MillError("USAGE_ERROR", `${option} is required for this operation.`, ExitCode.usage);
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
function integrationOptions(root, options) {
|
|
40
|
+
return {
|
|
41
|
+
sourceRoot: root,
|
|
42
|
+
prdPath: requiredValue(options.prd, "--prd"),
|
|
43
|
+
sourceManifestPath: requiredValue(options.sources, "--sources"),
|
|
44
|
+
proposalPath: requiredValue(options.proposal, "--proposal"),
|
|
45
|
+
productApprovalDigest: requiredValue(options.approveProduct, "--approve-product"),
|
|
46
|
+
repositoryId: requiredValue(options.repositoryId, "--repository-id"),
|
|
47
|
+
approvedBy: requiredValue(options.approvedBy, "--approved-by"),
|
|
48
|
+
approvedAt: requiredValue(options.approvedAt, "--approved-at"),
|
|
49
|
+
authorName: requiredValue(options.authorName, "--author-name"),
|
|
50
|
+
authorEmail: requiredValue(options.authorEmail, "--author-email"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function createProgram(io, jsonErrors = false) {
|
|
54
|
+
const program = new Command();
|
|
55
|
+
program
|
|
56
|
+
.name("millctl")
|
|
57
|
+
.description("Local-first, repo-native software delivery")
|
|
58
|
+
.version(MILL_VERSION)
|
|
59
|
+
.option("--json", "emit a stable JSON result envelope")
|
|
60
|
+
.option("--cwd <path>", "repository or directory to inspect", process.cwd())
|
|
61
|
+
.exitOverride()
|
|
62
|
+
.configureOutput({
|
|
63
|
+
writeOut: (value) => {
|
|
64
|
+
if (!jsonErrors) {
|
|
65
|
+
io.stdout.write(value);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
writeErr: (value) => {
|
|
69
|
+
if (!jsonErrors) {
|
|
70
|
+
io.stderr.write(value);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
program
|
|
75
|
+
.command("doctor")
|
|
76
|
+
.description("report host and repository readiness without running repository code")
|
|
77
|
+
.option("--mode <mode>", "readiness target", parseMode, "inspect")
|
|
78
|
+
.action(async (options) => {
|
|
79
|
+
const global = globals(program);
|
|
80
|
+
const report = await doctor(global.cwd, options.mode);
|
|
81
|
+
const ok = doctorReady(report);
|
|
82
|
+
emit(io, global.json === true, commandResult({
|
|
83
|
+
command: "doctor",
|
|
84
|
+
ok,
|
|
85
|
+
data: report,
|
|
86
|
+
reasons: ok
|
|
87
|
+
? []
|
|
88
|
+
: [
|
|
89
|
+
{
|
|
90
|
+
code: "READINESS_BLOCKED",
|
|
91
|
+
message: "One or more required runtime, tool, or lock checks failed.",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
}));
|
|
95
|
+
if (!ok) {
|
|
96
|
+
throw new MillError("READINESS_BLOCKED", "Readiness checks failed.", ExitCode.unavailable, { resultAlreadyEmitted: true });
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
program
|
|
100
|
+
.command("inspect")
|
|
101
|
+
.description("inspect an untrusted PRD without executing it")
|
|
102
|
+
.requiredOption("--prd <path>", "PRD path inside the selected root")
|
|
103
|
+
.action(async (options) => {
|
|
104
|
+
const global = globals(program);
|
|
105
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
106
|
+
await enforceExactVersion(root);
|
|
107
|
+
const inspection = await inspectPrd(root, options.prd);
|
|
108
|
+
emit(io, global.json === true, commandResult({ command: "inspect", ok: true, data: inspection }));
|
|
109
|
+
});
|
|
110
|
+
program
|
|
111
|
+
.command("new <directory>")
|
|
112
|
+
.description("preview or transactionally create the supported web recipe")
|
|
113
|
+
.option("--dry-run", "return the exact approved file plan without writes")
|
|
114
|
+
.option("--apply", "apply the exact approved file plan")
|
|
115
|
+
.option("--prd <path>", "PRD path inside the selected root")
|
|
116
|
+
.option("--sources <path>", "source manifest path")
|
|
117
|
+
.option("--proposal <path>", "approved specification proposal path")
|
|
118
|
+
.option("--approve-product <digest>", "exact specification approval digest")
|
|
119
|
+
.option("--approve-plan <digest>", "exact integration-plan approval digest")
|
|
120
|
+
.option("--repository-id <uuid>", "new stable repository UUID")
|
|
121
|
+
.option("--approved-by <identity>", "human integration approver")
|
|
122
|
+
.option("--approved-at <time>", "exact ISO approval time")
|
|
123
|
+
.option("--author-name <name>", "initial commit author name")
|
|
124
|
+
.option("--author-email <email>", "initial commit author email")
|
|
125
|
+
.option("--attended", "approve disclosed registry and local Git effects")
|
|
126
|
+
.action(async (directory, options) => {
|
|
127
|
+
const global = globals(program);
|
|
128
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
129
|
+
await enforceExactVersion(root);
|
|
130
|
+
if (options.dryRun === options.apply) {
|
|
131
|
+
throw new MillError("USAGE_ERROR", "Choose exactly one of --dry-run or --apply.", ExitCode.usage);
|
|
132
|
+
}
|
|
133
|
+
const common = {
|
|
134
|
+
...integrationOptions(root, options),
|
|
135
|
+
targetDirectory: directory,
|
|
136
|
+
};
|
|
137
|
+
if (options.dryRun === true) {
|
|
138
|
+
const planned = await planGreenfieldIntegration(common);
|
|
139
|
+
emit(io, global.json === true, commandResult({
|
|
140
|
+
command: "new.plan",
|
|
141
|
+
ok: true,
|
|
142
|
+
data: {
|
|
143
|
+
plan: planned.plan,
|
|
144
|
+
approvalDigest: planned.approvalDigest,
|
|
145
|
+
},
|
|
146
|
+
}));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (options.attended !== true) {
|
|
150
|
+
throw new MillError("ATTENDANCE_REQUIRED", "Greenfield apply requires attended registry and local Git authority.", ExitCode.configuration);
|
|
151
|
+
}
|
|
152
|
+
const result = await applyGreenfieldIntegration({
|
|
153
|
+
...common,
|
|
154
|
+
attended: true,
|
|
155
|
+
planApprovalDigest: requiredValue(options.approvePlan, "--approve-plan"),
|
|
156
|
+
});
|
|
157
|
+
emit(io, global.json === true, commandResult({ command: "new.apply", ok: true, data: result }));
|
|
158
|
+
});
|
|
159
|
+
program
|
|
160
|
+
.command("adopt")
|
|
161
|
+
.description("scan, plan, or transactionally integrate an existing repository")
|
|
162
|
+
.option("--scan-only", "perform only the static adoption scan")
|
|
163
|
+
.option("--plan", "return an exact adoption file plan")
|
|
164
|
+
.option("--apply", "apply an approved plan in an isolated branch")
|
|
165
|
+
.option("--prd <path>", "repo-native PRD path")
|
|
166
|
+
.option("--sources <path>", "source manifest path")
|
|
167
|
+
.option("--proposal <path>", "approved specification proposal path")
|
|
168
|
+
.option("--approve-product <digest>", "exact specification approval digest")
|
|
169
|
+
.option("--approve-plan <digest>", "exact integration-plan approval digest")
|
|
170
|
+
.option("--repository-id <uuid>", "new stable repository UUID")
|
|
171
|
+
.option("--approved-by <identity>", "human integration approver")
|
|
172
|
+
.option("--approved-at <time>", "exact ISO approval time")
|
|
173
|
+
.option("--author-name <name>", "adoption commit author name")
|
|
174
|
+
.option("--author-email <email>", "adoption commit author email")
|
|
175
|
+
.option("--attended", "approve isolated local branch creation")
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
const global = globals(program);
|
|
178
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
179
|
+
await enforceExactVersion(root);
|
|
180
|
+
const selected = [options.scanOnly, options.plan, options.apply].filter((value) => value === true).length;
|
|
181
|
+
if (selected !== 1) {
|
|
182
|
+
throw new MillError("USAGE_ERROR", "Choose exactly one of --scan-only, --plan, or --apply.", ExitCode.usage);
|
|
183
|
+
}
|
|
184
|
+
if (options.plan === true || options.apply === true) {
|
|
185
|
+
const common = {
|
|
186
|
+
...integrationOptions(root, options),
|
|
187
|
+
repositoryRoot: root,
|
|
188
|
+
};
|
|
189
|
+
if (options.plan === true) {
|
|
190
|
+
const planned = await planAdoptionIntegration(common);
|
|
191
|
+
emit(io, global.json === true, commandResult({
|
|
192
|
+
command: "adopt.plan",
|
|
193
|
+
ok: true,
|
|
194
|
+
data: {
|
|
195
|
+
plan: planned.plan,
|
|
196
|
+
approvalDigest: planned.approvalDigest,
|
|
197
|
+
},
|
|
198
|
+
}));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (options.attended !== true) {
|
|
202
|
+
throw new MillError("ATTENDANCE_REQUIRED", "Adoption apply requires attended local branch authority.", ExitCode.configuration);
|
|
203
|
+
}
|
|
204
|
+
const result = await applyAdoptionIntegration({
|
|
205
|
+
...common,
|
|
206
|
+
attended: true,
|
|
207
|
+
planApprovalDigest: requiredValue(options.approvePlan, "--approve-plan"),
|
|
208
|
+
});
|
|
209
|
+
emit(io, global.json === true, commandResult({ command: "adopt.apply", ok: true, data: result }));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const report = await scanRepository(root);
|
|
213
|
+
const blocked = report.gitConfigHazards.length > 0 ||
|
|
214
|
+
report.truncatedDirectories.length > 0 ||
|
|
215
|
+
report.symlinksSkipped.length > 0 ||
|
|
216
|
+
report.secretReferences.length > 0;
|
|
217
|
+
const reasons = [
|
|
218
|
+
...(report.gitConfigHazards.length > 0
|
|
219
|
+
? [
|
|
220
|
+
{
|
|
221
|
+
code: "UNSAFE_GIT_CONFIGURATION",
|
|
222
|
+
message: "Repository Git configuration requires human disposition.",
|
|
223
|
+
},
|
|
224
|
+
]
|
|
225
|
+
: []),
|
|
226
|
+
...(report.truncatedDirectories.length > 0
|
|
227
|
+
? [
|
|
228
|
+
{
|
|
229
|
+
code: "SCAN_INCOMPLETE",
|
|
230
|
+
message: "Repository scan reached its depth limit and is incomplete.",
|
|
231
|
+
},
|
|
232
|
+
]
|
|
233
|
+
: []),
|
|
234
|
+
...(report.symlinksSkipped.length > 0
|
|
235
|
+
? [
|
|
236
|
+
{
|
|
237
|
+
code: "SYMLINKS_UNSUPPORTED",
|
|
238
|
+
message: "Repository symbolic links require human disposition before adoption.",
|
|
239
|
+
},
|
|
240
|
+
]
|
|
241
|
+
: []),
|
|
242
|
+
...(report.secretReferences.length > 0
|
|
243
|
+
? [
|
|
244
|
+
{
|
|
245
|
+
code: "SENSITIVE_PATHS_PRESENT",
|
|
246
|
+
message: "Repository credential-like files require human disposition before adoption.",
|
|
247
|
+
},
|
|
248
|
+
]
|
|
249
|
+
: []),
|
|
250
|
+
];
|
|
251
|
+
emit(io, global.json === true, commandResult({
|
|
252
|
+
command: "adopt.scan",
|
|
253
|
+
ok: !blocked,
|
|
254
|
+
status: blocked ? "blocked" : "ok",
|
|
255
|
+
data: report,
|
|
256
|
+
reasons,
|
|
257
|
+
}));
|
|
258
|
+
if (blocked) {
|
|
259
|
+
throw new MillError(reasons[0]?.code ?? "SCAN_INCOMPLETE", reasons[0]?.message ?? "Repository scan is incomplete.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
const dependencies = program
|
|
263
|
+
.command("dependencies")
|
|
264
|
+
.description("manage exact local verifier dependency snapshots");
|
|
265
|
+
dependencies
|
|
266
|
+
.command("prepare")
|
|
267
|
+
.description("prepare one lock-bound snapshot through the exact OCI image")
|
|
268
|
+
.requiredOption("--attended", "approve disclosed npm registry network access")
|
|
269
|
+
.action(async () => {
|
|
270
|
+
const global = globals(program);
|
|
271
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
272
|
+
await enforceExactVersion(root);
|
|
273
|
+
const result = await prepareRepositoryDependencies(root, true);
|
|
274
|
+
emit(io, global.json === true, commandResult({
|
|
275
|
+
command: "dependencies.prepare",
|
|
276
|
+
ok: true,
|
|
277
|
+
data: result ?? { configured: false },
|
|
278
|
+
}));
|
|
279
|
+
});
|
|
280
|
+
const detach = program
|
|
281
|
+
.command("detach")
|
|
282
|
+
.description("inspect manual Mill detachment");
|
|
283
|
+
detach
|
|
284
|
+
.command("plan")
|
|
285
|
+
.description("report Mill-only removals and downstream-owned retained files")
|
|
286
|
+
.action(async () => {
|
|
287
|
+
const global = globals(program);
|
|
288
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
289
|
+
await enforceExactVersion(root);
|
|
290
|
+
emit(io, global.json === true, commandResult({
|
|
291
|
+
command: "detach.plan",
|
|
292
|
+
ok: true,
|
|
293
|
+
data: await planDetach(root),
|
|
294
|
+
}));
|
|
295
|
+
});
|
|
296
|
+
program
|
|
297
|
+
.command("validate-contract")
|
|
298
|
+
.description("validate one compact Mill YAML or JSON contract")
|
|
299
|
+
.requiredOption("--kind <kind>", "contract kind")
|
|
300
|
+
.requiredOption("--file <path>", "contract path inside the selected root")
|
|
301
|
+
.action(async (options) => {
|
|
302
|
+
const global = globals(program);
|
|
303
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
304
|
+
await enforceExactVersion(root);
|
|
305
|
+
if (!Object.hasOwn(contractSchemas, options.kind)) {
|
|
306
|
+
throw new MillError("UNKNOWN_CONTRACT_KIND", `Unknown contract kind: ${options.kind}`, ExitCode.usage, { supported: Object.keys(contractSchemas) });
|
|
307
|
+
}
|
|
308
|
+
const source = await safeReadText(root, options.file);
|
|
309
|
+
const extension = path.extname(options.file).toLowerCase();
|
|
310
|
+
let raw;
|
|
311
|
+
try {
|
|
312
|
+
raw = extension === ".json" ? JSON.parse(source) : parseYaml(source);
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
throw new MillError("INVALID_CONTRACT", `Contract syntax is invalid: ${String(error)}`, ExitCode.data);
|
|
316
|
+
}
|
|
317
|
+
const parsed = contractSchemas[options.kind].safeParse(raw);
|
|
318
|
+
if (!parsed.success) {
|
|
319
|
+
throw new MillError("INVALID_CONTRACT", "Contract validation failed.", ExitCode.data, { issues: parsed.error.issues });
|
|
320
|
+
}
|
|
321
|
+
emit(io, global.json === true, commandResult({
|
|
322
|
+
command: "validate-contract",
|
|
323
|
+
ok: true,
|
|
324
|
+
data: { kind: options.kind, file: options.file },
|
|
325
|
+
}));
|
|
326
|
+
});
|
|
327
|
+
const plan = program
|
|
328
|
+
.command("plan")
|
|
329
|
+
.description("compile and assess product-continuity authority without writes");
|
|
330
|
+
plan
|
|
331
|
+
.command("specification")
|
|
332
|
+
.description("assess one source-backed product proposal and approval digest")
|
|
333
|
+
.requiredOption("--prd <path>", "PRD path inside the selected root")
|
|
334
|
+
.requiredOption("--sources <path>", "source manifest path inside the selected root")
|
|
335
|
+
.requiredOption("--proposal <path>", "specification proposal path")
|
|
336
|
+
.action(async (options) => {
|
|
337
|
+
const global = globals(program);
|
|
338
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
339
|
+
await enforceExactVersion(root);
|
|
340
|
+
const [planning, proposal] = await Promise.all([
|
|
341
|
+
loadPlanningSources({
|
|
342
|
+
root,
|
|
343
|
+
prdPath: options.prd,
|
|
344
|
+
sourceManifestPath: options.sources,
|
|
345
|
+
}),
|
|
346
|
+
loadSpecificationProposal(root, options.proposal),
|
|
347
|
+
]);
|
|
348
|
+
const assessment = assessSpecificationProposal({
|
|
349
|
+
proposal,
|
|
350
|
+
prdPath: planning.prdPath,
|
|
351
|
+
prdDigest: planning.prdDigest,
|
|
352
|
+
sourceManifest: planning.sourceManifest,
|
|
353
|
+
sourceManifestDigest: planning.sourceManifestDigest,
|
|
354
|
+
});
|
|
355
|
+
emit(io, global.json === true, commandResult({
|
|
356
|
+
command: "plan.specification",
|
|
357
|
+
ok: assessment.promotable,
|
|
358
|
+
status: assessment.promotable ? "ok" : "blocked",
|
|
359
|
+
data: assessment,
|
|
360
|
+
reasons: assessment.blockers.map((message) => ({
|
|
361
|
+
code: "SPECIFICATION_PROMOTION_BLOCKED",
|
|
362
|
+
message,
|
|
363
|
+
})),
|
|
364
|
+
}));
|
|
365
|
+
if (!assessment.promotable) {
|
|
366
|
+
throw new MillError("SPECIFICATION_PROMOTION_BLOCKED", "The specification proposal is not promotable.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
plan
|
|
370
|
+
.command("promote")
|
|
371
|
+
.description("return exact frozen artifacts for one explicitly approved proposal")
|
|
372
|
+
.requiredOption("--prd <path>", "PRD path inside the selected root")
|
|
373
|
+
.requiredOption("--sources <path>", "source manifest path")
|
|
374
|
+
.requiredOption("--proposal <path>", "specification proposal path")
|
|
375
|
+
.requiredOption("--approve <digest>", "exact proposal digest")
|
|
376
|
+
.action(async (options) => {
|
|
377
|
+
const global = globals(program);
|
|
378
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
379
|
+
await enforceExactVersion(root);
|
|
380
|
+
const [planning, proposal] = await Promise.all([
|
|
381
|
+
loadPlanningSources({
|
|
382
|
+
root,
|
|
383
|
+
prdPath: options.prd,
|
|
384
|
+
sourceManifestPath: options.sources,
|
|
385
|
+
}),
|
|
386
|
+
loadSpecificationProposal(root, options.proposal),
|
|
387
|
+
]);
|
|
388
|
+
const assessment = assessSpecificationProposal({
|
|
389
|
+
proposal,
|
|
390
|
+
prdPath: planning.prdPath,
|
|
391
|
+
prdDigest: planning.prdDigest,
|
|
392
|
+
sourceManifest: planning.sourceManifest,
|
|
393
|
+
sourceManifestDigest: planning.sourceManifestDigest,
|
|
394
|
+
});
|
|
395
|
+
const promoted = promoteSpecificationProposal({
|
|
396
|
+
proposal,
|
|
397
|
+
approvalDigest: options.approve,
|
|
398
|
+
assessment,
|
|
399
|
+
});
|
|
400
|
+
emit(io, global.json === true, commandResult({
|
|
401
|
+
command: "plan.promote",
|
|
402
|
+
ok: true,
|
|
403
|
+
data: promoted,
|
|
404
|
+
}));
|
|
405
|
+
});
|
|
406
|
+
plan
|
|
407
|
+
.command("diff")
|
|
408
|
+
.description("compare a regenerated proposal without replacing approval")
|
|
409
|
+
.requiredOption("--approved <path>", "approved proposal path")
|
|
410
|
+
.requiredOption("--proposal <path>", "regenerated proposal path")
|
|
411
|
+
.action(async (options) => {
|
|
412
|
+
const global = globals(program);
|
|
413
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
414
|
+
await enforceExactVersion(root);
|
|
415
|
+
const [approved, proposal] = await Promise.all([
|
|
416
|
+
loadSpecificationProposal(root, options.approved),
|
|
417
|
+
loadSpecificationProposal(root, options.proposal),
|
|
418
|
+
]);
|
|
419
|
+
emit(io, global.json === true, commandResult({
|
|
420
|
+
command: "plan.diff",
|
|
421
|
+
ok: true,
|
|
422
|
+
data: { changedPaths: semanticProposalDiff(approved, proposal) },
|
|
423
|
+
}));
|
|
424
|
+
});
|
|
425
|
+
plan
|
|
426
|
+
.command("impact")
|
|
427
|
+
.description("assess one exact approved JIT impact manifest")
|
|
428
|
+
.requiredOption("--product <path>", "product contract path")
|
|
429
|
+
.requiredOption("--scenarios <path>", "scenario set path")
|
|
430
|
+
.requiredOption("--manifest <path>", "impact manifest path")
|
|
431
|
+
.action(async (options) => {
|
|
432
|
+
const global = globals(program);
|
|
433
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
434
|
+
await enforceExactVersion(root);
|
|
435
|
+
const inputs = await loadImpactPlanningInputs({
|
|
436
|
+
root,
|
|
437
|
+
productPath: options.product,
|
|
438
|
+
scenarioPath: options.scenarios,
|
|
439
|
+
impactPath: options.manifest,
|
|
440
|
+
});
|
|
441
|
+
const assessment = assessImpactManifest(inputs);
|
|
442
|
+
emit(io, global.json === true, commandResult({
|
|
443
|
+
command: "plan.impact",
|
|
444
|
+
ok: assessment.approved,
|
|
445
|
+
status: assessment.approved ? "ok" : "blocked",
|
|
446
|
+
data: assessment,
|
|
447
|
+
reasons: assessment.blockers.map((message) => ({
|
|
448
|
+
code: "IMPACT_PROMOTION_BLOCKED",
|
|
449
|
+
message,
|
|
450
|
+
})),
|
|
451
|
+
}));
|
|
452
|
+
if (!assessment.approved) {
|
|
453
|
+
throw new MillError("IMPACT_PROMOTION_BLOCKED", "The impact manifest is not approved for execution.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
const auth = program
|
|
457
|
+
.command("auth")
|
|
458
|
+
.description("inspect adapter authentication readiness");
|
|
459
|
+
auth
|
|
460
|
+
.command("status")
|
|
461
|
+
.description("report operator-owned Codex authentication readiness")
|
|
462
|
+
.action(async () => {
|
|
463
|
+
const global = globals(program);
|
|
464
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
465
|
+
await enforceExactVersion(root);
|
|
466
|
+
const status = await codexAuthStatus(root);
|
|
467
|
+
emit(io, global.json === true, commandResult({
|
|
468
|
+
command: "auth.status",
|
|
469
|
+
ok: status.available,
|
|
470
|
+
data: status,
|
|
471
|
+
}));
|
|
472
|
+
if (!status.available) {
|
|
473
|
+
throw new MillError("CODEX_AUTH_UNAVAILABLE", "The operator's Codex CLI is not logged in.", ExitCode.unavailable, { resultAlreadyEmitted: true });
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
const qualify = program
|
|
477
|
+
.command("qualify")
|
|
478
|
+
.description("qualify declared commands in a disposable exact-base worktree")
|
|
479
|
+
.option("--baseline", "qualify the pre-change base")
|
|
480
|
+
.option("--task <path>", "approved task packet path")
|
|
481
|
+
.action(async (options) => {
|
|
482
|
+
const global = globals(program);
|
|
483
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
484
|
+
await enforceExactVersion(root);
|
|
485
|
+
if (options.baseline !== true) {
|
|
486
|
+
throw new MillError("USAGE_ERROR", "Baseline qualification requires --baseline.", ExitCode.usage);
|
|
487
|
+
}
|
|
488
|
+
const result = await qualifyBaseline({
|
|
489
|
+
root,
|
|
490
|
+
taskPath: requiredValue(options.task, "--task"),
|
|
491
|
+
});
|
|
492
|
+
const { evidence } = result;
|
|
493
|
+
emit(io, global.json === true, commandResult({
|
|
494
|
+
command: "qualify.baseline",
|
|
495
|
+
ok: evidence.passed,
|
|
496
|
+
status: evidence.passed ? "ok" : "blocked",
|
|
497
|
+
data: result,
|
|
498
|
+
reasons: evidence.passed
|
|
499
|
+
? []
|
|
500
|
+
: [
|
|
501
|
+
{
|
|
502
|
+
code: "BASELINE_QUALIFICATION_FAILED",
|
|
503
|
+
message: "A required baseline command failed or was blocked.",
|
|
504
|
+
},
|
|
505
|
+
],
|
|
506
|
+
}));
|
|
507
|
+
if (!evidence.passed) {
|
|
508
|
+
throw new MillError("BASELINE_QUALIFICATION_FAILED", "A required baseline command failed or was blocked.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
qualify
|
|
512
|
+
.command("public-alpha")
|
|
513
|
+
.description("assess one exact longitudinal and clean-room qualification record")
|
|
514
|
+
.requiredOption("--file <path>", "qualification record path")
|
|
515
|
+
.action(async (options) => {
|
|
516
|
+
const global = globals(program);
|
|
517
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
518
|
+
await enforceExactVersion(root);
|
|
519
|
+
const assessment = await loadPublicAlphaQualification({
|
|
520
|
+
root,
|
|
521
|
+
file: options.file,
|
|
522
|
+
});
|
|
523
|
+
emit(io, global.json === true, commandResult({
|
|
524
|
+
command: "qualify.public-alpha",
|
|
525
|
+
ok: assessment.passed,
|
|
526
|
+
status: assessment.passed ? "ok" : "blocked",
|
|
527
|
+
data: assessment,
|
|
528
|
+
reasons: assessment.blockers.map((message) => ({
|
|
529
|
+
code: "PUBLIC_ALPHA_QUALIFICATION_BLOCKED",
|
|
530
|
+
message,
|
|
531
|
+
})),
|
|
532
|
+
}));
|
|
533
|
+
if (!assessment.passed) {
|
|
534
|
+
throw new MillError("PUBLIC_ALPHA_QUALIFICATION_BLOCKED", "The public-alpha qualification record is not promotable.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
program
|
|
538
|
+
.command("audit")
|
|
539
|
+
.description("run the bounded read-only public-alpha repository audit")
|
|
540
|
+
.action(async () => {
|
|
541
|
+
const global = globals(program);
|
|
542
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
543
|
+
await enforceExactVersion(root);
|
|
544
|
+
const report = await auditRepository({ root });
|
|
545
|
+
const ok = report.status === "passed";
|
|
546
|
+
emit(io, global.json === true, commandResult({
|
|
547
|
+
command: "audit",
|
|
548
|
+
ok,
|
|
549
|
+
status: ok ? "ok" : "blocked",
|
|
550
|
+
data: report,
|
|
551
|
+
reasons: report.checks
|
|
552
|
+
.filter((check) => check.status === "blocked")
|
|
553
|
+
.map((check) => ({
|
|
554
|
+
code: "AUDIT_CHECK_BLOCKED",
|
|
555
|
+
message: check.summary,
|
|
556
|
+
details: { category: check.category, checkId: check.id },
|
|
557
|
+
})),
|
|
558
|
+
}));
|
|
559
|
+
if (!ok) {
|
|
560
|
+
throw new MillError("AUDIT_BLOCKED", "One or more bounded audit categories are blocked.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
const run = program
|
|
564
|
+
.command("run")
|
|
565
|
+
.description("build one explicitly approved task in an isolated local worktree")
|
|
566
|
+
.option("--task <path>", "approved task packet path")
|
|
567
|
+
.option("--approve <digest>", "approval digest from successful matching baseline qualification")
|
|
568
|
+
.option("--attended", "acknowledge attended trusted-host Codex execution")
|
|
569
|
+
.action(async (options) => {
|
|
570
|
+
const global = globals(program);
|
|
571
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
572
|
+
await enforceExactVersion(root);
|
|
573
|
+
if (options.attended !== true) {
|
|
574
|
+
throw new MillError("ATTENDANCE_REQUIRED", "Run requires attended trusted-host Codex authority.", ExitCode.configuration);
|
|
575
|
+
}
|
|
576
|
+
const result = await startLocalRun({
|
|
577
|
+
root,
|
|
578
|
+
taskPath: requiredValue(options.task, "--task"),
|
|
579
|
+
approvalDigest: requiredValue(options.approve, "--approve"),
|
|
580
|
+
});
|
|
581
|
+
emit(io, global.json === true, commandResult({ command: "run", ok: true, data: result }));
|
|
582
|
+
});
|
|
583
|
+
run
|
|
584
|
+
.command("next")
|
|
585
|
+
.description("run the one ready approved outcome through the existing lifecycle")
|
|
586
|
+
.requiredOption("--approve <digest>", "approval digest from successful matching baseline qualification")
|
|
587
|
+
.requiredOption("--attended", "acknowledge attended trusted-host execution")
|
|
588
|
+
.action(async (options) => {
|
|
589
|
+
const global = globals(program);
|
|
590
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
591
|
+
await enforceExactVersion(root);
|
|
592
|
+
const started = await startNextReadyOutcome({
|
|
593
|
+
root,
|
|
594
|
+
approvalDigest: options.approve,
|
|
595
|
+
});
|
|
596
|
+
emit(io, global.json === true, commandResult({
|
|
597
|
+
command: "run.next",
|
|
598
|
+
ok: true,
|
|
599
|
+
data: { outcome: started.outcome, ...started.result },
|
|
600
|
+
}));
|
|
601
|
+
});
|
|
602
|
+
program
|
|
603
|
+
.command("start")
|
|
604
|
+
.description("advance the one ready outcome through the resumable founder path")
|
|
605
|
+
.requiredOption("--prd <path>", "approved repo-native PRD path")
|
|
606
|
+
.option("--repo <path>", "managed repository path")
|
|
607
|
+
.option("--draft-pr", "prepare the later exact reviewed candidate's draft-PR plan")
|
|
608
|
+
.requiredOption("--attended", "approve attended dependency, model, and scoped forge use")
|
|
609
|
+
.action(async (options) => {
|
|
610
|
+
const global = globals(program);
|
|
611
|
+
const root = await findRepositoryRoot(options.repo ?? global.cwd);
|
|
612
|
+
await enforceExactVersion(root);
|
|
613
|
+
const inspectedPrd = path
|
|
614
|
+
.normalize(options.prd)
|
|
615
|
+
.split(path.sep)
|
|
616
|
+
.join("/");
|
|
617
|
+
await safeReadText(root, inspectedPrd, 2 * 1024 * 1024);
|
|
618
|
+
const result = await startFounderDelivery({
|
|
619
|
+
root,
|
|
620
|
+
prdPath: inspectedPrd,
|
|
621
|
+
attended: true,
|
|
622
|
+
draftPr: options.draftPr === true,
|
|
623
|
+
});
|
|
624
|
+
emit(io, global.json === true, commandResult({ command: "start", ok: true, data: result }));
|
|
625
|
+
});
|
|
626
|
+
program
|
|
627
|
+
.command("status")
|
|
628
|
+
.description("report durable local run state")
|
|
629
|
+
.option("--run <id>", "run identifier")
|
|
630
|
+
.action(async (options) => {
|
|
631
|
+
const global = globals(program);
|
|
632
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
633
|
+
await enforceExactVersion(root);
|
|
634
|
+
const data = await runStatus({
|
|
635
|
+
root,
|
|
636
|
+
...(options.run === undefined ? {} : { runId: options.run }),
|
|
637
|
+
});
|
|
638
|
+
emit(io, global.json === true, commandResult({ command: "status", ok: true, data }));
|
|
639
|
+
});
|
|
640
|
+
program
|
|
641
|
+
.command("verify")
|
|
642
|
+
.description("validate an exact committed candidate through declared commands")
|
|
643
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
644
|
+
.requiredOption("--run <id>", "run identifier")
|
|
645
|
+
.action(async (options) => {
|
|
646
|
+
const global = globals(program);
|
|
647
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
648
|
+
await enforceExactVersion(root);
|
|
649
|
+
const result = await verifyRun({
|
|
650
|
+
root,
|
|
651
|
+
taskPath: options.task,
|
|
652
|
+
runId: options.run,
|
|
653
|
+
});
|
|
654
|
+
emit(io, global.json === true, commandResult({
|
|
655
|
+
command: "verify",
|
|
656
|
+
ok: result.evidence.passed,
|
|
657
|
+
status: result.evidence.passed ? "ok" : "blocked",
|
|
658
|
+
data: result,
|
|
659
|
+
reasons: result.evidence.passed
|
|
660
|
+
? []
|
|
661
|
+
: [
|
|
662
|
+
{
|
|
663
|
+
code: "VALIDATION_FAILED",
|
|
664
|
+
message: "A required command failed or was blocked.",
|
|
665
|
+
},
|
|
666
|
+
],
|
|
667
|
+
}));
|
|
668
|
+
if (!result.evidence.passed) {
|
|
669
|
+
throw new MillError("VALIDATION_FAILED", "A required command failed or was blocked.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
program
|
|
673
|
+
.command("review")
|
|
674
|
+
.description("obtain a fresh read-only review of the exact verified candidate")
|
|
675
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
676
|
+
.requiredOption("--run <id>", "run identifier")
|
|
677
|
+
.action(async (options) => {
|
|
678
|
+
const global = globals(program);
|
|
679
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
680
|
+
await enforceExactVersion(root);
|
|
681
|
+
const result = await reviewRun({
|
|
682
|
+
root,
|
|
683
|
+
taskPath: options.task,
|
|
684
|
+
runId: options.run,
|
|
685
|
+
});
|
|
686
|
+
const ok = result.review.findings.length === 0;
|
|
687
|
+
emit(io, global.json === true, commandResult({
|
|
688
|
+
command: "review",
|
|
689
|
+
ok,
|
|
690
|
+
status: ok ? "ok" : "blocked",
|
|
691
|
+
data: result,
|
|
692
|
+
reasons: ok
|
|
693
|
+
? []
|
|
694
|
+
: [
|
|
695
|
+
{
|
|
696
|
+
code: result.run.blockCode ?? "REVIEW_FINDINGS",
|
|
697
|
+
message: "The exact-candidate review reported actionable findings.",
|
|
698
|
+
},
|
|
699
|
+
],
|
|
700
|
+
}));
|
|
701
|
+
if (!ok) {
|
|
702
|
+
throw new MillError(result.run.blockCode ?? "REVIEW_FINDINGS", "The exact-candidate review reported actionable findings.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
program
|
|
706
|
+
.command("resume")
|
|
707
|
+
.description("resume one safe blocked checkpoint within its retry budget")
|
|
708
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
709
|
+
.requiredOption("--run <id>", "run identifier")
|
|
710
|
+
.action(async (options) => {
|
|
711
|
+
const global = globals(program);
|
|
712
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
713
|
+
await enforceExactVersion(root);
|
|
714
|
+
const run = await resumeRun({
|
|
715
|
+
root,
|
|
716
|
+
taskPath: options.task,
|
|
717
|
+
runId: options.run,
|
|
718
|
+
});
|
|
719
|
+
emit(io, global.json === true, commandResult({ command: "resume", ok: true, data: { run } }));
|
|
720
|
+
});
|
|
721
|
+
const pr = program
|
|
722
|
+
.command("pr")
|
|
723
|
+
.description("deliver an exact reviewed candidate through a draft PR");
|
|
724
|
+
pr.command("plan")
|
|
725
|
+
.description("read live GitHub identity and create a local approval plan")
|
|
726
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
727
|
+
.requiredOption("--run <id>", "run identifier")
|
|
728
|
+
.action(async (options) => {
|
|
729
|
+
const global = globals(program);
|
|
730
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
731
|
+
await enforceExactVersion(root);
|
|
732
|
+
const result = await planDraftPr({
|
|
733
|
+
root,
|
|
734
|
+
taskPath: options.task,
|
|
735
|
+
runId: options.run,
|
|
736
|
+
});
|
|
737
|
+
emit(io, global.json === true, commandResult({ command: "pr.plan", ok: true, data: result }));
|
|
738
|
+
});
|
|
739
|
+
pr.command("open")
|
|
740
|
+
.description("push and open the explicitly approved draft PR")
|
|
741
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
742
|
+
.requiredOption("--run <id>", "run identifier")
|
|
743
|
+
.requiredOption("--approve <digest>", "exact, unexpired proposal digest returned by pr plan")
|
|
744
|
+
.requiredOption("--attended", "acknowledge attended use of the operator-owned GitHub session")
|
|
745
|
+
.action(async (options) => {
|
|
746
|
+
const global = globals(program);
|
|
747
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
748
|
+
await enforceExactVersion(root);
|
|
749
|
+
const result = await openDraftPr({
|
|
750
|
+
root,
|
|
751
|
+
taskPath: options.task,
|
|
752
|
+
runId: options.run,
|
|
753
|
+
approvalDigest: options.approve,
|
|
754
|
+
attended: options.attended,
|
|
755
|
+
});
|
|
756
|
+
emit(io, global.json === true, commandResult({ command: "pr.open", ok: true, data: result }));
|
|
757
|
+
});
|
|
758
|
+
pr.command("reconcile")
|
|
759
|
+
.description("classify one unknown GitHub effect through readback only")
|
|
760
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
761
|
+
.requiredOption("--run <id>", "run identifier")
|
|
762
|
+
.action(async (options) => {
|
|
763
|
+
const global = globals(program);
|
|
764
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
765
|
+
await enforceExactVersion(root);
|
|
766
|
+
const result = await reconcileDraftPr({
|
|
767
|
+
root,
|
|
768
|
+
taskPath: options.task,
|
|
769
|
+
runId: options.run,
|
|
770
|
+
});
|
|
771
|
+
emit(io, global.json === true, commandResult({ command: "pr.reconcile", ok: true, data: result }));
|
|
772
|
+
});
|
|
773
|
+
pr.command("observe")
|
|
774
|
+
.description("read exact-head checks and configured GitHub review policy")
|
|
775
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
776
|
+
.requiredOption("--run <id>", "run identifier")
|
|
777
|
+
.action(async (options) => {
|
|
778
|
+
const global = globals(program);
|
|
779
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
780
|
+
await enforceExactVersion(root);
|
|
781
|
+
const result = await observeDraftPr({
|
|
782
|
+
root,
|
|
783
|
+
taskPath: options.task,
|
|
784
|
+
runId: options.run,
|
|
785
|
+
});
|
|
786
|
+
const blocked = result.run.status === "blocked";
|
|
787
|
+
emit(io, global.json === true, commandResult({
|
|
788
|
+
command: "pr.observe",
|
|
789
|
+
ok: !blocked,
|
|
790
|
+
status: blocked ? "blocked" : "ok",
|
|
791
|
+
data: result,
|
|
792
|
+
reasons: blocked
|
|
793
|
+
? [
|
|
794
|
+
{
|
|
795
|
+
code: result.run.blockCode ?? "REMOTE_POLICY_BLOCKED",
|
|
796
|
+
message: "GitHub checks or configured review policy blocked this exact candidate.",
|
|
797
|
+
},
|
|
798
|
+
]
|
|
799
|
+
: [],
|
|
800
|
+
}));
|
|
801
|
+
if (blocked) {
|
|
802
|
+
throw new MillError(result.run.blockCode ?? "REMOTE_POLICY_BLOCKED", "GitHub policy blocked this exact candidate.", ExitCode.configuration, { resultAlreadyEmitted: true });
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
pr.command("finalize")
|
|
806
|
+
.description("read back human merge and exact main checks before closure")
|
|
807
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
808
|
+
.requiredOption("--run <id>", "run identifier")
|
|
809
|
+
.action(async (options) => {
|
|
810
|
+
const global = globals(program);
|
|
811
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
812
|
+
await enforceExactVersion(root);
|
|
813
|
+
const result = await finalizeDraftPr({
|
|
814
|
+
root,
|
|
815
|
+
taskPath: options.task,
|
|
816
|
+
runId: options.run,
|
|
817
|
+
});
|
|
818
|
+
const closed = result.run.status === "closed";
|
|
819
|
+
const reasonCode = result.run.status === "blocked"
|
|
820
|
+
? (result.run.blockCode ?? "POST_MERGE_CHECKS_FAILED")
|
|
821
|
+
: "POST_MERGE_CHECKS_PENDING";
|
|
822
|
+
emit(io, global.json === true, commandResult({
|
|
823
|
+
command: "pr.finalize",
|
|
824
|
+
ok: closed,
|
|
825
|
+
status: closed ? "ok" : "blocked",
|
|
826
|
+
data: result,
|
|
827
|
+
reasons: closed
|
|
828
|
+
? []
|
|
829
|
+
: [
|
|
830
|
+
{
|
|
831
|
+
code: reasonCode,
|
|
832
|
+
message: "Post-merge required checks have not produced passing exact-commit evidence.",
|
|
833
|
+
},
|
|
834
|
+
],
|
|
835
|
+
}));
|
|
836
|
+
if (!closed) {
|
|
837
|
+
throw new MillError(reasonCode, "Post-merge verification is not complete.", result.run.status === "blocked"
|
|
838
|
+
? ExitCode.configuration
|
|
839
|
+
: ExitCode.temporary, { resultAlreadyEmitted: true });
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
program
|
|
843
|
+
.command("ship")
|
|
844
|
+
.description("plan or open one draft PR through the existing delivery state machine")
|
|
845
|
+
.requiredOption("--draft", "limit shipping to one draft pull request")
|
|
846
|
+
.requiredOption("--task <path>", "approved task packet path")
|
|
847
|
+
.requiredOption("--run <id>", "reviewed run identifier")
|
|
848
|
+
.option("--approve <digest>", "exact unexpired digest returned by this wrapper")
|
|
849
|
+
.option("--attended", "acknowledge attended GitHub authority")
|
|
850
|
+
.action(async (options) => {
|
|
851
|
+
const global = globals(program);
|
|
852
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
853
|
+
await enforceExactVersion(root);
|
|
854
|
+
if (options.approve === undefined) {
|
|
855
|
+
const result = await planDraftPr({
|
|
856
|
+
root,
|
|
857
|
+
taskPath: options.task,
|
|
858
|
+
runId: options.run,
|
|
859
|
+
});
|
|
860
|
+
emit(io, global.json === true, commandResult({
|
|
861
|
+
command: "ship.draft.plan",
|
|
862
|
+
ok: true,
|
|
863
|
+
data: result,
|
|
864
|
+
}));
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (options.attended !== true) {
|
|
868
|
+
throw new MillError("ATTENDANCE_REQUIRED", "Draft shipping requires attended use of the operator-owned GitHub session.", ExitCode.configuration);
|
|
869
|
+
}
|
|
870
|
+
const result = await openDraftPr({
|
|
871
|
+
root,
|
|
872
|
+
taskPath: options.task,
|
|
873
|
+
runId: options.run,
|
|
874
|
+
approvalDigest: options.approve,
|
|
875
|
+
attended: true,
|
|
876
|
+
});
|
|
877
|
+
emit(io, global.json === true, commandResult({ command: "ship.draft.open", ok: true, data: result }));
|
|
878
|
+
});
|
|
879
|
+
program
|
|
880
|
+
.command("cancel")
|
|
881
|
+
.description("persist cancellation for the exact foreground controller")
|
|
882
|
+
.requiredOption("--run <id>", "run identifier")
|
|
883
|
+
.action(async (options) => {
|
|
884
|
+
const global = globals(program);
|
|
885
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
886
|
+
await enforceExactVersion(root);
|
|
887
|
+
const run = await cancelRun({ root, runId: options.run });
|
|
888
|
+
emit(io, global.json === true, commandResult({ command: "cancel", ok: true, data: { run } }));
|
|
889
|
+
});
|
|
890
|
+
const state = program
|
|
891
|
+
.command("state")
|
|
892
|
+
.description("manage local operational state");
|
|
893
|
+
state
|
|
894
|
+
.command("backup")
|
|
895
|
+
.description("create a user-only SQLite backup")
|
|
896
|
+
.action(async () => {
|
|
897
|
+
const global = globals(program);
|
|
898
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
899
|
+
await enforceExactVersion(root);
|
|
900
|
+
const backupPath = await stateBackup({ root });
|
|
901
|
+
emit(io, global.json === true, commandResult({
|
|
902
|
+
command: "state.backup",
|
|
903
|
+
ok: true,
|
|
904
|
+
data: { backupPath },
|
|
905
|
+
}));
|
|
906
|
+
});
|
|
907
|
+
state
|
|
908
|
+
.command("restore")
|
|
909
|
+
.description("restore one Mill-owned state backup")
|
|
910
|
+
.requiredOption("--from <path>", "backup path returned by state backup")
|
|
911
|
+
.action(async (options) => {
|
|
912
|
+
const global = globals(program);
|
|
913
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
914
|
+
await enforceExactVersion(root);
|
|
915
|
+
const report = await stateRestore({ root, backupPath: options.from });
|
|
916
|
+
emit(io, global.json === true, commandResult({ command: "state.restore", ok: true, data: report }));
|
|
917
|
+
});
|
|
918
|
+
state
|
|
919
|
+
.command("purge")
|
|
920
|
+
.description("remove purge-safe local state and disposable worktrees")
|
|
921
|
+
.requiredOption("--confirm <repository-id>", "exact repository UUID acknowledgement")
|
|
922
|
+
.action(async (options) => {
|
|
923
|
+
const global = globals(program);
|
|
924
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
925
|
+
await enforceExactVersion(root);
|
|
926
|
+
await statePurge({ root, confirmation: options.confirm });
|
|
927
|
+
emit(io, global.json === true, commandResult({ command: "state.purge", ok: true, data: {} }));
|
|
928
|
+
});
|
|
929
|
+
program
|
|
930
|
+
.command("support-bundle")
|
|
931
|
+
.description("emit a redacted static support bundle")
|
|
932
|
+
.option("--run <id>", "run identifier")
|
|
933
|
+
.action(async (options) => {
|
|
934
|
+
const global = globals(program);
|
|
935
|
+
const root = await findRepositoryRoot(global.cwd);
|
|
936
|
+
await enforceExactVersion(root);
|
|
937
|
+
const data = await supportBundle({
|
|
938
|
+
root,
|
|
939
|
+
...(options.run === undefined ? {} : { runId: options.run }),
|
|
940
|
+
});
|
|
941
|
+
emit(io, global.json === true, commandResult({ command: "support-bundle", ok: true, data }));
|
|
942
|
+
});
|
|
943
|
+
return program;
|
|
944
|
+
}
|
|
945
|
+
export async function runCli(argv, io) {
|
|
946
|
+
const jsonRequested = argv.includes("--json");
|
|
947
|
+
if (jsonRequested && (argv.includes("--version") || argv.includes("-V"))) {
|
|
948
|
+
emit(io, true, commandResult({
|
|
949
|
+
command: "version",
|
|
950
|
+
ok: true,
|
|
951
|
+
data: { version: MILL_VERSION },
|
|
952
|
+
}));
|
|
953
|
+
return ExitCode.ok;
|
|
954
|
+
}
|
|
955
|
+
if (jsonRequested && (argv.includes("--help") || argv.includes("-h"))) {
|
|
956
|
+
emit(io, true, commandResult({
|
|
957
|
+
command: "millctl",
|
|
958
|
+
ok: false,
|
|
959
|
+
status: "error",
|
|
960
|
+
data: {},
|
|
961
|
+
reasons: [
|
|
962
|
+
{
|
|
963
|
+
code: "USAGE_ERROR",
|
|
964
|
+
message: "JSON mode does not support help output; omit --json.",
|
|
965
|
+
},
|
|
966
|
+
],
|
|
967
|
+
}));
|
|
968
|
+
return ExitCode.usage;
|
|
969
|
+
}
|
|
970
|
+
const program = createProgram(io, jsonRequested);
|
|
971
|
+
try {
|
|
972
|
+
await program.parseAsync(argv, { from: "user" });
|
|
973
|
+
return ExitCode.ok;
|
|
974
|
+
}
|
|
975
|
+
catch (error) {
|
|
976
|
+
if (error instanceof CommanderError) {
|
|
977
|
+
if (error.code === "commander.version" ||
|
|
978
|
+
error.code === "commander.helpDisplayed" ||
|
|
979
|
+
(!jsonRequested && error.code === "commander.help")) {
|
|
980
|
+
return ExitCode.ok;
|
|
981
|
+
}
|
|
982
|
+
if (jsonRequested) {
|
|
983
|
+
emit(io, true, commandResult({
|
|
984
|
+
command: program.args[0] ?? "millctl",
|
|
985
|
+
ok: false,
|
|
986
|
+
status: "error",
|
|
987
|
+
data: {},
|
|
988
|
+
reasons: [
|
|
989
|
+
{
|
|
990
|
+
code: "USAGE_ERROR",
|
|
991
|
+
message: error.message,
|
|
992
|
+
details: { commanderCode: error.code },
|
|
993
|
+
},
|
|
994
|
+
],
|
|
995
|
+
}));
|
|
996
|
+
}
|
|
997
|
+
return ExitCode.usage;
|
|
998
|
+
}
|
|
999
|
+
const millError = asMillError(error);
|
|
1000
|
+
if (millError.details.resultAlreadyEmitted !== true) {
|
|
1001
|
+
const global = program.opts();
|
|
1002
|
+
const result = commandResult({
|
|
1003
|
+
command: program.args[0] ?? "millctl",
|
|
1004
|
+
ok: false,
|
|
1005
|
+
status: "error",
|
|
1006
|
+
data: {},
|
|
1007
|
+
reasons: [
|
|
1008
|
+
{
|
|
1009
|
+
code: millError.code,
|
|
1010
|
+
message: millError.message,
|
|
1011
|
+
details: millError.details,
|
|
1012
|
+
},
|
|
1013
|
+
],
|
|
1014
|
+
});
|
|
1015
|
+
emit(io, global.json === true, result);
|
|
1016
|
+
}
|
|
1017
|
+
return millError.exitCode;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
//# sourceMappingURL=cli-program.js.map
|