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.
Files changed (89) hide show
  1. package/README.md +30 -22
  2. package/bin/agdf-validator.js +11 -0
  3. package/generated/.agdf/control/README.md +3 -3
  4. package/generated/.github/skills/agdf-brownfield-analysis/SKILL.md +1 -1
  5. package/generated/.github/skills/agdf-delivery-path-search/SKILL.md +7 -6
  6. package/generated/.github/skills/agdf-gate-check/SKILL.md +28 -91
  7. package/generated/.github/skills/agdf-interaction-locales.json +2 -0
  8. package/generated/.github/skills/contracts/gate-transition.md +4 -2
  9. package/generated/.github/skills/contracts/modes.md +12 -1
  10. package/generated/.opencode/README.md +6 -8
  11. package/generated/.opencode/agdf-interaction-locales.json +2 -0
  12. package/generated/.opencode/contracts/gate-transition.md +4 -2
  13. package/generated/.opencode/contracts/modes.md +12 -1
  14. package/generated/.opencode/skills/agdf-brownfield-analysis/SKILL.md +1 -1
  15. package/generated/.opencode/skills/agdf-delivery-path-search/SKILL.md +7 -6
  16. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +28 -91
  17. package/generated/plugins/agdf/.claude-plugin/plugin.json +19 -0
  18. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  19. package/generated/plugins/agdf/control/README.md +3 -3
  20. package/generated/plugins/agdf/hooks/session-start.sh +1 -1
  21. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +2 -0
  22. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  23. package/generated/plugins/agdf/meta/contracts/gate-transition.md +4 -2
  24. package/generated/plugins/agdf/meta/contracts/modes.md +12 -1
  25. package/generated/plugins/agdf/runtime/agdf-local.js +11 -0
  26. package/generated/plugins/agdf/runtime/create-agdf/NOTICE +10 -0
  27. package/generated/plugins/agdf/runtime/create-agdf/bin/agdf-validator.js +11 -0
  28. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +418 -0
  29. package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +220 -0
  30. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/command-registry.js +136 -0
  31. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +75 -0
  32. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/parse-args.js +156 -0
  33. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/runtime-context.js +83 -0
  34. package/generated/plugins/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +45 -0
  35. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +291 -0
  36. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +265 -0
  37. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +459 -0
  38. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +283 -0
  39. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/required-files.js +9 -0
  40. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +217 -0
  41. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +344 -0
  42. package/generated/plugins/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js +156 -0
  43. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/aggregate.js +18 -0
  44. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/gate-approval-validator.js +49 -0
  45. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/index.js +7 -0
  46. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js +95 -0
  47. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +227 -0
  48. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js +68 -0
  49. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-resolver.js +45 -0
  50. package/generated/plugins/agdf/runtime/create-agdf/lib/control-state/run-state-writer.js +97 -0
  51. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/candidate-policy.js +99 -0
  52. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +237 -0
  53. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +44 -0
  54. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +53 -0
  55. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/protocol.js +13 -0
  56. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/claude.js +40 -0
  57. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/codex.js +38 -0
  58. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/generators/protocol.js +12 -0
  59. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +39 -0
  60. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js +21 -0
  61. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +154 -0
  62. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +161 -0
  63. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +14 -0
  64. package/generated/plugins/agdf/runtime/create-agdf/lib/delivery-path-search/transports/read-only-guard.js +43 -0
  65. package/generated/plugins/agdf/runtime/create-agdf/lib/interaction-presentation.js +590 -0
  66. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +142 -0
  67. package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/validator-application.js +40 -0
  68. package/generated/plugins/agdf/runtime/create-agdf/package.json +66 -0
  69. package/generated/plugins/agdf/runtime/runtime-manifest.json +8 -0
  70. package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +1040 -0
  71. package/generated/plugins/agdf/skills/brownfield-analysis/SKILL.md +1 -1
  72. package/generated/plugins/agdf/skills/delivery-path-search/SKILL.md +7 -6
  73. package/generated/plugins/agdf/skills/gate-check/SKILL.md +28 -91
  74. package/lib/cli/application.js +21 -59
  75. package/lib/cli/command-registry.js +9 -11
  76. package/lib/cli/validation-handlers.js +45 -0
  77. package/lib/control-evaluation/doctor.js +1 -1
  78. package/lib/control-evaluation/gate-check.js +7 -7
  79. package/lib/control-evaluation/required-files.js +9 -0
  80. package/lib/installers/local-marketplace.js +322 -0
  81. package/lib/installers/opencode-activation.js +43 -0
  82. package/lib/installers/opencode.js +60 -18
  83. package/lib/installers/plugin-installers.js +102 -26
  84. package/lib/runtime/local-validator.js +142 -0
  85. package/lib/runtime/validator-application.js +40 -0
  86. package/lib/scaffold/plan.js +1 -30
  87. package/lib/scaffold/presentation.js +9 -14
  88. package/opencode-plugin.js +32 -24
  89. 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, io = console } = {}) {
5
+ export function installCodexGlobalPlugin({ exec = execFileSync, prepare = prepareLocalMarketplace, dataRoot } = {}) {
5
6
  const expectedVersion = pluginDefinition.version;
6
7
  const nativeOutput = [];
7
- nativeOutput.push(runPluginPhase(exec, "codex", ["plugin", "marketplace", "add", "arndtgold/ai-native-governance-delivery-framework"], "marketplace", captureOptions()));
8
- nativeOutput.push(runPluginPhase(exec, "codex", ["plugin", "marketplace", "upgrade", "agdf"], "marketplace", captureOptions()));
9
- nativeOutput.push(runPluginPhase(exec, "codex", ["plugin", "add", "agdf", "--marketplace", "agdf"], "plugin_operation", captureOptions()));
10
- const listOutput = runPluginPhase(exec, "codex", ["plugin", "list"], "verification", { encoding: "utf8", stdio: "pipe" });
11
- const installedVersion = pluginVersionFromList(listOutput, "agdf@agdf");
12
- if (installedVersion !== expectedVersion) {
13
- throw lifecycleAdapterError("version", versionMismatchMessage("Codex", "agdf@agdf", expectedVersion, installedVersion, "codex plugin marketplace upgrade agdf && codex plugin add agdf --marketplace agdf"));
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, io = console } = {}) {
31
+ export function installClaudeGlobalPlugin({ exec = execFileSync, prepare = prepareLocalMarketplace, dataRoot } = {}) {
19
32
  const expectedVersion = pluginDefinition.version;
20
33
  const nativeOutput = [];
21
- nativeOutput.push(runPluginPhase(exec, "claude", ["plugin", "marketplace", "add", "arndtgold/ai-native-governance-delivery-framework"], "marketplace", captureOptions()));
22
- nativeOutput.push(runPluginPhase(exec, "claude", ["plugin", "marketplace", "update", "agdf"], "marketplace", captureOptions()));
23
- const beforeList = runPluginPhase(exec, "claude", ["plugin", "list"], "verification", { encoding: "utf8", stdio: "pipe" });
24
- const alreadyInstalled = pluginListHasPlugin(beforeList, "agdf@agdf");
25
- nativeOutput.push(runPluginPhase(exec, "claude", ["plugin", alreadyInstalled ? "update" : "install", "agdf@agdf"], "plugin_operation", captureOptions()));
26
- const afterList = runPluginPhase(exec, "claude", ["plugin", "list"], "verification", { encoding: "utf8", stdio: "pipe" });
27
- const installedVersion = pluginVersionFromList(afterList, "agdf@agdf");
28
- if (installedVersion && installedVersion !== expectedVersion) {
29
- throw lifecycleAdapterError("version", versionMismatchMessage("Claude Code", "agdf@agdf", expectedVersion, installedVersion, "claude plugin marketplace update agdf && claude plugin update agdf@agdf"));
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
- return {
32
- surface: "claude",
33
- expectedVersion,
34
- installedVersion,
35
- verificationStatus: installedVersion ? "healthy" : "degraded",
36
- evidence: ["claude plugin list", ...(installedVersion ? [] : ["host_did_not_expose_version"])],
37
- nativeOutput: nativeOutput.filter(Boolean).map(String),
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
+ }
@@ -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, openCodeConfigFragmentPath } from "./plan.js";
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, wroteOpenCodeConfigFragment, removedOpenCodeAgents = [], { verbose = false, json = false, io = console } = {}) {
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 it loads the generated AGDF repository configuration."
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 npx --yes @agdf/cli@latest doctor to check the control state before the next agent 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 npx --yes @agdf/cli@latest doctor when this repository also uses durable AGDF control state.");
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 (wroteOpenCodeConfigFragment) {
94
- io.log(`- Existing opencode.json detected. Review ${openCodeConfigFragmentPath} and merge its owned entries so OpenCode loads .opencode/AGDF.md; preserve an explicit permission.question decision.`);
95
- }
96
- io.log(`- OpenCode will install the ${pluginDefinition.opencode.npmPackage} npm plugin from opencode.json at startup.`);
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.");
@@ -1,16 +1,34 @@
1
- import { existsSync, readFileSync } from "node:fs";
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 hasRepositorySurface = () => existsSync(`${directory}/.opencode/AGDF.md`) && existsSync(`${directory}/.opencode/skills/agdf-gate-check/SKILL.md`);
8
- const status = () => ({
9
- active: true,
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
- repositorySurface: hasRepositorySurface(),
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.repositorySurface ? "AGDF OpenCode active" : "AGDF OpenCode global hook active without repository surface",
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.session.compacting": async (_input, output) => {
39
- if (!hasRepositorySurface()) {
40
- output.context.push([
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
- output.context.push([
50
- "## AGDF Runtime Reminder",
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.0",
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": [