create-agdf 0.10.0 → 0.10.1

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 CHANGED
@@ -25,7 +25,7 @@ Use a different target when Codex is not your agent surface:
25
25
  |---|---|---|
26
26
  | Claude Code | `npx --yes @agdf/cli@latest claude` | The AGDF plugin for Claude Code. |
27
27
  | OpenCode, user-wide discovery | `npx --yes @agdf/cli@latest opencode` | The npm plugin and global native skills; repository governance remains opt-in. |
28
- | OpenCode, one repository | `npx --yes @agdf/cli@latest opencode-repo` | Repository instructions, native skills, permissions and control templates. |
28
+ | OpenCode, one repository | `npx --yes @agdf/cli@latest opencode-repo` | Durable control configuration that activates the once-installed global runtime. |
29
29
  | GitHub Copilot | `npx --yes @agdf/cli@latest copilot` | Repository instructions, visible skills and control templates. |
30
30
  | Durable control state in an existing setup | `npx --yes @agdf/cli@latest init` | Live `.agdf/control/` state when the repository explicitly needs it. |
31
31
 
@@ -115,11 +115,11 @@ it does not localize the CLI lifecycle card.
115
115
  - `claude` installs the AGDF plugin globally for Claude Code
116
116
  - `copilot` writes `AGENTS.md`, Copilot custom instructions under `.github/`, visible repository skills under `.github/skills/`, and AGDF control templates under `.agdf/control/`
117
117
  - `opencode` installs the AGDF npm plugin and nine native skills as a user-wide OpenCode surface
118
- - `opencode-status` reports OpenCode global config, package loadability, global native-skill completeness, session signals and repository surface presence
118
+ - `opencode-status` reports OpenCode global config, package loadability, global native-skill completeness, durable repository activation, legacy compatibility and observable session signals
119
119
  - `status` reports installation, repository activation and delivery separately without mutating state
120
120
  - `disable` writes a supported repository-local opt-out while retaining global capability and durable control state
121
121
  - `uninstall` previews and, only with `--confirm`, applies a selected global removal through supported native/owned operations
122
- - `opencode-repo` writes `opencode.json`, `.opencode/AGDF.md`, prefixed native OpenCode skills under `.opencode/skills/`, explicit question/edit/bash/skill permissions, and AGDF control templates under `.agdf/control/`
122
+ - `opencode-repo` writes durable AGDF control configuration and templates under `.agdf/control/`; it does not copy a second OpenCode runtime surface
123
123
  - `both` writes the Codex repository-local marketplace plus the Copilot-facing repository files
124
124
  - `config` writes or updates only `.agdf/control/config.json` for an already installed plugin or an existing repository
125
125
 
@@ -141,19 +141,19 @@ Then verify the visible installation state:
141
141
  npx --yes @agdf/cli@latest opencode-status --json
142
142
  ```
143
143
 
144
- The status command reports global configuration, package loadability, global native-skill completeness, session signals and whether the current repository has `.opencode/AGDF.md` plus the `agdf-gate-check` surface. It does not infer an active OpenCode session from config alone.
144
+ The status command reports global configuration, package loadability, global native-skill completeness, durable repository activation, legacy local-surface compatibility and session signals. It does not infer an active OpenCode session from config alone.
145
145
  In JSON schema version 1, `repository_surface.gate_check_agent` remains a deprecated compatibility alias for the native `gate_check_skill` path so existing status consumers keep working during the agent-to-skill migration.
146
146
 
147
- Use the `opencode-repo` target when AGDF should be available to OpenCode from repository files:
147
+ Use the `opencode-repo` target when a repository should opt into the globally installed AGDF OpenCode runtime:
148
148
 
149
149
  ```bash
150
150
  npm create agdf@latest -- opencode-repo
