@cassiomc1/forgeloop 0.1.15 → 1.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/DOCS_INDEX.md +60 -0
- package/EXECUTION_STATE.md +9 -0
- package/LOOP_ENGINEERING.md +15 -0
- package/LOOP_SYSTEM_DESIGN.md +8 -0
- package/PROTOCOL_INTEGRATION.md +8 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +183 -654
- package/TERMINOLOGY.md +4 -0
- package/THREAT_MODEL.md +9 -0
- package/docs/assets/forgeloop-flow.svg +1 -0
- package/docs/forgeloop-flow.mmd +51 -0
- package/package.json +16 -3
- package/schemas/continuity.schema.json +57 -0
- package/scripts/CI_VALIDATORS.md +32 -0
- package/src/cli.js +307 -204
- package/src/commands/clear-continuity.js +9 -0
- package/src/commands/continuity.js +25 -0
- package/src/commands/doctor.js +17 -2
- package/src/commands/reconcile-continuity.js +23 -0
- package/src/commands/record-continuity.js +63 -0
- package/src/commands/status.js +14 -1
- package/src/commands/update.js +12 -13
- package/src/commands/validate-protocol.js +19 -0
- package/src/core/artifacts.js +1 -0
- package/src/core/bundles.js +6 -0
- package/src/core/command-resolution.js +295 -0
- package/src/core/command-tokenizer.js +122 -0
- package/src/core/conformance.js +8 -2
- package/src/core/continuity-cli-options.js +58 -0
- package/src/core/continuity-conformance.js +46 -0
- package/src/core/continuity-observability.js +20 -0
- package/src/core/continuity-reconciliation.js +224 -0
- package/src/core/continuity.js +245 -0
- package/src/core/inspect.js +9 -1
- package/src/core/installation-authority.js +178 -0
- package/src/core/json-safety.js +17 -13
- package/src/core/next-action-artifacts.js +118 -0
- package/src/core/next-action-continuity.js +65 -0
- package/src/core/next-action-model.js +127 -0
- package/src/core/next-action-phases.js +12 -0
- package/src/core/next-action.js +22 -249
- package/src/core/npm-classifier.js +343 -0
- package/src/core/package-manager-classifiers.js +37 -0
- package/src/core/preflight-consistency.js +221 -0
- package/src/core/preflight-loaders.js +112 -0
- package/src/core/preflight-model.js +83 -0
- package/src/core/preflight.js +34 -444
- package/src/core/protocol.js +7 -0
- package/src/core/schema-validation.js +15 -1
- package/src/core/templates.js +2 -0
- package/src/core/verification-capability.js +31 -1106
- package/src/core/verification-constants.js +61 -0
package/src/cli.js
CHANGED
|
@@ -27,12 +27,51 @@ import { formatRecordCheckResult, runRecordCheck } from "./commands/record-check
|
|
|
27
27
|
import { formatRunCheckResult, runCheck } from "./commands/run-check.js";
|
|
28
28
|
import { formatRecordTerminalResult, runRecordTerminalResult } from "./commands/record-terminal-result.js";
|
|
29
29
|
import { formatNextActionResult, runNext } from "./commands/next.js";
|
|
30
|
+
import { formatContinuityResult, runContinuity } from "./commands/continuity.js";
|
|
31
|
+
import { formatRecordContinuityResult, runRecordContinuity } from "./commands/record-continuity.js";
|
|
32
|
+
import { formatReconcileContinuityResult, runReconcileContinuity } from "./commands/reconcile-continuity.js";
|
|
33
|
+
import { formatClearContinuityResult, runClearContinuity } from "./commands/clear-continuity.js";
|
|
34
|
+
import { continuityOptionDefaults, consumeContinuityOption, validateContinuityOptions } from "./core/continuity-cli-options.js";
|
|
30
35
|
import { resolveTarget } from "./core/filesystem.js";
|
|
31
36
|
import { getPackageRoot } from "./core/templates.js";
|
|
32
37
|
import { ARTIFACT_PATHS } from "./core/artifacts.js";
|
|
33
38
|
|
|
39
|
+
export const COMMANDS = Object.freeze([
|
|
40
|
+
"init",
|
|
41
|
+
"doctor",
|
|
42
|
+
"update",
|
|
43
|
+
"activate",
|
|
44
|
+
"route",
|
|
45
|
+
"preflight",
|
|
46
|
+
"advance",
|
|
47
|
+
"next",
|
|
48
|
+
"continuity",
|
|
49
|
+
"record-continuity",
|
|
50
|
+
"reconcile-continuity",
|
|
51
|
+
"clear-continuity",
|
|
52
|
+
"prepare-completion",
|
|
53
|
+
"run-check",
|
|
54
|
+
"record-check",
|
|
55
|
+
"record-terminal-result",
|
|
56
|
+
"complete",
|
|
57
|
+
"audit",
|
|
58
|
+
"report",
|
|
59
|
+
"policy",
|
|
60
|
+
"bundle",
|
|
61
|
+
"inspect",
|
|
62
|
+
"status",
|
|
63
|
+
"validate-state",
|
|
64
|
+
"clear-state",
|
|
65
|
+
"validate-receipt",
|
|
66
|
+
"validate-protocol",
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
// Commands whose usage blocks already describe --json (doctor, route) or that
|
|
70
|
+
// accept no --json at all (init, update).
|
|
71
|
+
const GENERIC_JSON_EXCLUDED_COMMANDS = new Set(["init", "doctor", "update", "route"]);
|
|
72
|
+
|
|
34
73
|
function usage(command = null) {
|
|
35
|
-
const commands = "
|
|
74
|
+
const commands = COMMANDS.join("|");
|
|
36
75
|
const options = [" --path <directory> target project directory (default: current directory)"];
|
|
37
76
|
if (!command || command === "init" || command === "update") {
|
|
38
77
|
options.push(" --dry-run show planned writes without changing files");
|
|
@@ -40,6 +79,7 @@ function usage(command = null) {
|
|
|
40
79
|
if (!command || command === "doctor") {
|
|
41
80
|
options.push(" --json emit doctor findings as JSON");
|
|
42
81
|
options.push(" --strict treat warnings as unhealthy");
|
|
82
|
+
options.push(" --fix restore missing managed template files");
|
|
43
83
|
options.push(" --adopt <path> preserve an existing adapter in the manifest");
|
|
44
84
|
}
|
|
45
85
|
if (!command || command === "route") {
|
|
@@ -54,7 +94,16 @@ function usage(command = null) {
|
|
|
54
94
|
if (!command || command === "advance") {
|
|
55
95
|
options.push(" --to <phase> destination workflow phase");
|
|
56
96
|
}
|
|
57
|
-
if (!command ||
|
|
97
|
+
if (!command || command === "record-continuity") {
|
|
98
|
+
options.push(" --focus-id <id> current implementation focus ID");
|
|
99
|
+
options.push(" --focus-summary <text> current implementation focus summary");
|
|
100
|
+
options.push(" --remaining <id:summary> remaining implementation item (repeatable)");
|
|
101
|
+
options.push(" --known-issue <id:summary> known implementation issue (repeatable)");
|
|
102
|
+
options.push(" --changed-area <path> changed project area (repeatable)");
|
|
103
|
+
options.push(" --inspect-first <path> suggested inspection path (repeatable)");
|
|
104
|
+
options.push(" --resume-note <text> bounded operational resume note");
|
|
105
|
+
}
|
|
106
|
+
if (!command || !GENERIC_JSON_EXCLUDED_COMMANDS.has(command)) {
|
|
58
107
|
options.push(" --json emit structured output as JSON");
|
|
59
108
|
}
|
|
60
109
|
if (!command || ["preflight", "complete", "audit", "report"].includes(command)) {
|
|
@@ -73,6 +122,7 @@ function usage(command = null) {
|
|
|
73
122
|
options.push(" --route-file <path> routing-result JSON relative to target");
|
|
74
123
|
options.push(" --state-file <path> work-state JSON relative to target");
|
|
75
124
|
options.push(" --receipt-file <path> execution-receipt JSON relative to target");
|
|
125
|
+
options.push(" --continuity-file <path> optional execution-continuity JSON relative to target");
|
|
76
126
|
options.push(" --task-brief-file <path> task brief JSON (repeatable)");
|
|
77
127
|
options.push(" --delegated-result-file <path> delegated result JSON (repeatable)");
|
|
78
128
|
}
|
|
@@ -117,6 +167,7 @@ export function parseArgs(argv) {
|
|
|
117
167
|
dryRun: false,
|
|
118
168
|
json: false,
|
|
119
169
|
strict: false,
|
|
170
|
+
fix: false,
|
|
120
171
|
adopt: [],
|
|
121
172
|
work: null,
|
|
122
173
|
surfaces: [],
|
|
@@ -130,6 +181,8 @@ export function parseArgs(argv) {
|
|
|
130
181
|
routeFile: null,
|
|
131
182
|
stateFile: null,
|
|
132
183
|
receiptFile: null,
|
|
184
|
+
continuityFile: null,
|
|
185
|
+
...continuityOptionDefaults(),
|
|
133
186
|
taskBriefFiles: [],
|
|
134
187
|
delegatedResultFiles: [],
|
|
135
188
|
checkId: null,
|
|
@@ -155,7 +208,7 @@ export function parseArgs(argv) {
|
|
|
155
208
|
|
|
156
209
|
for (let index = 0; index < argv.length; index += 1) {
|
|
157
210
|
const argument = argv[index];
|
|
158
|
-
if (
|
|
211
|
+
if (COMMANDS.includes(argument)) {
|
|
159
212
|
if (command) throw new Error(`Multiple commands are not supported: ${argument}`);
|
|
160
213
|
command = argument;
|
|
161
214
|
} else if (argument === "--help" || argument === "-h") {
|
|
@@ -166,6 +219,8 @@ export function parseArgs(argv) {
|
|
|
166
219
|
options.json = true;
|
|
167
220
|
} else if (argument === "--strict") {
|
|
168
221
|
options.strict = true;
|
|
222
|
+
} else if (argument === "--fix") {
|
|
223
|
+
options.fix = true;
|
|
169
224
|
} else if (argument === "--adopt") {
|
|
170
225
|
const relativePath = argv[index + 1];
|
|
171
226
|
if (!relativePath || relativePath.startsWith("-")) throw new Error("--adopt requires a path");
|
|
@@ -215,12 +270,13 @@ export function parseArgs(argv) {
|
|
|
215
270
|
if (!contractFile || contractFile.startsWith("-")) throw new Error("--contract-file requires a path");
|
|
216
271
|
options.contractFile = contractFile;
|
|
217
272
|
index += 1;
|
|
218
|
-
} else if (["--route-file", "--state-file", "--receipt-file"].includes(argument)) {
|
|
273
|
+
} else if (["--route-file", "--state-file", "--receipt-file", "--continuity-file"].includes(argument)) {
|
|
219
274
|
const file = argv[index + 1];
|
|
220
275
|
if (!file || file.startsWith("-")) throw new Error(`${argument} requires a path`);
|
|
221
276
|
if (argument === "--route-file") options.routeFile = file;
|
|
222
277
|
if (argument === "--state-file") options.stateFile = file;
|
|
223
278
|
if (argument === "--receipt-file") options.receiptFile = file;
|
|
279
|
+
if (argument === "--continuity-file") options.continuityFile = file;
|
|
224
280
|
index += 1;
|
|
225
281
|
} else if (argument === "--task-brief-file") {
|
|
226
282
|
const file = argv[index + 1];
|
|
@@ -328,6 +384,17 @@ export function parseArgs(argv) {
|
|
|
328
384
|
} else if (argument === "--" && command === "run-check") {
|
|
329
385
|
options.commandArgv = argv.slice(index + 1);
|
|
330
386
|
break;
|
|
387
|
+
} else if ([
|
|
388
|
+
"--focus-id",
|
|
389
|
+
"--focus-summary",
|
|
390
|
+
"--remaining",
|
|
391
|
+
"--known-issue",
|
|
392
|
+
"--changed-area",
|
|
393
|
+
"--inspect-first",
|
|
394
|
+
"--resume-note",
|
|
395
|
+
].includes(argument)) {
|
|
396
|
+
const consumed = consumeContinuityOption({ argument, argv, index, options });
|
|
397
|
+
index = consumed.index;
|
|
331
398
|
} else if (argument === "--path") {
|
|
332
399
|
options.path = argv[index + 1];
|
|
333
400
|
if (!options.path || options.path.startsWith("-")) throw new Error("--path requires a directory");
|
|
@@ -348,7 +415,7 @@ export function parseArgs(argv) {
|
|
|
348
415
|
|
|
349
416
|
if (!command) return { command: null, options };
|
|
350
417
|
|
|
351
|
-
const jsonCommands = ["doctor", "route", "activate", "advance", "next", "prepare-completion", "run-check", "record-check", "record-terminal-result", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
|
|
418
|
+
const jsonCommands = ["doctor", "route", "activate", "advance", "next", "continuity", "record-continuity", "reconcile-continuity", "clear-continuity", "prepare-completion", "run-check", "record-check", "record-terminal-result", "preflight", "complete", "audit", "report", "policy", "bundle", "inspect", "status", "validate-state", "clear-state", "validate-receipt", "validate-protocol"];
|
|
352
419
|
if (!jsonCommands.includes(command) && options.json) {
|
|
353
420
|
throw new Error(`Option --json is not valid for ${command}`);
|
|
354
421
|
}
|
|
@@ -364,6 +431,9 @@ export function parseArgs(argv) {
|
|
|
364
431
|
if (command !== "doctor" && options.adopt.length > 0) {
|
|
365
432
|
throw new Error(`Option --adopt is not valid for ${command}`);
|
|
366
433
|
}
|
|
434
|
+
if (command !== "doctor" && options.fix) {
|
|
435
|
+
throw new Error(`Option --fix is not valid for ${command}`);
|
|
436
|
+
}
|
|
367
437
|
if (command !== "route" && (options.work || options.surfaces.length || options.risks.length || options.platforms.length || options.behaviorChange || options.executableChange)) {
|
|
368
438
|
throw new Error(`Route options are not valid for ${command}`);
|
|
369
439
|
}
|
|
@@ -388,7 +458,7 @@ export function parseArgs(argv) {
|
|
|
388
458
|
if (!["status", "inspect", "validate-protocol"].includes(command) && options.contractFile) {
|
|
389
459
|
throw new Error(`Option --contract-file is not valid for ${command}`);
|
|
390
460
|
}
|
|
391
|
-
if (command !== "validate-protocol" && (options.routeFile || options.stateFile || options.receiptFile || options.taskBriefFiles.length > 0 || options.delegatedResultFiles.length > 0)) {
|
|
461
|
+
if (command !== "validate-protocol" && (options.routeFile || options.stateFile || options.receiptFile || options.continuityFile || options.taskBriefFiles.length > 0 || options.delegatedResultFiles.length > 0)) {
|
|
392
462
|
throw new Error(`Protocol artifact options are not valid for ${command}`);
|
|
393
463
|
}
|
|
394
464
|
const checkOptions = [
|
|
@@ -407,6 +477,7 @@ export function parseArgs(argv) {
|
|
|
407
477
|
if (!["record-check", "run-check"].includes(command) && checkOptions.some((value) => value !== null)) {
|
|
408
478
|
throw new Error(`Check recording options are not valid for ${command}`);
|
|
409
479
|
}
|
|
480
|
+
validateContinuityOptions(command, options);
|
|
410
481
|
if (command === "record-check" && !options.help) {
|
|
411
482
|
if (!options.checkId) throw new Error("record-check requires --id");
|
|
412
483
|
if (!options.checkRequirement) throw new Error("record-check requires --requirement");
|
|
@@ -442,6 +513,227 @@ function printActions(actions) {
|
|
|
442
513
|
}
|
|
443
514
|
}
|
|
444
515
|
|
|
516
|
+
export const COMMAND_HANDLERS = Object.freeze({
|
|
517
|
+
init: async ({ target, packageRoot, packageVersion, options }) => {
|
|
518
|
+
const result = await runInit({ target, dryRun: options.dryRun, packageRoot, packageVersion });
|
|
519
|
+
printActions(result.actions);
|
|
520
|
+
return 0;
|
|
521
|
+
},
|
|
522
|
+
doctor: async ({ target, packageRoot, options }) => {
|
|
523
|
+
const result = await runDoctor({
|
|
524
|
+
target,
|
|
525
|
+
packageRoot,
|
|
526
|
+
adoptPaths: options.adopt,
|
|
527
|
+
strict: options.strict,
|
|
528
|
+
fix: options.fix,
|
|
529
|
+
});
|
|
530
|
+
if (options.json) {
|
|
531
|
+
console.log(JSON.stringify(result, null, 2));
|
|
532
|
+
} else {
|
|
533
|
+
for (const item of result.findings) {
|
|
534
|
+
console.log(`${item.severity}: ${item.code}: ${item.path} - ${item.message}`);
|
|
535
|
+
}
|
|
536
|
+
console.log(result.ok ? "healthy: ForgeLoop target is ready" : "unhealthy: ForgeLoop target needs attention");
|
|
537
|
+
}
|
|
538
|
+
return result.ok ? 0 : 1;
|
|
539
|
+
},
|
|
540
|
+
route: async ({ target, packageRoot, options }) => {
|
|
541
|
+
const result = await runRoute({
|
|
542
|
+
target,
|
|
543
|
+
packageRoot,
|
|
544
|
+
workType: options.work,
|
|
545
|
+
surfaces: options.surfaces,
|
|
546
|
+
risks: options.risks,
|
|
547
|
+
platforms: options.platforms,
|
|
548
|
+
behaviorChange: options.behaviorChange,
|
|
549
|
+
executableChange: options.executableChange,
|
|
550
|
+
});
|
|
551
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatRouteResult(result));
|
|
552
|
+
return 0;
|
|
553
|
+
},
|
|
554
|
+
activate: async ({ target, packageRoot, options }) => {
|
|
555
|
+
const result = await runActivate({ target, packageRoot });
|
|
556
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatActivateResult(result));
|
|
557
|
+
return 0;
|
|
558
|
+
},
|
|
559
|
+
preflight: async ({ target, packageRoot, options }) => {
|
|
560
|
+
const result = await runPreflight({ target, packageRoot, strict: options.strict });
|
|
561
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatPreflightResult(result));
|
|
562
|
+
return result.status === "READY" ? 0 : 1;
|
|
563
|
+
},
|
|
564
|
+
advance: async ({ target, packageRoot, options }) => {
|
|
565
|
+
const result = await runAdvance({ target, packageRoot, to: options.to });
|
|
566
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatAdvanceResult(result));
|
|
567
|
+
return 0;
|
|
568
|
+
},
|
|
569
|
+
next: async ({ target, packageRoot, options }) => {
|
|
570
|
+
const result = await runNext({ target, packageRoot });
|
|
571
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatNextActionResult(result));
|
|
572
|
+
return 0;
|
|
573
|
+
},
|
|
574
|
+
continuity: async ({ target, packageRoot, options }) => {
|
|
575
|
+
const result = await runContinuity({ target, packageRoot });
|
|
576
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatContinuityResult(result));
|
|
577
|
+
return 0;
|
|
578
|
+
},
|
|
579
|
+
"record-continuity": async ({ target, packageRoot, options }) => {
|
|
580
|
+
const result = await runRecordContinuity({
|
|
581
|
+
target, packageRoot,
|
|
582
|
+
focusId: options.continuityFocusId,
|
|
583
|
+
focusSummary: options.continuityFocusSummary,
|
|
584
|
+
remaining: options.continuityRemaining,
|
|
585
|
+
knownIssues: options.continuityKnownIssues,
|
|
586
|
+
changedAreas: options.continuityChangedAreas,
|
|
587
|
+
inspectFirst: options.continuityInspectFirst,
|
|
588
|
+
resumeNote: options.continuityResumeNote,
|
|
589
|
+
});
|
|
590
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordContinuityResult(result));
|
|
591
|
+
return 0;
|
|
592
|
+
},
|
|
593
|
+
"reconcile-continuity": async ({ target, packageRoot, options }) => {
|
|
594
|
+
const result = await runReconcileContinuity({ target, packageRoot });
|
|
595
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatReconcileContinuityResult(result));
|
|
596
|
+
return 0;
|
|
597
|
+
},
|
|
598
|
+
"clear-continuity": async ({ target, options }) => {
|
|
599
|
+
const result = await runClearContinuity({ target });
|
|
600
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatClearContinuityResult(result));
|
|
601
|
+
return 0;
|
|
602
|
+
},
|
|
603
|
+
"prepare-completion": async ({ target, packageRoot, options }) => {
|
|
604
|
+
const result = await runPrepareCompletion({ target, packageRoot });
|
|
605
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatPrepareCompletionResult(result));
|
|
606
|
+
return 0;
|
|
607
|
+
},
|
|
608
|
+
"run-check": async ({ target, packageRoot, options }) => {
|
|
609
|
+
const result = await runCheck({
|
|
610
|
+
target,
|
|
611
|
+
packageRoot,
|
|
612
|
+
id: options.checkId,
|
|
613
|
+
requirement: options.checkRequirement,
|
|
614
|
+
argv: options.commandArgv,
|
|
615
|
+
details: options.checkDetails ?? undefined,
|
|
616
|
+
});
|
|
617
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatRunCheckResult(result));
|
|
618
|
+
return result.check.status === "passed" ? 0 : 1;
|
|
619
|
+
},
|
|
620
|
+
"record-check": async ({ target, packageRoot, options }) => {
|
|
621
|
+
const result = await runRecordCheck({
|
|
622
|
+
target,
|
|
623
|
+
packageRoot,
|
|
624
|
+
id: options.checkId,
|
|
625
|
+
kind: options.checkKind ?? "command",
|
|
626
|
+
requirement: options.checkRequirement,
|
|
627
|
+
status: options.checkStatus,
|
|
628
|
+
evidenceKind: options.checkEvidenceKind,
|
|
629
|
+
command: options.checkCommand ?? undefined,
|
|
630
|
+
result: options.checkResult ?? undefined,
|
|
631
|
+
...(options.checkExitCode === null ? {} : { exitCode: options.checkExitCode }),
|
|
632
|
+
details: options.checkDetails ?? undefined,
|
|
633
|
+
executionRef: options.checkExecutionRef ?? undefined,
|
|
634
|
+
provenance: options.checkProvenance ?? undefined,
|
|
635
|
+
});
|
|
636
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordCheckResult(result));
|
|
637
|
+
return 0;
|
|
638
|
+
},
|
|
639
|
+
"record-terminal-result": async ({ target, packageRoot, options }) => {
|
|
640
|
+
const result = await runRecordTerminalResult({
|
|
641
|
+
target,
|
|
642
|
+
packageRoot,
|
|
643
|
+
requirement: options.checkRequirement,
|
|
644
|
+
type: options.checkType,
|
|
645
|
+
status: options.checkStatus,
|
|
646
|
+
source: options.checkSource ?? options.checkCommand,
|
|
647
|
+
result: options.checkResult,
|
|
648
|
+
details: options.checkDetails ?? undefined,
|
|
649
|
+
});
|
|
650
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordTerminalResult(result));
|
|
651
|
+
return 0;
|
|
652
|
+
},
|
|
653
|
+
complete: async ({ target, packageRoot, options }) => {
|
|
654
|
+
const result = await runComplete({ target, packageRoot, strict: options.strict });
|
|
655
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatCompleteResult(result));
|
|
656
|
+
return result.status === "VALID" ? 0 : 1;
|
|
657
|
+
},
|
|
658
|
+
audit: async ({ target, packageRoot, options }) => {
|
|
659
|
+
const result = await runAudit({ target, packageRoot, strict: options.strict });
|
|
660
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatAuditResult(result));
|
|
661
|
+
return result.status === "VALID" ? 0 : 1;
|
|
662
|
+
},
|
|
663
|
+
report: async ({ target, packageRoot, options }) => {
|
|
664
|
+
const result = await runReport({ target, packageRoot, strict: options.strict });
|
|
665
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatReportResult(result));
|
|
666
|
+
return result.verdict === "VALID" ? 0 : 1;
|
|
667
|
+
},
|
|
668
|
+
policy: async ({ target, packageRoot, options }) => {
|
|
669
|
+
const result = await runPolicy({ target, packageRoot, name: options.policy });
|
|
670
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatPolicyResult(result));
|
|
671
|
+
return 0;
|
|
672
|
+
},
|
|
673
|
+
bundle: async ({ target, packageRoot, options }) => {
|
|
674
|
+
const result = await runBundle({ target, packageRoot, taskId: options.task });
|
|
675
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatBundleResult(result));
|
|
676
|
+
return 0;
|
|
677
|
+
},
|
|
678
|
+
inspect: async ({ target, packageRoot, options }) => {
|
|
679
|
+
const result = await inspectTarget({ target, packageRoot, contractFile: options.contractFile });
|
|
680
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatInspectResult(result));
|
|
681
|
+
return result.ok ? 0 : 1;
|
|
682
|
+
},
|
|
683
|
+
"validate-receipt": async ({ target, packageRoot, options }) => {
|
|
684
|
+
const result = await runValidateReceipt({ target, packageRoot, file: options.file });
|
|
685
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : "valid: execution receipt");
|
|
686
|
+
return 0;
|
|
687
|
+
},
|
|
688
|
+
"validate-protocol": async ({ target, packageRoot, options }) => {
|
|
689
|
+
const result = await runValidateProtocol({
|
|
690
|
+
target,
|
|
691
|
+
packageRoot,
|
|
692
|
+
stateFile: options.stateFile ?? ARTIFACT_PATHS.state,
|
|
693
|
+
receiptFile: options.receiptFile ?? ARTIFACT_PATHS.receipt,
|
|
694
|
+
routeFile: options.routeFile ?? ARTIFACT_PATHS.route,
|
|
695
|
+
contractFile: options.contractFile,
|
|
696
|
+
continuityFile: options.continuityFile,
|
|
697
|
+
taskBriefFiles: options.taskBriefFiles,
|
|
698
|
+
delegatedResultFiles: options.delegatedResultFiles,
|
|
699
|
+
});
|
|
700
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatValidateProtocolResult(result));
|
|
701
|
+
return result.status === "VALID" ? 0 : 1;
|
|
702
|
+
},
|
|
703
|
+
status: async ({ target, packageRoot, options }) => {
|
|
704
|
+
const result = await runStatus({ target, packageRoot, contractFile: options.contractFile });
|
|
705
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatStatusResult(result));
|
|
706
|
+
return 0;
|
|
707
|
+
},
|
|
708
|
+
"validate-state": async ({ target, packageRoot, options }) => {
|
|
709
|
+
const result = await runValidateState({ target, packageRoot });
|
|
710
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatValidateStateResult(result));
|
|
711
|
+
return result.ok ? 0 : 1;
|
|
712
|
+
},
|
|
713
|
+
"clear-state": async ({ target, options }) => {
|
|
714
|
+
const result = await runClearState({ target });
|
|
715
|
+
console.log(options.json ? JSON.stringify(result, null, 2) : formatClearStateResult(result));
|
|
716
|
+
return 0;
|
|
717
|
+
},
|
|
718
|
+
update: async ({ target, packageRoot, packageVersion, options }) => {
|
|
719
|
+
const result = await runUpdate({ target, dryRun: options.dryRun, packageRoot, packageVersion });
|
|
720
|
+
printActions(result.actions);
|
|
721
|
+
for (const conflict of result.conflicts) {
|
|
722
|
+
const code = conflict.code ? `${conflict.code}: ` : "";
|
|
723
|
+
console.log(`conflict: ${code}${conflict.path} - ${conflict.message}`);
|
|
724
|
+
}
|
|
725
|
+
return result.conflicts.length === 0 ? 0 : 1;
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
export const COMMAND_TABLE = Object.freeze(
|
|
730
|
+
COMMANDS.map((name) => Object.freeze({
|
|
731
|
+
name,
|
|
732
|
+
handler: COMMAND_HANDLERS[name],
|
|
733
|
+
usage: usage(name),
|
|
734
|
+
})),
|
|
735
|
+
);
|
|
736
|
+
|
|
445
737
|
export async function main(argv = process.argv.slice(2)) {
|
|
446
738
|
try {
|
|
447
739
|
const { command, options } = parseArgs(argv);
|
|
@@ -458,205 +750,16 @@ export async function main(argv = process.argv.slice(2)) {
|
|
|
458
750
|
const packageRoot = getPackageRoot();
|
|
459
751
|
const version = await packageVersion(packageRoot);
|
|
460
752
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return 0;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (command === "doctor") {
|
|
468
|
-
const result = await runDoctor({
|
|
469
|
-
target,
|
|
470
|
-
packageRoot,
|
|
471
|
-
adoptPaths: options.adopt,
|
|
472
|
-
strict: options.strict,
|
|
473
|
-
});
|
|
474
|
-
if (options.json) {
|
|
475
|
-
console.log(JSON.stringify(result, null, 2));
|
|
476
|
-
} else {
|
|
477
|
-
for (const item of result.findings) {
|
|
478
|
-
console.log(`${item.severity}: ${item.code}: ${item.path} - ${item.message}`);
|
|
479
|
-
}
|
|
480
|
-
console.log(result.ok ? "healthy: ForgeLoop target is ready" : "unhealthy: ForgeLoop target needs attention");
|
|
481
|
-
}
|
|
482
|
-
return result.ok ? 0 : 1;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (command === "route") {
|
|
486
|
-
const result = await runRoute({
|
|
487
|
-
target,
|
|
488
|
-
packageRoot,
|
|
489
|
-
workType: options.work,
|
|
490
|
-
surfaces: options.surfaces,
|
|
491
|
-
risks: options.risks,
|
|
492
|
-
platforms: options.platforms,
|
|
493
|
-
behaviorChange: options.behaviorChange,
|
|
494
|
-
executableChange: options.executableChange,
|
|
495
|
-
});
|
|
496
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatRouteResult(result));
|
|
497
|
-
return 0;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (command === "activate") {
|
|
501
|
-
const result = await runActivate({ target, packageRoot });
|
|
502
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatActivateResult(result));
|
|
503
|
-
return 0;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (command === "preflight") {
|
|
507
|
-
const result = await runPreflight({ target, packageRoot, strict: options.strict });
|
|
508
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatPreflightResult(result));
|
|
509
|
-
return result.status === "READY" ? 0 : 1;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (command === "advance") {
|
|
513
|
-
const result = await runAdvance({ target, packageRoot, to: options.to });
|
|
514
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatAdvanceResult(result));
|
|
515
|
-
return 0;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (command === "next") {
|
|
519
|
-
const result = await runNext({ target, packageRoot });
|
|
520
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatNextActionResult(result));
|
|
521
|
-
return 0;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (command === "prepare-completion") {
|
|
525
|
-
const result = await runPrepareCompletion({ target, packageRoot });
|
|
526
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatPrepareCompletionResult(result));
|
|
527
|
-
return 0;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if (command === "run-check") {
|
|
531
|
-
const result = await runCheck({
|
|
532
|
-
target,
|
|
533
|
-
packageRoot,
|
|
534
|
-
id: options.checkId,
|
|
535
|
-
requirement: options.checkRequirement,
|
|
536
|
-
argv: options.commandArgv,
|
|
537
|
-
details: options.checkDetails ?? undefined,
|
|
538
|
-
});
|
|
539
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatRunCheckResult(result));
|
|
540
|
-
return result.check.status === "passed" ? 0 : 1;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (command === "record-check") {
|
|
544
|
-
const result = await runRecordCheck({
|
|
545
|
-
target,
|
|
546
|
-
packageRoot,
|
|
547
|
-
id: options.checkId,
|
|
548
|
-
kind: options.checkKind ?? "command",
|
|
549
|
-
requirement: options.checkRequirement,
|
|
550
|
-
status: options.checkStatus,
|
|
551
|
-
evidenceKind: options.checkEvidenceKind,
|
|
552
|
-
command: options.checkCommand ?? undefined,
|
|
553
|
-
result: options.checkResult ?? undefined,
|
|
554
|
-
...(options.checkExitCode === null ? {} : { exitCode: options.checkExitCode }),
|
|
555
|
-
details: options.checkDetails ?? undefined,
|
|
556
|
-
executionRef: options.checkExecutionRef ?? undefined,
|
|
557
|
-
provenance: options.checkProvenance ?? undefined,
|
|
558
|
-
});
|
|
559
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordCheckResult(result));
|
|
560
|
-
return 0;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (command === "record-terminal-result") {
|
|
564
|
-
const result = await runRecordTerminalResult({
|
|
565
|
-
target,
|
|
566
|
-
packageRoot,
|
|
567
|
-
requirement: options.checkRequirement,
|
|
568
|
-
type: options.checkType,
|
|
569
|
-
status: options.checkStatus,
|
|
570
|
-
source: options.checkSource ?? options.checkCommand,
|
|
571
|
-
result: options.checkResult,
|
|
572
|
-
details: options.checkDetails ?? undefined,
|
|
573
|
-
});
|
|
574
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatRecordTerminalResult(result));
|
|
575
|
-
return 0;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (command === "complete") {
|
|
579
|
-
const result = await runComplete({ target, packageRoot, strict: options.strict });
|
|
580
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatCompleteResult(result));
|
|
581
|
-
return result.status === "VALID" ? 0 : 1;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (command === "audit") {
|
|
585
|
-
const result = await runAudit({ target, packageRoot, strict: options.strict });
|
|
586
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatAuditResult(result));
|
|
587
|
-
return result.status === "VALID" ? 0 : 1;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (command === "report") {
|
|
591
|
-
const result = await runReport({ target, packageRoot, strict: options.strict });
|
|
592
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatReportResult(result));
|
|
593
|
-
return result.verdict === "VALID" ? 0 : 1;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (command === "policy") {
|
|
597
|
-
const result = await runPolicy({ target, packageRoot, name: options.policy });
|
|
598
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatPolicyResult(result));
|
|
599
|
-
return 0;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (command === "bundle") {
|
|
603
|
-
const result = await runBundle({ target, packageRoot, taskId: options.task });
|
|
604
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatBundleResult(result));
|
|
605
|
-
return 0;
|
|
753
|
+
const handler = COMMAND_HANDLERS[command];
|
|
754
|
+
if (typeof handler !== "function") {
|
|
755
|
+
throw new Error(`Unsupported command: ${command}`);
|
|
606
756
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
if (command === "validate-receipt") {
|
|
615
|
-
const result = await runValidateReceipt({ target, packageRoot, file: options.file });
|
|
616
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : "valid: execution receipt");
|
|
617
|
-
return 0;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
if (command === "validate-protocol") {
|
|
621
|
-
const result = await runValidateProtocol({
|
|
622
|
-
target,
|
|
623
|
-
packageRoot,
|
|
624
|
-
stateFile: options.stateFile ?? ARTIFACT_PATHS.state,
|
|
625
|
-
receiptFile: options.receiptFile ?? ARTIFACT_PATHS.receipt,
|
|
626
|
-
routeFile: options.routeFile ?? ARTIFACT_PATHS.route,
|
|
627
|
-
contractFile: options.contractFile,
|
|
628
|
-
taskBriefFiles: options.taskBriefFiles,
|
|
629
|
-
delegatedResultFiles: options.delegatedResultFiles,
|
|
630
|
-
});
|
|
631
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatValidateProtocolResult(result));
|
|
632
|
-
return result.status === "VALID" ? 0 : 1;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
if (command === "status") {
|
|
636
|
-
const result = await runStatus({ target, packageRoot, contractFile: options.contractFile });
|
|
637
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatStatusResult(result));
|
|
638
|
-
return 0;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
if (command === "validate-state") {
|
|
642
|
-
const result = await runValidateState({ target, packageRoot });
|
|
643
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatValidateStateResult(result));
|
|
644
|
-
return result.ok ? 0 : 1;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
if (command === "clear-state") {
|
|
648
|
-
const result = await runClearState({ target });
|
|
649
|
-
console.log(options.json ? JSON.stringify(result, null, 2) : formatClearStateResult(result));
|
|
650
|
-
return 0;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
const result = await runUpdate({ target, dryRun: options.dryRun, packageRoot, packageVersion: version });
|
|
654
|
-
printActions(result.actions);
|
|
655
|
-
for (const conflict of result.conflicts) {
|
|
656
|
-
const code = conflict.code ? `${conflict.code}: ` : "";
|
|
657
|
-
console.log(`conflict: ${code}${conflict.path} - ${conflict.message}`);
|
|
658
|
-
}
|
|
659
|
-
return result.conflicts.length === 0 ? 0 : 1;
|
|
757
|
+
return await handler({
|
|
758
|
+
target,
|
|
759
|
+
packageRoot,
|
|
760
|
+
packageVersion: version,
|
|
761
|
+
options,
|
|
762
|
+
});
|
|
660
763
|
} catch (error) {
|
|
661
764
|
console.error(`error: ${error.code ? `${error.code}: ` : ""}${error.message}`);
|
|
662
765
|
return 1;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { clearContinuity } from "../core/continuity.js";
|
|
2
|
+
|
|
3
|
+
export async function runClearContinuity({ target } = {}) {
|
|
4
|
+
return clearContinuity(target);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function formatClearContinuityResult(result) {
|
|
8
|
+
return `${result.removed ? "cleared" : "absent"}: ${result.path}\n`;
|
|
9
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { reconcileContinuity } from "../core/continuity-reconciliation.js";
|
|
2
|
+
|
|
3
|
+
export async function runContinuity({ target, packageRoot } = {}) {
|
|
4
|
+
return reconcileContinuity({ target, packageRoot });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function formatContinuityResult(result) {
|
|
8
|
+
const value = result.continuity;
|
|
9
|
+
const lines = [
|
|
10
|
+
"FORGELOOP EXECUTION CONTINUITY",
|
|
11
|
+
"",
|
|
12
|
+
`classification: ${result.classification}`,
|
|
13
|
+
`task: ${value?.taskId ?? "none"}`,
|
|
14
|
+
`phase: ${value?.phase ?? "none"}`,
|
|
15
|
+
`current focus: ${value?.currentFocus?.id ?? "none"}`,
|
|
16
|
+
`remaining work: ${value?.remainingWork?.length ?? 0}`,
|
|
17
|
+
`known issues: ${value?.knownIssues?.length ?? 0}`,
|
|
18
|
+
`inspect first: ${value?.inspectFirst?.join(", ") || "none"}`,
|
|
19
|
+
"",
|
|
20
|
+
"Authority: OPERATIONAL_CONTEXT_ONLY",
|
|
21
|
+
"Evidence: NONE",
|
|
22
|
+
];
|
|
23
|
+
if (result.reasons?.length) lines.push(`Reasons: ${result.reasons.join(", ")}`);
|
|
24
|
+
return `${lines.join("\n")}\n`;
|
|
25
|
+
}
|