create-agdf 0.10.0 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -22
- package/bin/agdf-validator.js +11 -0
- package/generated/.agdf/control/README.md +3 -3
- package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +1 -1
- package/generated/.github/skills/agdf-delivery-path-search/SKILL.md +7 -6
- package/generated/.github/skills/agdf-gate-check/SKILL.md +28 -91
- package/generated/.github/skills/agdf-interaction-locales.json +2 -0
- package/generated/.github/skills/contracts/gate-transition.md +4 -2
- package/generated/.github/skills/contracts/modes.md +12 -1
- package/generated/.opencode/README.md +6 -8
- package/generated/.opencode/agdf-interaction-locales.json +2 -0
- package/generated/.opencode/contracts/gate-transition.md +4 -2
- package/generated/.opencode/contracts/modes.md +12 -1
- package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +1 -1
- package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +7 -6
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +28 -91
- package/generated/plugins/agdf/.claude-plugin/plugin.json +19 -0
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/control/README.md +3 -3
- package/generated/plugins/agdf/hooks/session-start.sh +1 -1
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/contracts/gate-transition.md +4 -2
- package/generated/plugins/agdf/meta/contracts/modes.md +12 -1
- package/generated/plugins/agdf/runtime/agdf-local.js +11 -0
- package/generated/plugins/agdf/runtime/create-agdf/NOTICE +10 -0
- package/generated/plugins/agdf/runtime/create-agdf/bin/agdf-validator.js +11 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +418 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +220 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +136 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +75 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +156 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/runtime-context.js +83 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +45 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +291 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +265 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +459 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +283 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/required-files.js +9 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +217 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +344 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js +156 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/aggregate.js +18 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/gate-approval-validator.js +49 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/index.js +7 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js +95 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +227 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js +68 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-resolver.js +45 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-writer.js +97 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/candidate-policy.js +99 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +237 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +44 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +53 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/protocol.js +13 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/claude.js +40 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/codex.js +38 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/protocol.js +12 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +39 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js +21 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +154 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +161 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +14 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/transports/read-only-guard.js +43 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +590 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +142 -0
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/validator-application.js +40 -0
- package/generated/plugins/agdf/runtime/create-agdf/package.json +66 -0
- package/generated/plugins/agdf/runtime/runtime-manifest.json +8 -0
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +1040 -0
- package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +1 -1
- package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +7 -6
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +28 -91
- package/lib/cli/application.js +21 -59
- package/lib/cli/command-registry.js +9 -11
- package/lib/cli/validation-handlers.js +45 -0
- package/lib/control-evaluation/doctor.js +1 -1
- package/lib/control-evaluation/gate-check.js +7 -7
- package/lib/control-evaluation/required-files.js +9 -0
- package/lib/installers/local-marketplace.js +322 -0
- package/lib/installers/opencode-activation.js +43 -0
- package/lib/installers/opencode.js +60 -18
- package/lib/installers/plugin-installers.js +102 -26
- package/lib/runtime/local-validator.js +142 -0
- package/lib/runtime/validator-application.js +40 -0
- package/lib/scaffold/plan.js +1 -30
- package/lib/scaffold/presentation.js +9 -14
- package/opencode-plugin.js +32 -24
- package/package.json +6 -2
|
@@ -1,41 +1,117 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { pluginDefinition } from "../cli/runtime-context.js";
|
|
3
|
+
import { classifyMarketplaceList, prepareLocalMarketplace } from "./local-marketplace.js";
|
|
3
4
|
|
|
4
|
-
export function installCodexGlobalPlugin({ exec = execFileSync,
|
|
5
|
+
export function installCodexGlobalPlugin({ exec = execFileSync, prepare = prepareLocalMarketplace, dataRoot } = {}) {
|
|
5
6
|
const expectedVersion = pluginDefinition.version;
|
|
6
7
|
const nativeOutput = [];
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const transaction = prepare({ expectedVersion, ...(dataRoot ? { dataRoot } : {}) });
|
|
9
|
+
const migration = { state: "unknown", source: "", addedLocal: false, removedLegacy: false };
|
|
10
|
+
try {
|
|
11
|
+
const marketplaceOutput = runPluginPhase(exec, "codex", ["plugin", "marketplace", "list", "--json"], "marketplace", captureOptions());
|
|
12
|
+
migrateMarketplace({ surface: "codex", exec, output: marketplaceOutput, root: transaction.root, nativeOutput, migration });
|
|
13
|
+
nativeOutput.push(runPluginPhase(exec, "codex", ["plugin", "add", "agdf", "--marketplace", "agdf"], "plugin_operation", captureOptions()));
|
|
14
|
+
const listOutput = runPluginPhase(exec, "codex", ["plugin", "list"], "verification", captureOptions());
|
|
15
|
+
const installedVersion = pluginVersionFromList(listOutput, "agdf@agdf");
|
|
16
|
+
if (installedVersion !== expectedVersion) {
|
|
17
|
+
throw lifecycleAdapterError("version", versionMismatchMessage("Codex", "agdf@agdf", expectedVersion, installedVersion, "npx --yes @agdf/cli@latest codex"));
|
|
18
|
+
}
|
|
19
|
+
transaction.commit();
|
|
20
|
+
return {
|
|
21
|
+
surface: "codex", operation: migration.state === "owned_local_current" ? "update" : "install", expectedVersion, installedVersion, verificationStatus: "healthy",
|
|
22
|
+
evidence: ["durable_local_marketplace", `marketplace:${migration.state}`, "codex plugin list"],
|
|
23
|
+
nativeOutput: nativeOutput.filter(Boolean).map(String),
|
|
24
|
+
};
|
|
25
|
+
} catch (error) {
|
|
26
|
+
recoverMarketplace({ surface: "codex", exec, migration, transaction, error });
|
|
27
|
+
throw error;
|
|
14
28
|
}
|
|
15
|
-
return { surface: "codex", expectedVersion, installedVersion, verificationStatus: "healthy", evidence: ["codex plugin list"], nativeOutput: nativeOutput.filter(Boolean).map(String) };
|
|
16
29
|
}
|
|
17
30
|
|
|
18
|
-
export function installClaudeGlobalPlugin({ exec = execFileSync,
|
|
31
|
+
export function installClaudeGlobalPlugin({ exec = execFileSync, prepare = prepareLocalMarketplace, dataRoot } = {}) {
|
|
19
32
|
const expectedVersion = pluginDefinition.version;
|
|
20
33
|
const nativeOutput = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
const transaction = prepare({ expectedVersion, ...(dataRoot ? { dataRoot } : {}) });
|
|
35
|
+
const migration = { state: "unknown", source: "", addedLocal: false, removedLegacy: false };
|
|
36
|
+
try {
|
|
37
|
+
const marketplaceOutput = runPluginPhase(exec, "claude", ["plugin", "marketplace", "list", "--json"], "marketplace", captureOptions());
|
|
38
|
+
migrateMarketplace({ surface: "claude", exec, output: marketplaceOutput, root: transaction.root, nativeOutput, migration });
|
|
39
|
+
nativeOutput.push(runPluginPhase(exec, "claude", ["plugin", "marketplace", "update", "agdf"], "marketplace", captureOptions()));
|
|
40
|
+
const beforeList = runPluginPhase(exec, "claude", ["plugin", "list"], "verification", captureOptions());
|
|
41
|
+
const alreadyInstalled = pluginListHasPlugin(beforeList, "agdf@agdf");
|
|
42
|
+
nativeOutput.push(runPluginPhase(exec, "claude", ["plugin", alreadyInstalled ? "update" : "install", "agdf@agdf"], "plugin_operation", captureOptions()));
|
|
43
|
+
const afterList = runPluginPhase(exec, "claude", ["plugin", "list"], "verification", captureOptions());
|
|
44
|
+
const installedVersion = pluginVersionFromList(afterList, "agdf@agdf");
|
|
45
|
+
if (installedVersion && installedVersion !== expectedVersion) {
|
|
46
|
+
throw lifecycleAdapterError("version", versionMismatchMessage("Claude Code", "agdf@agdf", expectedVersion, installedVersion, "npx --yes @agdf/cli@latest claude"));
|
|
47
|
+
}
|
|
48
|
+
transaction.commit();
|
|
49
|
+
return {
|
|
50
|
+
surface: "claude",
|
|
51
|
+
operation: migration.state === "owned_local_current" ? "update" : "install",
|
|
52
|
+
expectedVersion,
|
|
53
|
+
installedVersion,
|
|
54
|
+
verificationStatus: installedVersion ? "healthy" : "degraded",
|
|
55
|
+
evidence: ["durable_local_marketplace", `marketplace:${migration.state}`, "claude plugin list", ...(installedVersion ? [] : ["host_did_not_expose_version"])],
|
|
56
|
+
nativeOutput: nativeOutput.filter(Boolean).map(String),
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
recoverMarketplace({ surface: "claude", exec, migration, transaction, error });
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function migrateMarketplace({ surface, exec, output, root, nativeOutput, migration }) {
|
|
65
|
+
const classification = classifyMarketplaceList(surface, output, root);
|
|
66
|
+
Object.assign(migration, classification);
|
|
67
|
+
if (["conflict", "unknown"].includes(classification.state)) {
|
|
68
|
+
throw lifecycleAdapterError("marketplace", `Refusing to replace non-AGDF marketplace registration agdf (${classification.reason || classification.state}).`, classification);
|
|
30
69
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
const executable = surface === "claude" ? "claude" : "codex";
|
|
71
|
+
if (classification.state === "legacy_github") {
|
|
72
|
+
const args = surface === "claude"
|
|
73
|
+
? ["plugin", "marketplace", "remove", "agdf", "--scope", "user"]
|
|
74
|
+
: ["plugin", "marketplace", "remove", "agdf", "--json"];
|
|
75
|
+
nativeOutput.push(runPluginPhase(exec, executable, args, "marketplace", captureOptions()));
|
|
76
|
+
migration.removedLegacy = true;
|
|
77
|
+
}
|
|
78
|
+
if (["absent", "legacy_github"].includes(classification.state)) {
|
|
79
|
+
const args = surface === "claude"
|
|
80
|
+
? ["plugin", "marketplace", "add", root, "--scope", "user"]
|
|
81
|
+
: ["plugin", "marketplace", "add", root, "--json"];
|
|
82
|
+
nativeOutput.push(runPluginPhase(exec, executable, args, "marketplace", captureOptions()));
|
|
83
|
+
migration.addedLocal = true;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function recoverMarketplace({ surface, exec, migration, transaction, error }) {
|
|
88
|
+
const executable = surface === "claude" ? "claude" : "codex";
|
|
89
|
+
const recovery = [];
|
|
90
|
+
const attempt = (args) => {
|
|
91
|
+
try {
|
|
92
|
+
exec(executable, args, captureOptions());
|
|
93
|
+
recovery.push({ args, status: "restored" });
|
|
94
|
+
} catch (rollbackError) {
|
|
95
|
+
recovery.push({ args, status: "failed", message: commandErrorText(rollbackError) });
|
|
96
|
+
}
|
|
38
97
|
};
|
|
98
|
+
if (migration?.addedLocal) {
|
|
99
|
+
attempt(surface === "claude"
|
|
100
|
+
? ["plugin", "marketplace", "remove", "agdf", "--scope", "user"]
|
|
101
|
+
: ["plugin", "marketplace", "remove", "agdf", "--json"]);
|
|
102
|
+
}
|
|
103
|
+
if (migration?.removedLegacy && migration.source) {
|
|
104
|
+
attempt(surface === "claude"
|
|
105
|
+
? ["plugin", "marketplace", "add", migration.source, "--scope", "user"]
|
|
106
|
+
: ["plugin", "marketplace", "add", migration.source, "--json"]);
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
transaction.rollback();
|
|
110
|
+
recovery.push({ filesystem: transaction.root, status: "restored" });
|
|
111
|
+
} catch (rollbackError) {
|
|
112
|
+
recovery.push({ filesystem: transaction.root, status: "failed", message: rollbackError.message });
|
|
113
|
+
}
|
|
114
|
+
error.evidence = { ...(error.evidence ?? {}), rollback: recovery };
|
|
39
115
|
}
|
|
40
116
|
|
|
41
117
|
function captureOptions() {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
|
|
7
|
+
function readJson(path) {
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function digestDirectory(root) {
|
|
16
|
+
const files = [];
|
|
17
|
+
function visit(directory) {
|
|
18
|
+
for (const name of readdirSync(directory).sort()) {
|
|
19
|
+
const path = join(directory, name);
|
|
20
|
+
const stats = statSync(path);
|
|
21
|
+
if (stats.isDirectory()) visit(path);
|
|
22
|
+
else if (stats.isFile()) files.push(path);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
visit(root);
|
|
26
|
+
const hash = createHash("sha256");
|
|
27
|
+
for (const path of files) {
|
|
28
|
+
hash.update(relative(root, path).replaceAll("\\", "/"));
|
|
29
|
+
hash.update("\0");
|
|
30
|
+
hash.update(readFileSync(path));
|
|
31
|
+
hash.update("\0");
|
|
32
|
+
}
|
|
33
|
+
return hash.digest("hex");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function envelope(machineValidation, options, evidence = {}) {
|
|
37
|
+
return {
|
|
38
|
+
schema_version: "1",
|
|
39
|
+
machine_validation: machineValidation,
|
|
40
|
+
surface: options.surface,
|
|
41
|
+
expected_version: options.expectedVersion,
|
|
42
|
+
observed_version: evidence.observedVersion ?? null,
|
|
43
|
+
source: evidence.source ?? null,
|
|
44
|
+
registry_access: false,
|
|
45
|
+
...(evidence.reason ? { reason: evidence.reason } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resolveLocalValidator(options) {
|
|
50
|
+
if (options.ownedPackageRoot) {
|
|
51
|
+
const packageManifest = readJson(join(options.ownedPackageRoot, "package.json"));
|
|
52
|
+
const observedVersion = packageManifest?.version ?? null;
|
|
53
|
+
if (!packageManifest) {
|
|
54
|
+
return { envelope: envelope("unavailable", options, { source: "config_local_package", reason: "package_missing" }) };
|
|
55
|
+
}
|
|
56
|
+
if (observedVersion !== options.expectedVersion) {
|
|
57
|
+
return { envelope: envelope("version_mismatch", options, { observedVersion, source: "config_local_package" }) };
|
|
58
|
+
}
|
|
59
|
+
const entrypoint = join(options.ownedPackageRoot, "bin", "create-agdf.js");
|
|
60
|
+
if (!existsSync(entrypoint)) {
|
|
61
|
+
return { envelope: envelope("unavailable", options, { observedVersion, source: "config_local_package", reason: "entrypoint_missing" }) };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
envelope: envelope("owned_version_matched", options, { observedVersion, source: "config_local_package" }),
|
|
65
|
+
executable: process.execPath,
|
|
66
|
+
prefixArgs: [entrypoint],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const manifestPath = join(options.runtimeRoot, "runtime-manifest.json");
|
|
70
|
+
const manifestExists = existsSync(manifestPath);
|
|
71
|
+
const manifest = manifestExists ? readJson(manifestPath) : null;
|
|
72
|
+
if (manifestExists && !manifest) {
|
|
73
|
+
return { envelope: envelope("version_mismatch", options, { source: "plugin_bundle", reason: "manifest_invalid" }) };
|
|
74
|
+
}
|
|
75
|
+
if (manifest) {
|
|
76
|
+
const packageRoot = join(options.runtimeRoot, "create-agdf");
|
|
77
|
+
if (!existsSync(packageRoot)) {
|
|
78
|
+
return { envelope: envelope("unavailable", options, { source: "plugin_bundle", reason: "package_missing" }) };
|
|
79
|
+
}
|
|
80
|
+
const packageManifest = readJson(join(packageRoot, "package.json"));
|
|
81
|
+
const observedVersion = packageManifest?.version ?? manifest.version ?? null;
|
|
82
|
+
if (observedVersion !== options.expectedVersion || manifest.version !== options.expectedVersion) {
|
|
83
|
+
return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle" }) };
|
|
84
|
+
}
|
|
85
|
+
let observedDigest = null;
|
|
86
|
+
try { observedDigest = digestDirectory(packageRoot); } catch {}
|
|
87
|
+
if (!observedDigest || manifest.digest !== observedDigest) {
|
|
88
|
+
return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", reason: "digest_mismatch" }) };
|
|
89
|
+
}
|
|
90
|
+
const entrypoint = resolve(options.runtimeRoot, manifest.entrypoint ?? "");
|
|
91
|
+
const entrypointRelative = relative(options.runtimeRoot, entrypoint);
|
|
92
|
+
if (!manifest.entrypoint || entrypointRelative === ".." || entrypointRelative.startsWith(`..${sep}`) || isAbsolute(entrypointRelative)) {
|
|
93
|
+
return { envelope: envelope("version_mismatch", options, { observedVersion, source: "plugin_bundle", reason: "invalid_entrypoint" }) };
|
|
94
|
+
}
|
|
95
|
+
if (!existsSync(entrypoint)) {
|
|
96
|
+
return { envelope: envelope("unavailable", options, { observedVersion, source: "plugin_bundle", reason: "entrypoint_missing" }) };
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
envelope: envelope("owned_version_matched", options, { observedVersion, source: "plugin_bundle" }),
|
|
100
|
+
executable: process.execPath,
|
|
101
|
+
prefixArgs: [entrypoint],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const configuredPath = options.configuredPath ?? process.env.AGDF_VALIDATOR_PATH ?? "";
|
|
106
|
+
if (configuredPath) {
|
|
107
|
+
if (!isAbsolute(configuredPath) || !existsSync(configuredPath)) {
|
|
108
|
+
return { envelope: envelope("unavailable", options, { source: "configured_path", reason: "invalid_absolute_path" }) };
|
|
109
|
+
}
|
|
110
|
+
const versionProbe = spawnSync(configuredPath, ["--version", "--json"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
111
|
+
let observedVersion = null;
|
|
112
|
+
try { observedVersion = JSON.parse(versionProbe.stdout)?.version ?? null; } catch {}
|
|
113
|
+
if (versionProbe.status !== 0 || observedVersion !== options.expectedVersion) {
|
|
114
|
+
return { envelope: envelope("version_mismatch", options, { observedVersion, source: "configured_path" }) };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
envelope: envelope("configured_version_matched", options, { observedVersion, source: "configured_path" }),
|
|
118
|
+
executable: configuredPath,
|
|
119
|
+
prefixArgs: [],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { envelope: envelope(options.externalRequired ? "external_required" : "unavailable", options) };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function runLocalValidator(options, args, io = console) {
|
|
127
|
+
const resolved = resolveLocalValidator(options);
|
|
128
|
+
if (args[0] === "--resolve-only") {
|
|
129
|
+
io.log(JSON.stringify(resolved.envelope, null, args.includes("--json") ? 2 : 0));
|
|
130
|
+
return resolved.executable ? 0 : 2;
|
|
131
|
+
}
|
|
132
|
+
if (!resolved.executable) {
|
|
133
|
+
io.error(JSON.stringify(resolved.envelope));
|
|
134
|
+
return 2;
|
|
135
|
+
}
|
|
136
|
+
const child = spawnSync(resolved.executable, [...resolved.prefixArgs, ...args], {
|
|
137
|
+
cwd: options.cwd ?? process.cwd(),
|
|
138
|
+
env: { ...process.env, AGDF_MACHINE_VALIDATION: resolved.envelope.machine_validation },
|
|
139
|
+
stdio: "inherit",
|
|
140
|
+
});
|
|
141
|
+
return child.status ?? 2;
|
|
142
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { createValidationHandlers } from "../cli/validation-handlers.js";
|
|
3
|
+
import { validateCommandOptions } from "../cli/command-registry.js";
|
|
4
|
+
import { CliUsageError, parseArgs } from "../cli/parse-args.js";
|
|
5
|
+
import { pluginDefinition } from "../cli/runtime-context.js";
|
|
6
|
+
|
|
7
|
+
const validatorCommands = new Set(["doctor", "gate-check", "delivery-map", "delivery-path-search"]);
|
|
8
|
+
|
|
9
|
+
export async function runValidatorCli(argv = process.argv.slice(2), adapters = {}) {
|
|
10
|
+
const io = adapters.io ?? console;
|
|
11
|
+
if (argv.includes("--version")) {
|
|
12
|
+
const output = { name: "create-agdf", version: pluginDefinition.version };
|
|
13
|
+
io.log(argv.includes("--json") ? JSON.stringify(output) : output.version);
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = parseArgs(argv, adapters.parser);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (!(error instanceof CliUsageError)) throw error;
|
|
21
|
+
io.error(error.message);
|
|
22
|
+
return error.exitCode;
|
|
23
|
+
}
|
|
24
|
+
if (parsed.kind === "help") {
|
|
25
|
+
io.log("AGDF surface-local validator: doctor | gate-check | delivery-map | delivery-path-search");
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
let options;
|
|
29
|
+
try {
|
|
30
|
+
options = validateCommandOptions(parsed.options);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
io.error(error instanceof Error ? error.message : String(error));
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
if (!validatorCommands.has(options.target)) {
|
|
36
|
+
io.error(`The surface-local validator does not support lifecycle command: ${options.target}`);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
return await createValidationHandlers(io).get(options.target)(options);
|
|
40
|
+
}
|
package/lib/scaffold/plan.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { generatedRoot, languageConfigContent, pluginDefinition } from "../cli/runtime-context.js";
|
|
4
|
+
export { doctorRequiredFiles } from "../control-evaluation/required-files.js";
|
|
4
5
|
|
|
5
6
|
export const agdfFragmentPath = "AGENTS.agdf.md";
|
|
6
|
-
export const openCodeConfigFragmentPath = "opencode.agdf.json";
|
|
7
7
|
const codexSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.codex.skillPrefix}${skill.slug}`);
|
|
8
8
|
const copilotSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.copilot.skillPrefix}${skill.slug}`);
|
|
9
|
-
const openCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.skillPrefix}${skill.slug}`);
|
|
10
9
|
const globalOpenCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.globalSkillPrefix}${skill.slug}`);
|
|
11
10
|
const contractModules = [
|
|
12
11
|
"gate-transition.md",
|
|
@@ -94,13 +93,6 @@ const artefactTemplateFiles = [
|
|
|
94
93
|
join(".agdf", "control", "templates", "artefacts", "QA_REPORT.md"),
|
|
95
94
|
join(".agdf", "control", "templates", "artefacts", "OR.md"),
|
|
96
95
|
];
|
|
97
|
-
export const doctorRequiredFiles = [
|
|
98
|
-
join(".agdf", "control", "AGDF_RUN.md"),
|
|
99
|
-
join(".agdf", "control", "MASTER_BACKLOG.md"),
|
|
100
|
-
join(".agdf", "control", "SOT_REGISTRY.md"),
|
|
101
|
-
join(".agdf", "control", "CONTEXT_GRAPH.md"),
|
|
102
|
-
join(".agdf", "control", "AGENT_QUALITY_CONTRACTS.json"),
|
|
103
|
-
];
|
|
104
96
|
const copilotInstructionFiles = [
|
|
105
97
|
join(".github", "copilot-instructions.md"),
|
|
106
98
|
join(".github", "instructions", "agdf-governance.instructions.md"),
|
|
@@ -111,13 +103,6 @@ const copilotSkillFiles = [
|
|
|
111
103
|
...contractModules.map((moduleName) => join(".github", "skills", "contracts", moduleName)),
|
|
112
104
|
...copilotSkillNames.map((skillName) => join(".github", "skills", skillName, "SKILL.md")),
|
|
113
105
|
];
|
|
114
|
-
const openCodeFiles = [
|
|
115
|
-
join(".opencode", pluginDefinition.opencode.instructionsFileName),
|
|
116
|
-
join(".opencode", "README.md"),
|
|
117
|
-
join(".opencode", pluginDefinition.opencode.runtimeContractFileName),
|
|
118
|
-
...contractModules.map((moduleName) => join(".opencode", "contracts", moduleName)),
|
|
119
|
-
...openCodeSkillNames.map((skillName) => join(".opencode", "skills", skillName, "SKILL.md")),
|
|
120
|
-
];
|
|
121
106
|
|
|
122
107
|
function loadAsset(relativePath) {
|
|
123
108
|
return readFileSync(join(generatedRoot, relativePath), "utf8");
|
|
@@ -252,19 +237,6 @@ export function generatedFilesForTarget(target, targetDir, force, languagePrefer
|
|
|
252
237
|
|
|
253
238
|
if (target === "opencode-repo") {
|
|
254
239
|
addLanguageConfig(files, languagePreference);
|
|
255
|
-
const openCodeConfigTargetPath = existsSync(join(targetDir, "opencode.json")) && !force ? openCodeConfigFragmentPath : "opencode.json";
|
|
256
|
-
files.push({
|
|
257
|
-
path: openCodeConfigTargetPath,
|
|
258
|
-
content: loadAsset("opencode.json"),
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
for (const openCodePath of openCodeFiles) {
|
|
262
|
-
files.push({
|
|
263
|
-
path: openCodePath,
|
|
264
|
-
content: loadAsset(openCodePath),
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
240
|
for (const controlPath of controlFiles) {
|
|
269
241
|
files.push({
|
|
270
242
|
path: controlPath,
|
|
@@ -275,4 +247,3 @@ export function generatedFilesForTarget(target, targetDir, force, languagePrefer
|
|
|
275
247
|
|
|
276
248
|
return files;
|
|
277
249
|
}
|
|
278
|
-
|
|
@@ -3,11 +3,11 @@ import { join } from "node:path";
|
|
|
3
3
|
import { pluginDefinition } from "../cli/runtime-context.js";
|
|
4
4
|
import { printLifecycleResult } from "../lifecycle/presentation.js";
|
|
5
5
|
import { createLifecycleResult } from "../lifecycle/result.js";
|
|
6
|
-
import { agdfFragmentPath
|
|
6
|
+
import { agdfFragmentPath } from "./plan.js";
|
|
7
7
|
|
|
8
8
|
const pluginInstallCommand = "npx --yes @agdf/cli@latest claude";
|
|
9
9
|
|
|
10
|
-
export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
10
|
+
export function printNextSteps(target, destination, files, wroteAgentsFragment, removedOpenCodeAgents = [], { verbose = false, json = false, io = console } = {}) {
|
|
11
11
|
const repositorySurface = { "codex-repo": "codex", "opencode-repo": "opencode", copilot: "copilot" }[target];
|
|
12
12
|
const verified = files.every((file) => {
|
|
13
13
|
const path = join(destination, file.path);
|
|
@@ -17,7 +17,7 @@ export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
|
17
17
|
const nextAction = repositorySurface === "codex"
|
|
18
18
|
? "Restart Codex, open /plugins, select This repository and install agdf; then start a new task with: Run an AGDF gate check for this request."
|
|
19
19
|
: repositorySurface === "opencode"
|
|
20
|
-
? "Restart OpenCode in this repository so
|
|
20
|
+
? "Restart OpenCode in this repository so the global AGDF plugin observes the durable control configuration."
|
|
21
21
|
: "Run /instructions in GitHub Copilot CLI to confirm that the generated AGDF repository instructions are visible.";
|
|
22
22
|
printLifecycleResult(createLifecycleResult({
|
|
23
23
|
operation: "repository_setup",
|
|
@@ -70,13 +70,13 @@ export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
|
70
70
|
}
|
|
71
71
|
if (target === "init") {
|
|
72
72
|
io.log("- Create or migrate a canonical run, then fill its RUN_STATE.md with the current gate, evidence and next allowed action.");
|
|
73
|
-
io.log("- Run
|
|
73
|
+
io.log("- Run the installed `agdf doctor` to check the control state before the next agent run.");
|
|
74
74
|
io.log("- Commit the live control files once they represent the repository's current delivery state.");
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
if (target === "config") {
|
|
78
78
|
io.log("- Restart or start a new agent session so the AGDF SessionStart hook reads the updated project language config.");
|
|
79
|
-
io.log("- Run
|
|
79
|
+
io.log("- Run the installed `agdf doctor` when this repository also uses durable AGDF control state.");
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
if (wroteAgentsFragment) {
|
|
@@ -90,15 +90,10 @@ export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
|
90
90
|
io.log(`- Optional global Claude Code install: ${pluginInstallCommand}`);
|
|
91
91
|
}
|
|
92
92
|
if (target === "opencode-repo") {
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
io.log(
|
|
97
|
-
io.log("- Optional: also add create-agdf to ~/.config/opencode/opencode.json plugin[] for a user-wide OpenCode hook.");
|
|
98
|
-
io.log("- The global hook does not replace repository instructions or native skills; this repository's .opencode files remain the AGDF source of truth.");
|
|
99
|
-
io.log("- Start OpenCode in this repository; it will load opencode.json, .opencode/AGDF.md and the native AGDF skills.");
|
|
100
|
-
io.log("- Load agdf-gate-check through OpenCode's native skill tool for new build/change intent or unclear approval before later artefacts or implementation.");
|
|
101
|
-
io.log("- Run npx --yes @agdf/cli@latest init when the repository needs live AGDF control files.");
|
|
93
|
+
io.log("- Install the global OpenCode surface once with npx --yes @agdf/cli@latest opencode if it is not already installed.");
|
|
94
|
+
io.log("- Start or restart OpenCode in this repository; valid .agdf/control/config.json activates the global AGDF runtime here.");
|
|
95
|
+
io.log("- Load agdf-global-gate-check through OpenCode's native skill tool for new build/change intent or unclear approval before later artefacts or implementation.");
|
|
96
|
+
io.log("- Existing .opencode files are left untouched as a compatibility path; this command does not copy a second runtime surface.");
|
|
102
97
|
}
|
|
103
98
|
if (target === "copilot" || target === "both") {
|
|
104
99
|
io.log("- In GitHub Copilot CLI, run /instructions after the AGENTS.md step is complete to confirm that AGDF instructions and the repository skills are visible.");
|
package/opencode-plugin.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { evaluateOpenCodeRepositoryActivation } from "./lib/installers/opencode-activation.js";
|
|
2
3
|
|
|
3
4
|
const packageJson = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
4
5
|
|
|
5
6
|
export const AGDFPlugin = async ({ directory, client }) => {
|
|
6
7
|
const controlDir = `${directory}/.agdf/control`;
|
|
7
|
-
const
|
|
8
|
-
const status = () =>
|
|
9
|
-
|
|
8
|
+
const activation = () => evaluateOpenCodeRepositoryActivation(directory);
|
|
9
|
+
const status = () => {
|
|
10
|
+
const repositoryActivation = activation();
|
|
11
|
+
return {
|
|
12
|
+
active: repositoryActivation.active,
|
|
10
13
|
version: packageJson.version,
|
|
11
14
|
controlDir,
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
repositoryActivation: repositoryActivation.state,
|
|
16
|
+
repositorySurface: repositoryActivation.legacy_surface,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const activeGuidance = [
|
|
21
|
+
"## AGDF Runtime Reminder",
|
|
22
|
+
"This repository is AGDF-active through `.agdf/control/config.json`; use the globally installed AGDF runtime surface.",
|
|
23
|
+
"For new build/change intent or unclear approval, load the native `agdf-global-gate-check` skill before later artefacts or implementation.",
|
|
24
|
+
"Use the config-local `agdf/bin/agdf-local.js gate-check --status-card` for compact interactive status. Use `--json` only as deterministic proof for automation or audit evidence, and summarize it instead of mirroring full JSON into chat.",
|
|
25
|
+
].join("\n");
|
|
26
|
+
|
|
27
|
+
const inactiveGuidance = [
|
|
28
|
+
"## AGDF Plugin Notice",
|
|
29
|
+
"The AGDF OpenCode npm plugin is loaded, but this repository has no valid `.agdf/control/config.json`.",
|
|
30
|
+
"Do not apply AGDF gates from the global plugin alone. Create or repair durable AGDF control state with `npx --yes @agdf/cli@latest opencode-repo` when governance should be active here.",
|
|
31
|
+
].join("\n");
|
|
14
32
|
|
|
15
33
|
return {
|
|
16
34
|
event: async ({ event }) => {
|
|
@@ -20,7 +38,7 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
20
38
|
body: {
|
|
21
39
|
service: "agdf",
|
|
22
40
|
level: "info",
|
|
23
|
-
message: currentStatus.
|
|
41
|
+
message: currentStatus.active ? "AGDF OpenCode active through durable control" : "AGDF OpenCode global hook active without durable control",
|
|
24
42
|
extra: currentStatus,
|
|
25
43
|
},
|
|
26
44
|
});
|
|
@@ -29,29 +47,19 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
29
47
|
|
|
30
48
|
"shell.env": async (_input, output) => {
|
|
31
49
|
const currentStatus = status();
|
|
32
|
-
output.env.AGDF_PLUGIN_ACTIVE = "1";
|
|
50
|
+
output.env.AGDF_PLUGIN_ACTIVE = currentStatus.active ? "1" : "0";
|
|
33
51
|
output.env.AGDF_PLUGIN_VERSION = currentStatus.version;
|
|
34
52
|
output.env.AGDF_CONTROL_DIR = controlDir;
|
|
35
53
|
output.env.AGDF_OPENCODE_REPOSITORY_SURFACE = currentStatus.repositorySurface ? "1" : "0";
|
|
54
|
+
output.env.AGDF_OPENCODE_REPOSITORY_ACTIVATION = currentStatus.repositoryActivation;
|
|
36
55
|
},
|
|
37
56
|
|
|
38
|
-
"experimental.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"## AGDF Plugin Notice",
|
|
42
|
-
"The AGDF OpenCode npm plugin is loaded, but this repository does not contain the AGDF OpenCode surface.",
|
|
43
|
-
"Do not apply AGDF gates from the global plugin alone; repository instructions, native skills and control files are the source of truth.",
|
|
44
|
-
"Run `npx --yes @agdf/cli@latest opencode-repo` in this repository when AGDF governance should be active here.",
|
|
45
|
-
].join("\n"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
57
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
58
|
+
output.system.push(activation().active ? activeGuidance : inactiveGuidance);
|
|
59
|
+
},
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"Use `.opencode/AGDF.md`, `.opencode/skills/` and `.agdf/control/` as the AGDF source for this OpenCode session.",
|
|
52
|
-
"For new build/change intent or unclear approval, load the native `agdf-gate-check` skill before later artefacts or implementation.",
|
|
53
|
-
"Use `npx --yes @agdf/cli@latest gate-check --status-card` for compact interactive status. Use `--json` only as deterministic proof for automation or audit evidence, and summarize it instead of mirroring full JSON into chat.",
|
|
54
|
-
].join("\n"));
|
|
61
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
62
|
+
output.context.push(activation().active ? activeGuidance : inactiveGuidance);
|
|
55
63
|
},
|
|
56
64
|
};
|
|
57
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-agdf",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Bootstrap AGDF repository-local plugin files, Copilot/OpenCode instructions, skills, control files and machine-readable checks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"test:delivery-path-search-unit": "node ./scripts/delivery-path-search-unit-test.js",
|
|
26
26
|
"test:delivery-path-search-generator": "node ./scripts/delivery-path-search-generator-test.js",
|
|
27
27
|
"test:cli-modularization": "node ./scripts/cli-modularization-test.js",
|
|
28
|
+
"test:local-validator": "node ./scripts/local-validator-test.js",
|
|
29
|
+
"test:local-marketplace": "node ./scripts/local-marketplace-test.js",
|
|
30
|
+
"test:package-contents": "node ./scripts/package-contents-test.js",
|
|
31
|
+
"test:package-build": "node ./scripts/package-build-test.js",
|
|
28
32
|
"test:lifecycle": "node ./scripts/lifecycle-test.js",
|
|
29
33
|
"test:control-state": "node ./scripts/control-state-test.js",
|
|
30
34
|
"test:interaction-presentation": "node ./scripts/interaction-presentation-test.js",
|
|
@@ -36,7 +40,7 @@
|
|
|
36
40
|
"eval:skills:record": "node ./scripts/record-skill-evals.js",
|
|
37
41
|
"test:release-bootstrap": "node ./scripts/release-bootstrap-smoke-test.js",
|
|
38
42
|
"test:routing": "npm run sync-package-assets && node ./scripts/test-routing.js",
|
|
39
|
-
"smoke-test": "npm run sync-package-assets && npm run test:cli-modularization && npm run test:lifecycle && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:skill-evals && npm run eval:skills && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
|
|
43
|
+
"smoke-test": "npm run sync-package-assets && npm run test:cli-modularization && npm run test:local-validator && npm run test:local-marketplace && npm run test:package-build && npm run test:package-contents && npm run test:lifecycle && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:skill-evals && npm run eval:skills && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
|
|
40
44
|
"prepack": "npm run sync-package-assets"
|
|
41
45
|
},
|
|
42
46
|
"keywords": [
|