151
151
  ```
152
152
 
153
- OpenCode loads the AGDF npm plugin and global native skills from global OpenCode config, plus repository instructions from `.opencode/AGDF.md`, explicit `question`/`edit`/`bash`/`skill` permissions and canonical `agdf-` project skills under `.opencode/skills/`. Global adapters use the collision-safe `agdf-global-` prefix. AGDF for OpenCode has a global discoverability layer installed with `npx --yes @agdf/cli@latest opencode` and a repository-local governance surface generated by `opencode-repo`.
154
- Those generated skills load on demand through OpenCode's native `skill` tool. Use `agdf-gate-check` first for new build/change intent or unclear approval.
155
- The built-in `question` tool can present a gate choice, but `.agdf/control/` and exact post-response validation remain authoritative; technical permission and auto-mode outcomes never approve an AGDF gate. If `opencode.json` already exists, AGDF keeps it unchanged and writes `opencode.agdf.json` as a merge fragment. Preserve any explicit `permission.question` decision when reviewing that fragment; `deny` uses exact-text fallback.
156
- The global layer makes the nine native AGDF skills discoverable, but does not activate repository governance by itself. Global skills fail closed until the current repository has `.opencode/AGDF.md`, `.opencode/skills/agdf-gate-check/SKILL.md` and `.agdf/control/`. Use `opencode-repo` to write that repository surface.
153
+ OpenCode loads the AGDF npm plugin and global native skills from global OpenCode config. Global adapters use the collision-safe `agdf-global-` prefix, including `agdf-global-gate-check`. `opencode-repo` adds the durable `.agdf/control/config.json` marker and control templates; it does not write `opencode.json`, `.opencode/AGDF.md`, copied contracts or copied skills.
154
+ The global skills load on demand through OpenCode's native `skill` tool. Use `agdf-global-gate-check` first for new build/change intent or unclear approval. Existing local `.opencode/` assets remain supported as a compatibility path and are not deleted.
155
+ The built-in `question` tool can present a gate choice, but `.agdf/control/` and exact post-response validation remain authoritative; technical permission and auto-mode outcomes never approve an AGDF gate. Explicit `permission.question: deny` remains unchanged and uses exact-text fallback.
156
+ The global layer makes the nine native AGDF skills discoverable, but does not activate repository governance by itself. Global skills fail closed until the current repository has valid `.agdf/control/config.json`. Use `opencode-repo` to create the durable activation marker.
157
157
 
158
158
  ## Control scaffold
159
159
 
@@ -2,16 +2,14 @@
2
2
 
3
3
  These files were generated from the AGDF source repository for OpenCode.
4
4
 
5
- - `opencode.json` loads the npm plugin `create-agdf` and `.opencode/AGDF.md` as repository instructions.
6
- - AGDF for OpenCode has a global npm/native-skill discoverability layer and this repository-local governance surface.
7
- - The global plugin and skills do not replace `.opencode/AGDF.md`, `.opencode/skills/` or `.agdf/control/`; repository files remain the AGDF source of truth.
8
- - `opencode.json` allows the built-in `question` tool, keeps `edit` and `bash` on `ask` and explicitly allows the generated `agdf-*` skills; global adapters use `agdf-global-*` to avoid same-name masking.
5
+ - The globally installed npm plugin `create-agdf` owns the shared OpenCode runtime surface.
6
+ - A repository is AGDF-active when `.agdf/control/config.json` is valid; this activation does not copy `.opencode/` assets.
7
+ - Global adapters use `agdf-global-*` to avoid same-name masking by supported legacy local `agdf-*` skills.
9
8
  - Preserve an explicit user `permission.question: deny`; AGDF then uses exact textual approval and never interprets OpenCode permission outcomes or auto mode as gate authority.
10
- - `.opencode/skills/` contains native OpenCode skills generated from the canonical AGDF skills.
11
- - For new build/change intent or unclear approval, load `agdf-gate-check` through OpenCode's native `skill` tool.
12
- - Use `npx --yes @agdf/cli@latest opencode-status --json` to distinguish global hook/native-skill configuration, package loadability, session activity and this repository surface.
9
+ - For new build/change intent or unclear approval, load `agdf-global-gate-check` through OpenCode's native `skill` tool.
10
+ - Use `npx --yes @agdf/cli@latest opencode-status --json` to distinguish global hook/native-skill configuration, package loadability, durable activation, legacy compatibility and session activity.
13
11
  - For deterministic ready-gate rendering, prefer an installed `agdf gate-check --approval-envelope`; reserve `agdf gate-check --json` for native-adapter input, automation or audit evidence and use `npx ...@latest` only for bootstrap, refresh or a missing local executable.
14
- - `.opencode/agdf-runtime-contract.md` is the shared gate and output contract.
12
+ - The globally installed runtime contract is the shared gate and output contract.
15
13
 
16
14
  ## Skills
17
15
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agdf",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Operating system for governed AI-assisted delivery.",
5
5
  "author": {
6
6
  "name": "Arndt Gold",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "agdf",
4
- "version": "0.10.0",
4
+ "version": "0.10.1",
5
5
  "displayName": "AI Governance & Delivery Framework",
6
6
  "developerName": "Arndt Gold",
7
7
  "description": "Operating system for governed AI-assisted delivery.",
@@ -39,9 +39,9 @@ import {
39
39
  import { printGeneralStatus, printLifecycleResult } from "../lifecycle/presentation.js";
40
40
  import { createLifecycleResult, globalInstallRestartAction, lifecycleFailure } from "../lifecycle/result.js";
41
41
  import { evaluateGeneralStatus } from "../lifecycle/status.js";
42
- import { agdfFragmentPath, generatedFilesForTarget, openCodeConfigFragmentPath } from "../scaffold/plan.js";
42
+ import { agdfFragmentPath, generatedFilesForTarget } from "../scaffold/plan.js";
43
43
  import { printNextSteps } from "../scaffold/presentation.js";
44
- import { assertGeneratedWritePlan, removeOwnedLegacyOpenCodeAgents, writeGeneratedFile } from "../scaffold/write.js";
44
+ import { assertGeneratedWritePlan, writeGeneratedFile } from "../scaffold/write.js";
45
45
  import { renderUsage, resolveCommand, validateCommandOptions } from "./command-registry.js";
46
46
  import { executeDeliveryPathSearch } from "./delivery-path-search-command.js";
47
47
  import { CliUsageError, parseArgs } from "./parse-args.js";
@@ -308,7 +308,6 @@ function runUninstall(options, { io, env, exec }) {
308
308
  function runScaffold(options, io) {
309
309
  const files = generatedFilesForTarget(options.target, options.dir, options.force, options.language);
310
310
  const wroteAgentsFragment = files.some((file) => file.path === agdfFragmentPath);
311
- const wroteOpenCodeConfigFragment = files.some((file) => file.path === openCodeConfigFragmentPath);
312
311
  let removedOpenCodeAgents = [];
313
312
 
314
313
  try {
@@ -316,13 +315,14 @@ function runScaffold(options, io) {
316
315
  for (const file of files) {
317
316
  writeGeneratedFile(options.dir, file.path, file.content, options.force, file.allowOverwrite);
318
317
  }
319
- if (options.target === "opencode-repo") removedOpenCodeAgents = removeOwnedLegacyOpenCodeAgents(options.dir);
318
+ // Legacy OpenCode assets are intentionally preserved. A future explicit migration command
319
+ // may remove only ownership-proven files after its precedence behavior is verified.
320
320
  } catch (error) {
321
321
  io.error(error.message);
322
322
  return 1;
323
323
  }
324
324
 
325
- printNextSteps(options.target, options.dir, files, wroteAgentsFragment, wroteOpenCodeConfigFragment, removedOpenCodeAgents, { verbose: options.verbose, json: options.json, io });
325
+ printNextSteps(options.target, options.dir, files, wroteAgentsFragment, removedOpenCodeAgents, { verbose: options.verbose, json: options.json, io });
326
326
  return 0;
327
327
  }
328
328
 
@@ -0,0 +1,43 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { pluginDefinition } from "../cli/runtime-context.js";
4
+
5
+ const requiredConfigFields = ["artifact_language", "chat_language", "runtime_language"];
6
+
7
+ export function isOpenCodeLegacySurfacePresent(targetDir) {
8
+ return existsSync(join(targetDir, ".opencode", pluginDefinition.opencode.instructionsFileName))
9
+ && existsSync(join(targetDir, ".opencode", "skills", `${pluginDefinition.opencode.skillPrefix}gate-check`, "SKILL.md"));
10
+ }
11
+
12
+ export function evaluateOpenCodeRepositoryActivation(targetDir) {
13
+ const controlDir = join(targetDir, ".agdf", "control");
14
+ const configPath = join(controlDir, "config.json");
15
+ const legacySurface = isOpenCodeLegacySurfacePresent(targetDir);
16
+ const base = {
17
+ control_dir: controlDir,
18
+ config_path: configPath,
19
+ legacy_surface: legacySurface,
20
+ };
21
+
22
+ if (!existsSync(configPath)) {
23
+ return { ...base, state: "inactive", active: false, diagnostic: "missing_control_config" };
24
+ }
25
+
26
+ try {
27
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
28
+ const valid = config
29
+ && typeof config === "object"
30
+ && !Array.isArray(config)
31
+ && requiredConfigFields.every((field) => typeof config[field] === "string" && config[field].trim());
32
+ if (!valid) return { ...base, state: "invalid_control", active: false, diagnostic: "invalid_control_config" };
33
+ } catch (error) {
34
+ return { ...base, state: "invalid_control", active: false, diagnostic: "invalid_control_json", error: error.message };
35
+ }
36
+
37
+ return {
38
+ ...base,
39
+ state: legacySurface ? "legacy_compatible" : "active",
40
+ active: true,
41
+ diagnostic: "valid_control_config",
42
+ };
43
+ }
@@ -4,6 +4,7 @@ import { homedir } from "node:os";
4
4
  import { dirname, join } from "node:path";
5
5
  import process from "node:process";
6
6
  import { generatedRoot, pluginDefinition } from "../cli/runtime-context.js";
7
+ import { evaluateOpenCodeRepositoryActivation } from "./opencode-activation.js";
7
8
 
8
9
  const contractModules = ["gate-transition.md", "interaction.md", "modes.md", "quality.md", "context-graph.md", "control-scaffold.md", "closeout.md"];
9
10
  const openCodeSkillNames = pluginDefinition.skillSet.map((skill) => pluginDefinition.opencode.skillPrefix + skill.slug);
@@ -169,9 +170,9 @@ function globalOpenCodeBoundary() {
169
170
  "## Global OpenCode Surface Boundary",
170
171
  "",
171
172
  "This skill is globally discoverable, but global plugin or skill presence is not repository governance activation.",
172
- "Before applying AGDF gates, later artefacts or implementation guidance, inspect the current repository for `.opencode/AGDF.md`, `.opencode/skills/agdf-gate-check/SKILL.md` and `.agdf/control/`.",
173
- "If the repository surface is absent, stop and direct the user to `npx --yes @agdf/cli@latest opencode-repo` in this repository.",
174
- "When the repository surface exists, its local instructions, native skills and `.agdf/control/` state are authoritative.",
173
+ "Before applying AGDF gates, later artefacts or implementation guidance, inspect the current repository for valid `.agdf/control/config.json` durable control.",
174
+ "If durable control is missing or invalid, stop and direct the user to `npx --yes @agdf/cli@latest opencode-repo` in this repository.",
175
+ "When durable control is valid, use the global `agdf-global-*` skill surface; existing local OpenCode assets remain a compatibility path.",
175
176
  "",
176
177
  ].join("\n");
177
178
  }
@@ -198,7 +199,7 @@ export function installOpenCodeGlobalSurface(configDir) {
198
199
  "AGDF native skills are globally discoverable through OpenCode.",
199
200
  "",
200
201
  globalOpenCodeBoundary(),
201
- "The repository-local `.opencode/AGDF.md`, `.opencode/skills/` and `.agdf/control/` files remain the source of truth for active governance.",
202
+ "Valid `.agdf/control/` is the repository source of truth for active governance; legacy `.opencode/` assets remain supported without being required.",
202
203
  "",
203
204
  toGlobalOpenCodeContent(generatedInstructions),
204
205
  ].join("\n");
@@ -243,11 +244,6 @@ function readOpenCodeConfig(configPath) {
243
244
  }
244
245
  }
245
246
 
246
- function isOpenCodeRepositorySurfacePresent(targetDir) {
247
- return existsSync(join(targetDir, ".opencode", pluginDefinition.opencode.instructionsFileName))
248
- && existsSync(join(targetDir, ".opencode", "skills", `${pluginDefinition.opencode.skillPrefix}gate-check`, "SKILL.md"));
249
- }
250
-
251
247
  function resolveOpenCodePackage(configDir) {
252
248
  const packageName = pluginDefinition.opencode.npmPackage;
253
249
  try {
@@ -343,8 +339,9 @@ export function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeCon
343
339
  version: process.env.AGDF_PLUGIN_VERSION || "",
344
340
  control_dir: process.env.AGDF_CONTROL_DIR || "",
345
341
  repository_surface: process.env.AGDF_OPENCODE_REPOSITORY_SURFACE === "1",
342
+ repository_activation: process.env.AGDF_OPENCODE_REPOSITORY_ACTIVATION || "",
346
343
  };
347
- const repositorySurface = isOpenCodeRepositorySurfacePresent(targetDir);
344
+ const repositoryActivation = evaluateOpenCodeRepositoryActivation(targetDir);
348
345
  const gateCheckSkillPath = join(targetDir, ".opencode", "skills", `${pluginDefinition.opencode.skillPrefix}gate-check`, "SKILL.md");
349
346
 
350
347
  const findings = [];
@@ -360,7 +357,11 @@ export function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeCon
360
357
  );
361
358
  }
362
359
  if (!sessionSignals.active) findings.push("No active AGDF OpenCode session signal is visible in this process.");
363
- if (!repositorySurface) findings.push("Current repository does not contain the AGDF OpenCode surface.");
360
+ if (!repositoryActivation.active) {
361
+ findings.push(repositoryActivation.state === "invalid_control"
362
+ ? "Current repository has invalid AGDF durable control configuration."
363
+ : "Current repository does not contain valid AGDF durable control configuration.");
364
+ }
364
365
 
365
366
  return {
366
367
  schema_version: "1",
@@ -383,21 +384,30 @@ export function evaluateOpenCodeStatus(targetDir, configDir = defaultOpenCodeCon
383
384
  },
384
385
  global_native_surface: globalNativeSurface,
385
386
  session: sessionSignals,
387
+ repository_activation: repositoryActivation.state,
388
+ repository_control: {
389
+ path: repositoryActivation.config_path,
390
+ diagnostic: repositoryActivation.diagnostic,
391
+ ...(repositoryActivation.error ? { error: repositoryActivation.error } : {}),
392
+ },
386
393
  repository_surface: {
387
394
  path: targetDir,
388
- present: repositorySurface,
395
+ present: repositoryActivation.active,
396
+ legacy_present: repositoryActivation.legacy_surface,
389
397
  instructions: join(targetDir, ".opencode", pluginDefinition.opencode.instructionsFileName),
390
398
  gate_check_agent: gateCheckSkillPath,
391
399
  gate_check_skill: gateCheckSkillPath,
392
400
  },
393
- visible_entrypoint: repositorySurface ? `${pluginDefinition.opencode.skillPrefix}gate-check (native skill)` : "none until opencode-repo is installed for this repository",
401
+ visible_entrypoint: repositoryActivation.active ? `${pluginDefinition.opencode.globalSkillPrefix}gate-check (native skill)` : "none until durable AGDF control is configured for this repository",
394
402
  findings,
395
403
  next_step: packageVersionStatus !== "current"
396
404
  ? "Run npx --yes @agdf/cli@latest opencode to install or repair the OpenCode package version."
397
405
  : !globalNativeSurface.complete
398
406
  ? "Run npx --yes @agdf/cli@latest opencode to install or repair the global native OpenCode skill surface."
399
- : repositorySurface
400
- ? `Restart OpenCode if needed, then load ${pluginDefinition.opencode.skillPrefix}gate-check through the native skill tool for new build/change intent.`
407
+ : repositoryActivation.state === "invalid_control"
408
+ ? "Repair .agdf/control/config.json so it contains valid artifact_language, chat_language and runtime_language values."
409
+ : repositoryActivation.active
410
+ ? `Restart OpenCode if needed, then load ${pluginDefinition.opencode.globalSkillPrefix}gate-check through the native skill tool for new build/change intent.`
401
411
  : "Run npx --yes @agdf/cli@latest opencode-repo in repositories where AGDF governance should be active and reviewable.",
402
412
  };
403
413
  }
@@ -431,7 +441,9 @@ export function printOpenCodeStatus(report, json, io = console) {
431
441
  io.log(`Session active signal: ${report.session.active ? "yes" : "no"}`);
432
442
  if (report.session.version) io.log(`Session plugin version: ${report.session.version}`);
433
443
  if (report.session.control_dir) io.log(`Session control dir: ${report.session.control_dir}`);
434
- io.log(`Repository surface: ${report.repository_surface.present ? "present" : "missing"}`);
444
+ io.log(`Repository activation: ${report.repository_activation}`);
445
+ io.log(`Repository control: ${report.repository_control.path} (${report.repository_control.diagnostic})`);
446
+ io.log(`Legacy repository surface: ${report.repository_surface.legacy_present ? "present" : "absent"}`);
435
447
  io.log(`Visible entrypoint: ${report.visible_entrypoint}`);
436
448
  io.log(`Next step: ${report.next_step}`);
437
449
 
@@ -3,10 +3,8 @@ import { join } from "node:path";
3
3
  import { generatedRoot, languageConfigContent, pluginDefinition } from "../cli/runtime-context.js";
4
4
 
5
5
  export const agdfFragmentPath = "AGENTS.agdf.md";
6
- export const openCodeConfigFragmentPath = "opencode.agdf.json";
7
6
  const codexSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.codex.skillPrefix}${skill.slug}`);
