create-agdf 0.9.1 → 0.9.5

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 (37) hide show
  1. package/README.md +33 -4
  2. package/bin/create-agdf.js +2 -3333
  3. package/generated/.agdf/control/templates/MASTER_BACKLOG.md +3 -3
  4. package/generated/.github/skills/agdf-gate-check/SKILL.md +5 -0
  5. package/generated/.github/skills/agdf-interaction-locales.json +36 -0
  6. package/generated/.github/skills/contracts/interaction.md +9 -1
  7. package/generated/.opencode/agdf-interaction-locales.json +36 -0
  8. package/generated/.opencode/contracts/interaction.md +9 -1
  9. package/generated/.opencode/skills/agdf-gate-check/SKILL.md +5 -0
  10. package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
  11. package/generated/plugins/agdf/control/templates/MASTER_BACKLOG.md +3 -3
  12. package/generated/plugins/agdf/meta/agdf-interaction-locales.json +36 -0
  13. package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
  14. package/generated/plugins/agdf/meta/contracts/interaction.md +9 -1
  15. package/generated/plugins/agdf/skills/gate-check/SKILL.md +5 -0
  16. package/lib/cli/application.js +342 -0
  17. package/lib/cli/command-registry.js +119 -0
  18. package/lib/cli/delivery-path-search-command.js +75 -0
  19. package/lib/cli/parse-args.js +150 -0
  20. package/lib/cli/runtime-context.js +83 -0
  21. package/lib/control-evaluation/delivery-map.js +291 -0
  22. package/lib/control-evaluation/doctor.js +265 -0
  23. package/lib/control-evaluation/gate-check.js +404 -0
  24. package/lib/control-evaluation/gate-policy.js +283 -0
  25. package/lib/control-evaluation/run-state.js +217 -0
  26. package/lib/control-evaluation/shared.js +344 -0
  27. package/lib/control-evaluation/verified-change.js +156 -0
  28. package/lib/installers/opencode.js +443 -0
  29. package/lib/installers/plugin-installers.js +99 -0
  30. package/lib/lifecycle/operations.js +177 -0
  31. package/lib/lifecycle/presentation.js +52 -0
  32. package/lib/lifecycle/result.js +69 -0
  33. package/lib/lifecycle/status.js +108 -0
  34. package/lib/scaffold/plan.js +278 -0
  35. package/lib/scaffold/presentation.js +98 -0
  36. package/lib/scaffold/write.js +45 -0
  37. package/package.json +4 -2
