create-agdf 0.9.9 → 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 +9 -9
- package/generated/.opencode/README.md +6 -8
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/lib/cli/application.js +5 -5
- package/lib/installers/opencode-activation.js +43 -0
- package/lib/installers/opencode.js +28 -16
- package/lib/scaffold/plan.js +0 -23
- package/lib/scaffold/presentation.js +7 -12
- package/opencode-plugin.js +32 -24
- package/package.json +1 -1
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` |
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
154
|
-
|
|
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.
|
|
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
|
|
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
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
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
|
-
-
|
|
11
|
-
-
|
|
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
|
-
-
|
|
12
|
+
- The globally installed runtime contract is the shared gate and output contract.
|
|
15
13
|
|
|
16
14
|
## Skills
|
|
17
15
|
|
package/lib/cli/application.js
CHANGED
|
@@ -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
|
|
42
|
+
import { agdfFragmentPath, generatedFilesForTarget } from "../scaffold/plan.js";
|
|
43
43
|
import { printNextSteps } from "../scaffold/presentation.js";
|
|
44
|
-
import { assertGeneratedWritePlan,
|
|
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
|
-
|
|
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,
|
|
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
|
|
173
|
-
"If
|
|
174
|
-
"When
|
|
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
|
-
"
|
|
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
|
|
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 (!
|
|
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:
|
|
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:
|
|
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
|
-
:
|
|
400
|
-
?
|
|
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
|
|
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
|
|
package/lib/scaffold/plan.js
CHANGED
|
@@ -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
|
|
6
|
+
import { agdfFragmentPath } from "./plan.js";
|
|
7
7
|
|
|
8
8
|
const pluginInstallCommand = "npx --yes @agdf/cli@latest claude";
|
|
9
9
|
|
|
10
|
-
export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
10
|
+
export function printNextSteps(target, destination, files, wroteAgentsFragment, removedOpenCodeAgents = [], { verbose = false, json = false, io = console } = {}) {
|
|
11
11
|
const repositorySurface = { "codex-repo": "codex", "opencode-repo": "opencode", copilot: "copilot" }[target];
|
|
12
12
|
const verified = files.every((file) => {
|
|
13
13
|
const path = join(destination, file.path);
|
|
@@ -17,7 +17,7 @@ export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
|
17
17
|
const nextAction = repositorySurface === "codex"
|
|
18
18
|
? "Restart Codex, open /plugins, select This repository and install agdf; then start a new task with: Run an AGDF gate check for this request."
|
|
19
19
|
: repositorySurface === "opencode"
|
|
20
|
-
? "Restart OpenCode in this repository so
|
|
20
|
+
? "Restart OpenCode in this repository so the global AGDF plugin observes the durable control configuration."
|
|
21
21
|
: "Run /instructions in GitHub Copilot CLI to confirm that the generated AGDF repository instructions are visible.";
|
|
22
22
|
printLifecycleResult(createLifecycleResult({
|
|
23
23
|
operation: "repository_setup",
|
|
@@ -90,15 +90,10 @@ export function printNextSteps(target, destination, files, wroteAgentsFragment,
|
|
|
90
90
|
io.log(`- Optional global Claude Code install: ${pluginInstallCommand}`);
|
|
91
91
|
}
|
|
92
92
|
if (target === "opencode-repo") {
|
|
93
|
-
if
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
io.log(
|
|
97
|
-
io.log("- Optional: also add create-agdf to ~/.config/opencode/opencode.json plugin[] for a user-wide OpenCode hook.");
|
|
98
|
-
io.log("- The global hook does not replace repository instructions or native skills; this repository's .opencode files remain the AGDF source of truth.");
|
|
99
|
-
io.log("- Start OpenCode in this repository; it will load opencode.json, .opencode/AGDF.md and the native AGDF skills.");
|
|
100
|
-
io.log("- Load agdf-gate-check through OpenCode's native skill tool for new build/change intent or unclear approval before later artefacts or implementation.");
|
|
101
|
-
io.log("- Run npx --yes @agdf/cli@latest init when the repository needs live AGDF control files.");
|
|
93
|
+
io.log("- Install the global OpenCode surface once with npx --yes @agdf/cli@latest opencode if it is not already installed.");
|
|
94
|
+
io.log("- Start or restart OpenCode in this repository; valid .agdf/control/config.json activates the global AGDF runtime here.");
|
|
95
|
+
io.log("- Load agdf-global-gate-check through OpenCode's native skill tool for new build/change intent or unclear approval before later artefacts or implementation.");
|
|
96
|
+
io.log("- Existing .opencode files are left untouched as a compatibility path; this command does not copy a second runtime surface.");
|
|
102
97
|
}
|
|
103
98
|
if (target === "copilot" || target === "both") {
|
|
104
99
|
io.log("- In GitHub Copilot CLI, run /instructions after the AGENTS.md step is complete to confirm that AGDF instructions and the repository skills are visible.");
|
package/opencode-plugin.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { evaluateOpenCodeRepositoryActivation } from "./lib/installers/opencode-activation.js";
|
|
2
3
|
|
|
3
4
|
const packageJson = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8"));
|
|
4
5
|
|
|
5
6
|
export const AGDFPlugin = async ({ directory, client }) => {
|
|
6
7
|
const controlDir = `${directory}/.agdf/control`;
|
|
7
|
-
const
|
|
8
|
-
const status = () =>
|
|
9
|
-
|
|
8
|
+
const activation = () => evaluateOpenCodeRepositoryActivation(directory);
|
|
9
|
+
const status = () => {
|
|
10
|
+
const repositoryActivation = activation();
|
|
11
|
+
return {
|
|
12
|
+
active: repositoryActivation.active,
|
|
10
13
|
version: packageJson.version,
|
|
11
14
|
controlDir,
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
repositoryActivation: repositoryActivation.state,
|
|
16
|
+
repositorySurface: repositoryActivation.legacy_surface,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const activeGuidance = [
|
|
21
|
+
"## AGDF Runtime Reminder",
|
|
22
|
+
"This repository is AGDF-active through `.agdf/control/config.json`; use the globally installed AGDF runtime surface.",
|
|
23
|
+
"For new build/change intent or unclear approval, load the native `agdf-global-gate-check` skill before later artefacts or implementation.",
|
|
24
|
+
"Use `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.
|
|
41
|
+
message: currentStatus.active ? "AGDF OpenCode active through durable control" : "AGDF OpenCode global hook active without durable control",
|
|
24
42
|
extra: currentStatus,
|
|
25
43
|
},
|
|
26
44
|
});
|
|
@@ -29,29 +47,19 @@ export const AGDFPlugin = async ({ directory, client }) => {
|
|
|
29
47
|
|
|
30
48
|
"shell.env": async (_input, output) => {
|
|
31
49
|
const currentStatus = status();
|
|
32
|
-
output.env.AGDF_PLUGIN_ACTIVE = "1";
|
|
50
|
+
output.env.AGDF_PLUGIN_ACTIVE = currentStatus.active ? "1" : "0";
|
|
33
51
|
output.env.AGDF_PLUGIN_VERSION = currentStatus.version;
|
|
34
52
|
output.env.AGDF_CONTROL_DIR = controlDir;
|
|
35
53
|
output.env.AGDF_OPENCODE_REPOSITORY_SURFACE = currentStatus.repositorySurface ? "1" : "0";
|
|
54
|
+
output.env.AGDF_OPENCODE_REPOSITORY_ACTIVATION = currentStatus.repositoryActivation;
|
|
36
55
|
},
|
|
37
56
|
|
|
38
|
-
"experimental.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"## AGDF Plugin Notice",
|
|
42
|
-
"The AGDF OpenCode npm plugin is loaded, but this repository does not contain the AGDF OpenCode surface.",
|
|
43
|
-
"Do not apply AGDF gates from the global plugin alone; repository instructions, native skills and control files are the source of truth.",
|
|
44
|
-
"Run `npx --yes @agdf/cli@latest opencode-repo` in this repository when AGDF governance should be active here.",
|
|
45
|
-
].join("\n"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
57
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
58
|
+
output.system.push(activation().active ? activeGuidance : inactiveGuidance);
|
|
59
|
+
},
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"Use `.opencode/AGDF.md`, `.opencode/skills/` and `.agdf/control/` as the AGDF source for this OpenCode session.",
|
|
52
|
-
"For new build/change intent or unclear approval, load the native `agdf-gate-check` skill before later artefacts or implementation.",
|
|
53
|
-
"Use `npx --yes @agdf/cli@latest gate-check --status-card` for compact interactive status. Use `--json` only as deterministic proof for automation or audit evidence, and summarize it instead of mirroring full JSON into chat.",
|
|
54
|
-
].join("\n"));
|
|
61
|
+
"experimental.session.compacting": async (_input, output) => {
|
|
62
|
+
output.context.push(activation().active ? activeGuidance : inactiveGuidance);
|
|
55
63
|
},
|
|
56
64
|
};
|
|
57
65
|
};
|
package/package.json
CHANGED