8
7
  const copilotSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.copilot.skillPrefix}${skill.slug}`);
9
- const openCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.skillPrefix}${skill.slug}`);
10
8
  const globalOpenCodeSkillNames = pluginDefinition.skillSet.map((skill) => `${pluginDefinition.opencode.globalSkillPrefix}${skill.slug}`);
11
9
  const contractModules = [
12
10
  "gate-transition.md",
@@ -111,13 +109,6 @@ const copilotSkillFiles = [
111
109
  ...contractModules.map((moduleName) => join(".github", "skills", "contracts", moduleName)),
112
110
  ...copilotSkillNames.map((skillName) => join(".github", "skills", skillName, "SKILL.md")),
113
111
  ];
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
112
 
122
113
  function loadAsset(relativePath) {
123
114
  return readFileSync(join(generatedRoot, relativePath), "utf8");
@@ -252,19 +243,6 @@ export function generatedFilesForTarget(target, targetDir, force, languagePrefer
252
243
 
253
244
  if (target === "opencode-repo") {
254
245
  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
246
  for (const controlPath of controlFiles) {
269
247
  files.push({
270
248
  path: controlPath,
@@ -275,4 +253,3 @@ export function generatedFilesForTarget(target, targetDir, force, languagePrefer
275
253
 
276
254
  return files;
277
255
  }
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",
@@ -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 `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.",
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.1",
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": [