@@ -0,0 +1,119 @@
1
+ function command(name, usages) {
2
+ return Object.freeze({
3
+ name,
4
+ handler: name,
5
+ groups: Object.freeze(Object.keys(usages)),
6
+ usages: Object.freeze(Object.fromEntries(
7
+ Object.entries(usages).map(([group, suffixes]) => [group, Object.freeze(suffixes)]),
8
+ )),
9
+ });
10
+ }
11
+
12
+ export const commandRegistry = Object.freeze([
13
+ command("codex", { preferred: [""], scaffold: [""] }),
14
+ command("codex-repo", { preferred: [""], scaffold: [""] }),
15
+ command("claude", { preferred: [""], scaffold: [""] }),
16
+ command("copilot", { preferred: [""], scaffold: [""] }),
17
+ command("opencode", { preferred: [""], scaffold: [""] }),
18
+ command("opencode-status", { preferred: [""], scaffold: [""] }),
19
+ command("status", { preferred: [" [--surface <surface>] [--run <run_id>] [--json]"] }),
20
+ command("disable", { preferred: [" --surface <surface> [--scope repository] [--dir <path>]"] }),
21
+ command("uninstall", { preferred: [" --surface <surface> --scope global [--confirm]"] }),
22
+ command("opencode-repo", { preferred: [""], scaffold: [""] }),
23
+ command("both", { scaffold: [""] }),
24
+ command("init", { preferred: [""], scaffold: [""] }),
25
+ command("config", { scaffold: [" --language de"] }),
26
+ command("doctor", { preferred: [""], scaffold: [""], legacy: [" --json"] }),
27
+ command("gate-check", { preferred: [" --json"], scaffold: [""], legacy: [" --json"] }),
28
+ command("delivery-map", { scaffold: [""] }),
29
+ command("delivery-path-search", {
30
+ preferred: [" --surface codex --json", " --surface claude --json"],
31
+ scaffold: [" --surface codex", " --surface claude"],
32
+ }),
33
+ command("run-create", { preferred: [" --run <run_id>"] }),
34
+ command("run-migrate", { preferred: [" [--run <run_id>]"] }),
35
+ command("run-render-legacy", { preferred: [" --run <run_id>"] }),
36
+ ]);
37
+
38
+ const commandByName = new Map(commandRegistry.map((entry) => [entry.name, entry]));
39
+
40
+ export function resolveCommand(name) {
41
+ return commandByName.get(name) ?? null;
42
+ }
43
+
44
+ export function supportedCommandNames() {
45
+ return commandRegistry.map((entry) => entry.name);
46
+ }
47
+
48
+ export function validateCommandOptions(options) {
49
+ if (options.allActive && !["doctor", "delivery-map"].includes(options.target)) {
50
+ throw new Error("--all-active is supported only by doctor and delivery-map");
51
+ }
52
+ if (options.target === "run-create" && (!options.runId || options.allActive)) {
53
+ throw new Error("run-create requires --run and rejects --all-active");
54
+ }
55
+ if (options.target === "run-render-legacy" && !options.runId) {
56
+ throw new Error("run-render-legacy requires --run");
57
+ }
58
+ if (["disable", "uninstall"].includes(options.target) && ["", "generic", undefined].includes(options.surface)) {
59
+ throw new Error(`${options.target} requires an explicit --surface`);
60
+ }
61
+ if (options.target === "disable" && options.scope && options.scope !== "repository") {
62
+ throw new Error("disable supports repository scope only");
63
+ }
64
+ if (options.target === "uninstall" && options.scope !== "global") {
65
+ throw new Error("uninstall requires explicit --scope global");
66
+ }
67
+ if (options.confirm && options.target !== "uninstall") throw new Error("--confirm is supported only by uninstall");
68
+ return options;
69
+ }
70
+
71
+ function usageLines(group, prefix) {
72
+ return commandRegistry
73
+ .flatMap((entry) => (entry.usages[group] ?? []).map((suffix) => ` ${prefix}${entry.name}${suffix}`))
74
+ .join("\n");
75
+ }
76
+
77
+ export function renderUsage() {
78
+ return `AGDF CLI
79
+
80
+ Primary commands:
81
+ ${usageLines("preferred", "npx --yes @agdf/cli@latest ")}
82
+
83
+ Advanced / Compatibility
84
+
85
+ Scaffold-compatible npm create usage:
86
+ ${usageLines("scaffold", "npm create agdf@latest -- ")}
87
+
88
+ Backward-compatible create-agdf usage:
89
+ ${usageLines("legacy", "npx --yes create-agdf@latest ")}
90
+
91
+ Options:
92
+ --dir <path> Write files into a specific directory. With opencode, use this as the OpenCode config directory.
93
+ --force Overwrite existing generated files
94
+ --language <tag>
95
+ Set AGDF chat and artefact language. Defaults to detected system locale.
96
+ --lang <tag> Alias for --language
97
+ --json Print machine-readable command output as JSON
98
+ --status-card Print compact gate-check status-card output for interactive use
99
+ --run <run_id> Select one canonical run
100
+ --all-active Evaluate every active run (doctor and delivery-map only)
101
+ --surface <codex|claude|copilot|opencode|generic>
102
+ Declare the active Delivery Path Search surface
103
+ --scope <repository|global>
104
+ Select the lifecycle mutation scope
105
+ --confirm Apply a previously previewed global uninstall plan
106
+ --fixture <path>
107
+ Use deterministic evaluator/candidate fixtures instead of a live evaluator
108
+ --persist Persist the redacted Delivery Path Search result under the current scope
109
+ --model <id> Optional Codex evaluator model
110
+ --generate-candidates
111
+ Add one bounded AI-native candidate-generation call before evaluation
112
+ --generator-model <id>
113
+ Optional candidate-generator model override
114
+ --max-generated-candidates <1-5>
115
+ --generation-timeout-ms <1-30000>
116
+ --generation-cost-units <1-5>
117
+ --help Show this help
118
+ `;
119
+ }
@@ -0,0 +1,75 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { searchInputFromControl } from '../delivery-path-search/state-adapter.js';
3
+ import { runDeliveryPathSearch } from '../delivery-path-search/search-engine.js';
4
+ import { codexEvaluator } from '../delivery-path-search/evaluators/codex.js';
5
+ import { claudeEvaluator } from '../delivery-path-search/evaluators/claude.js';
6
+ import { fixtureEvaluator } from '../delivery-path-search/evaluators/protocol.js';
7
+ import { codexGenerator } from '../delivery-path-search/generators/codex.js';
8
+ import { claudeGenerator } from '../delivery-path-search/generators/claude.js';
9
+ import { fixtureGenerator } from '../delivery-path-search/generators/protocol.js';
10
+ import { enforcementForSurface } from '../delivery-path-search/surfaces/capabilities.js';
11
+ import { persistSearchResult } from '../delivery-path-search/persistence.js';
12
+
13
+ export async function executeDeliveryPathSearch(options, io = console) {
14
+ const fixture = options.fixture ? JSON.parse(readFileSync(options.fixture, "utf8")) : null;
15
+ const enforcement = fixture?.input?.enforcement ?? enforcementForSurface(options.surface);
16
+ const fixtureGeneration = fixture?.generator_response;
17
+ const generationEnabled = Boolean(fixtureGeneration || options.generateCandidates);
18
+ const baseInput = fixture?.input ?? searchInputFromControl(options.dir, {
19
+ scopeKey: options.runId,
20
+ enforcement,
21
+ generation: generationEnabled ? {
22
+ enabled: true,
23
+ maxProposals: options.maxGeneratedCandidates,
24
+ maxDurationMs: options.generationTimeoutMs,
25
+ maxCostUnits: options.generationCostUnits,
26
+ } : undefined,
27
+ });
28
+ const input = generationEnabled && !baseInput.generation ? {
29
+ ...baseInput,
30
+ generation: {
31
+ enabled: true,
32
+ max_calls: 1,
33
+ max_proposals: options.maxGeneratedCandidates,
34
+ max_duration_ms: options.generationTimeoutMs,
35
+ max_cost_units: options.generationCostUnits,
36
+ },
37
+ } : baseInput;
38
+ const evaluator = fixture
39
+ ? fixtureEvaluator(fixture.evaluations ?? {})
40
+ : options.surface === "codex"
41
+ ? codexEvaluator({ cwd: options.dir, model: options.model })
42
+ : options.surface === "claude"
43
+ ? claudeEvaluator({ cwd: options.dir, model: options.model })
44
+ : null;
45
+ if (!evaluator) {
46
+ throw new Error(`${options.surface} has no executable evaluator in this release. Codex and Claude are the reference adapters; --fixture is available for deterministic contract testing.`);
47
+ }
48
+ const generator = fixtureGeneration
49
+ ? fixtureGenerator(fixtureGeneration)
50
+ : options.generateCandidates && options.surface === "codex"
51
+ ? codexGenerator({ cwd: options.dir, model: options.generatorModel, timeoutMs: options.generationTimeoutMs })
52
+ : options.generateCandidates && options.surface === "claude"
53
+ ? claudeGenerator({ cwd: options.dir, model: options.generatorModel, timeoutMs: options.generationTimeoutMs })
54
+ : null;
55
+ if (options.generateCandidates && !generator) {
56
+ throw new Error(`${options.surface} has no executable candidate generator; Codex and Claude are tool-enforced reference transports.`);
57
+ }
58
+ const result = await runDeliveryPathSearch(input, evaluator, { candidates: fixture?.candidates, generator });
59
+ if (options.persist) result.persistence = persistSearchResult(options.dir, result);
60
+ if (options.json) {
61
+ io.log(JSON.stringify(result, null, 2));
62
+ return result;
63
+ }
64
+ io.log(`AGDF Delivery Path Search: ${result.status}`);
65
+ io.log(`Current gate: ${result.current_gate}`);
66
+ io.log(`Enforcement: ${result.enforcement.level}`);
67
+ io.log(`Recommendation: ${result.recommendation?.action ?? "none"}`);
68
+ io.log(`Evaluations: ${result.budgets.evaluations}`);
69
+ io.log(`Generation: ${result.generation.status} (${result.generation.accepted}/${result.generation.returned} accepted)`);
70
+ io.log(`Generation budget: ${result.generation.cost_units} cost units, ${result.generation.duration_ms} ms`);
71
+ if (result.generation.failure_code) io.log(`Generation failure: ${result.generation.failure_code}`);
72
+ io.log(`Stopping reason: ${result.stopping_reason}`);
73
+ io.log(result.next_gate_action);
74
+ return result;
75
+ }
@@ -0,0 +1,150 @@
1
+ import { resolve } from "node:path";
2
+ import process from "node:process";
3
+ import { configuredLanguage, resolveLanguagePreference } from "./runtime-context.js";
4
+ import { resolveCommand, supportedCommandNames } from "./command-registry.js";
5
+
6
+ export class CliUsageError extends Error {
7
+ constructor(message, { showUsage = false } = {}) {
8
+ super(message);
9
+ this.name = "CliUsageError";
10
+ this.exitCode = 1;
11
+ this.showUsage = showUsage;
12
+ }
13
+ }
14
+
15
+ function requiredValue(args, index, option) {
16
+ const next = args[index + 1];
17
+ if (!next) throw new CliUsageError(`Missing value for ${option}`);
18
+ return next;
19
+ }
20
+
21
+ export function parseArgs(argv, dependencies = {}) {
22
+ const cwd = dependencies.cwd ?? process.cwd();
23
+ const languagePreference = dependencies.resolveLanguagePreference ?? resolveLanguagePreference;
24
+ const normalizeLanguage = dependencies.configuredLanguage ?? configuredLanguage;
25
+ const args = [...argv];
26
+ let target;
27
+ let dir = ".";
28
+ let force = false;
29
+ let json = false;
30
+ let statusCard = false;
31
+ let language;
32
+ let dirExplicit = false;
33
+ let surface = "generic";
34
+ let fixture;
35
+ let persist = false;
36
+ let model;
37
+ let generateCandidates = false;
38
+ let generatorModel;
39
+ let maxGeneratedCandidates = 5;
40
+ let generationTimeoutMs = 30000;
41
+ let generationCostUnits = 5;
42
+ let runId;
43
+ let allActive = false;
44
+ let scope;
45
+ let confirm = false;
46
+
47
+ for (let i = 0; i < args.length; i += 1) {
48
+ const arg = args[i];
49
+ if (!arg) continue;
50
+ if (arg === "--help" || arg === "-h") return { kind: "help" };
51
+ if (arg === "--force") { force = true; continue; }
52
+ if (arg === "--json") { json = true; continue; }
53
+ if (arg === "--status-card") { statusCard = true; continue; }
54
+ if (arg === "--persist") { persist = true; continue; }
55
+ if (arg === "--generate-candidates") { generateCandidates = true; continue; }
56
+ if (arg === "--all-active") { allActive = true; continue; }
57
+ if (arg === "--confirm") { confirm = true; continue; }
58
+
59
+ if (arg === "--run") {
60
+ runId = requiredValue(args, i, arg);
61
+ i += 1;
62
+ continue;
63
+ }
64
+
65
+ if (["--surface", "--scope", "--fixture", "--model", "--generator-model", "--max-generated-candidates", "--generation-timeout-ms", "--generation-cost-units"].includes(arg)) {
66
+ const next = requiredValue(args, i, arg);
67
+ if (arg === "--surface") {
68
+ if (!["codex", "claude", "copilot", "opencode", "generic"].includes(next)) {
69
+ throw new CliUsageError("Unsupported surface. Use codex, claude, copilot, opencode or generic.");
70
+ }
71
+ surface = next;
72
+ } else if (arg === "--scope") {
73
+ if (!["repository", "global"].includes(next)) throw new CliUsageError("Unsupported scope. Use repository or global.");
74
+ scope = next;
75
+ } else if (arg === "--fixture") fixture = next;
76
+ else if (arg === "--model") model = next;
77
+ else if (arg === "--generator-model") generatorModel = next;
78
+ else {
79
+ const value = Number(next);
80
+ const maximum = arg === "--generation-timeout-ms" ? 30000 : 5;
81
+ if (!Number.isInteger(value) || value < 1 || value > maximum) {
82
+ throw new CliUsageError(`${arg} must be an integer from 1 to ${maximum}.`);
83
+ }
84
+ if (arg === "--max-generated-candidates") maxGeneratedCandidates = value;
85
+ else if (arg === "--generation-timeout-ms") generationTimeoutMs = value;
86
+ else generationCostUnits = value;
87
+ }
88
+ i += 1;
89
+ continue;
90
+ }
91
+
92
+ if (arg === "--language" || arg === "--lang") {
93
+ const next = requiredValue(args, i, arg);
94
+ const normalized = normalizeLanguage(next);
95
+ if (!normalized) throw new CliUsageError("Invalid language tag. Use a BCP 47 tag such as de, en or fr-CA.");
96
+ language = normalized;
97
+ i += 1;
98
+ continue;
99
+ }
100
+
101
+ if (arg === "--dir") {
102
+ dir = requiredValue(args, i, arg);
103
+ dirExplicit = true;
104
+ i += 1;
105
+ continue;
106
+ }
107
+
108
+ if (arg === "--target" || arg === "-t") {
109
+ target = requiredValue(args, i, arg);
110
+ i += 1;
111
+ continue;
112
+ }
113
+
114
+ if (!arg.startsWith("-") && !target) {
115
+ target = arg;
116
+ continue;
117
+ }
118
+ throw new CliUsageError(`Unknown argument: ${arg}`);
119
+ }
120
+
121
+ if (!target || !resolveCommand(target)) {
122
+ throw new CliUsageError(`Please choose one target: ${supportedCommandNames().join(", ")}.`, { showUsage: true });
123
+ }
124
+
125
+ return {
126
+ kind: "command",
127
+ options: {
128
+ target,
129
+ dir: resolve(cwd, dir),
130
+ force,
131
+ json,
132
+ statusCard,
133
+ dirExplicit,
134
+ language: languagePreference(language),
135
+ surface,
136
+ fixture: fixture ? resolve(cwd, fixture) : null,
137
+ persist,
138
+ model,
139
+ generateCandidates,
140
+ runId,
141
+ allActive,
142
+ scope,
143
+ confirm,
144
+ generatorModel,
145
+ maxGeneratedCandidates,
146
+ generationTimeoutMs,
147
+ generationCostUnits,
148
+ },
149
+ };
150
+ }
@@ -0,0 +1,83 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import process from "node:process";
5
+ import {
6
+ canonicalizeLanguageTag,
7
+ resolvePresentationLocale,
8
+ } from "../interaction-presentation.js";
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+
12
+ export const packageRoot = resolve(__dirname, "../..");
13
+ export const generatedRoot = join(packageRoot, "generated");
14
+
15
+ const pluginDefinitionPath = join(generatedRoot, "plugins", "agdf", "meta", "agdf-plugin.definition.json");
16
+ const interactionLocalesPath = join(generatedRoot, "plugins", "agdf", "meta", "agdf-interaction-locales.json");
17
+
18
+ export const pluginDefinition = JSON.parse(readFileSync(pluginDefinitionPath, "utf8"));
19
+ export const interactionLocales = JSON.parse(readFileSync(interactionLocalesPath, "utf8"));
20
+
21
+ export function configuredLanguage(value) {
22
+ const normalized = canonicalizeLanguageTag(value);
23
+ if (!normalized) return "";
24
+ const language = normalized.split("-")[0];
25
+ if (interactionLocales.locales[normalized] || interactionLocales.locales[language]) {
26
+ return resolvePresentationLocale(interactionLocales, normalized);
27
+ }
28
+ return normalized;
29
+ }
30
+
31
+ export function detectSystemLocale(env = process.env) {
32
+ const envLocale = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE || "";
33
+ if (envLocale) return envLocale;
34
+ try {
35
+ return Intl.DateTimeFormat().resolvedOptions().locale || "";
36
+ } catch {
37
+ return "";
38
+ }
39
+ }
40
+
41
+ export function resolveLanguagePreference(explicitLanguage, env = process.env) {
42
+ const explicit = configuredLanguage(explicitLanguage);
43
+ if (explicit) {
44
+ return {
45
+ artifact_language: explicit,
46
+ chat_language: explicit,
47
+ runtime_language: "en",
48
+ source: "parameter",
49
+ detected_locale: detectSystemLocale(env) || "unknown",
50
+ };
51
+ }
52
+
53
+ const detectedLocale = detectSystemLocale(env);
54
+ const detected = configuredLanguage(detectedLocale) || interactionLocales.fallbackLocale;
55
+ return {
56
+ artifact_language: detected,
57
+ chat_language: detected,
58
+ runtime_language: "en",
59
+ source: detectedLocale ? "system_locale" : "default",
60
+ detected_locale: detectedLocale || "unknown",
61
+ };
62
+ }
63
+
64
+ export function resolveConfiguredChatLanguage(targetDir) {
65
+ const configPath = join(targetDir, ".agdf", "control", "config.json");
66
+ if (!existsSync(configPath)) return interactionLocales.fallbackLocale;
67
+ try {
68
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
69
+ return resolvePresentationLocale(interactionLocales, config.chat_language);
70
+ } catch {
71
+ return interactionLocales.fallbackLocale;
72
+ }
73
+ }
74
+
75
+ export function languageConfigContent(languagePreference) {
76
+ return `${JSON.stringify({
77
+ artifact_language: languagePreference.artifact_language,
78
+ chat_language: languagePreference.chat_language,
79
+ runtime_language: languagePreference.runtime_language,
80
+ source: languagePreference.source,
81
+ detected_locale: languagePreference.detected_locale,
82
+ }, null, 2)}\n`;
83
+ }