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.
- package/README.md +33 -4
- package/bin/create-agdf.js +2 -3333
- package/generated/.agdf/control/templates/MASTER_BACKLOG.md +3 -3
- package/generated/.github/skills/agdf-gate-check/SKILL.md +5 -0
- package/generated/.github/skills/agdf-interaction-locales.json +36 -0
- package/generated/.github/skills/contracts/interaction.md +9 -1
- package/generated/.opencode/agdf-interaction-locales.json +36 -0
- package/generated/.opencode/contracts/interaction.md +9 -1
- package/generated/.opencode/skills/agdf-gate-check/SKILL.md +5 -0
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/control/templates/MASTER_BACKLOG.md +3 -3
- package/generated/plugins/agdf/meta/agdf-interaction-locales.json +36 -0
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +1 -1
- package/generated/plugins/agdf/meta/contracts/interaction.md +9 -1
- package/generated/plugins/agdf/skills/gate-check/SKILL.md +5 -0
- package/lib/cli/application.js +342 -0
- package/lib/cli/command-registry.js +119 -0
- package/lib/cli/delivery-path-search-command.js +75 -0
- package/lib/cli/parse-args.js +150 -0
- package/lib/cli/runtime-context.js +83 -0
- package/lib/control-evaluation/delivery-map.js +291 -0
- package/lib/control-evaluation/doctor.js +265 -0
- package/lib/control-evaluation/gate-check.js +404 -0
- package/lib/control-evaluation/gate-policy.js +283 -0
- package/lib/control-evaluation/run-state.js +217 -0
- package/lib/control-evaluation/shared.js +344 -0
- package/lib/control-evaluation/verified-change.js +156 -0
- package/lib/installers/opencode.js +443 -0
- package/lib/installers/plugin-installers.js +99 -0
- package/lib/lifecycle/operations.js +177 -0
- package/lib/lifecycle/presentation.js +52 -0
- package/lib/lifecycle/result.js +69 -0
- package/lib/lifecycle/status.js +108 -0
- package/lib/scaffold/plan.js +278 -0
- package/lib/scaffold/presentation.js +98 -0
- package/lib/scaffold/write.js +45 -0
- package/package.json +4 -2
|
@@ -15,10 +15,10 @@ This file is the living control pointer for active AGDF delivery work. Detailed
|
|
|
15
15
|
9. Historical artefacts remain linkable but do not override the current pointer.
|
|
16
16
|
10. If an item is superseded, mark the replacement.
|
|
17
17
|
11. If active work grows beyond roughly ten items, triage the backlog.
|
|
18
|
-
12. `Status` must be one of the human-readable labels `create-agdf/
|
|
19
|
-
13. `Artefacts` link labels must be one of
|
|
18
|
+
12. `Status` must be one of the human-readable labels normalized by `create-agdf/lib/control-evaluation/shared.js`: Needs UR, Awaiting Brownfield Review, Awaiting PRD, Awaiting PRD Approval, Awaiting SD, Awaiting SD Approval, Awaiting TP, Awaiting TP Approval, In Progress, Blocked, Awaiting QA, Awaiting UAT, Completed, Superseded, Abandoned. The parser module is authoritative; this list only mirrors it for readability.
|
|
19
|
+
13. `Artefacts` link labels must be one of the labels normalized by the same parser module: `ur`, `brownfield`, `prd`, `sd`, `tp`, `qa`, `or` (case-insensitive). Do not invent other labels such as a raw artefact filename.
|
|
20
20
|
14. After writing or updating this file, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before treating the edit as done.
|
|
21
|
-
15. A `Work item` cell may optionally start with a bracketed scope tag, one of `[framework-maintenance]` or `[external-delivery]`, so the split between AGDF's own maintenance work and external product delivery stays visible at a glance. The tag is never required retroactively on `Completed / Superseded Pointers` rows. `create-agdf/
|
|
21
|
+
15. A `Work item` cell may optionally start with a bracketed scope tag, one of `[framework-maintenance]` or `[external-delivery]`, so the split between AGDF's own maintenance work and external product delivery stays visible at a glance. The tag is never required retroactively on `Completed / Superseded Pointers` rows. `create-agdf/lib/control-evaluation/shared.js` is authoritative for the accepted values.
|
|
22
22
|
16. After writing or updating this file, also resolve any `AGDF_BACKLOG_SCOPE_LABEL_UNKNOWN` finding, which fires only for a present-but-unrecognized bracketed tag, never for an absent one.
|
|
23
23
|
|
|
24
24
|
## Active Backlog
|
|
@@ -64,6 +64,11 @@ Use the Runtime Contract's Native Interaction Contract to classify every candida
|
|
|
64
64
|
|
|
65
65
|
Prefer inspection over asking. Do not invoke a structured question for status reporting, facts available in the repository, routine read-only work or a gate that is not ready. Do not repeatedly prompt for the same non-ready gate.
|
|
66
66
|
|
|
67
|
+
For a fresh request classified as read-only with no required run decision, emit the locale pack's
|
|
68
|
+
`primary.readOnlyOrientationDescription` sentence once before the findings. This path creates no run, persists
|
|
69
|
+
nothing, requests no approval and reuses the existing status projection. Do not repeat the sentence
|
|
70
|
+
during the same request.
|
|
71
|
+
|
|
67
72
|
For a ready `gate_approval`:
|
|
68
73
|
|
|
69
74
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
@@ -109,6 +109,24 @@
|
|
|
109
109
|
"notYetCreated": "not yet created"
|
|
110
110
|
},
|
|
111
111
|
"primary": {
|
|
112
|
+
"readOnlyOrientationDescription": "Read-only check — no new AGDF run and no approval required.",
|
|
113
|
+
"lifecycleResult": {
|
|
114
|
+
"title": "AGDF lifecycle result",
|
|
115
|
+
"statusTitle": "AGDF status",
|
|
116
|
+
"result": "Result",
|
|
117
|
+
"surface": "Surface",
|
|
118
|
+
"scope": "Installation scope",
|
|
119
|
+
"version": "AGDF version",
|
|
120
|
+
"verification": "Verification",
|
|
121
|
+
"restart": "Restart required",
|
|
122
|
+
"installation": "Installation",
|
|
123
|
+
"repository": "Repository",
|
|
124
|
+
"delivery": "Delivery",
|
|
125
|
+
"next": "Next action",
|
|
126
|
+
"yes": "yes",
|
|
127
|
+
"no": "no",
|
|
128
|
+
"unknown": "unknown"
|
|
129
|
+
},
|
|
112
130
|
"status": {
|
|
113
131
|
"open": "open",
|
|
114
132
|
"blocked": "blocked",
|
|
@@ -278,6 +296,24 @@
|
|
|
278
296
|
"notYetCreated": "noch nicht erstellt"
|
|
279
297
|
},
|
|
280
298
|
"primary": {
|
|
299
|
+
"readOnlyOrientationDescription": "Read-only Prüfung – kein neuer AGDF-Run und keine Freigabe erforderlich.",
|
|
300
|
+
"lifecycleResult": {
|
|
301
|
+
"title": "AGDF-Lifecycle-Ergebnis",
|
|
302
|
+
"statusTitle": "AGDF-Status",
|
|
303
|
+
"result": "Ergebnis",
|
|
304
|
+
"surface": "Oberfläche",
|
|
305
|
+
"scope": "Installationsscope",
|
|
306
|
+
"version": "AGDF-Version",
|
|
307
|
+
"verification": "Verifikation",
|
|
308
|
+
"restart": "Neustart erforderlich",
|
|
309
|
+
"installation": "Installation",
|
|
310
|
+
"repository": "Repository",
|
|
311
|
+
"delivery": "Delivery",
|
|
312
|
+
"next": "Nächste Aktion",
|
|
313
|
+
"yes": "ja",
|
|
314
|
+
"no": "nein",
|
|
315
|
+
"unknown": "unbekannt"
|
|
316
|
+
},
|
|
281
317
|
"status": {
|
|
282
318
|
"open": "offen",
|
|
283
319
|
"blocked": "blockiert",
|
|
@@ -274,6 +274,15 @@ Surface adapter rules:
|
|
|
274
274
|
|
|
275
275
|
Native structured questions are for real decision points. Prefer repository inspection over clarification and do not show them for status reporting, discoverable facts, routine read-only work or repeated non-ready gate prompts.
|
|
276
276
|
|
|
277
|
+
### Read-only request orientation
|
|
278
|
+
|
|
279
|
+
For a newly classified read-only request that does not require a run decision, render the localized
|
|
280
|
+
`primary.readOnlyOrientationDescription` sentence exactly once before the findings. This is a visible
|
|
281
|
+
orientation, not durable state: do not create a run, write control files, request gate approval or
|
|
282
|
+
repeat the sentence later in the same request. Existing-run status inspection remains read-only and
|
|
283
|
+
uses the existing status projection; this branch must not create a second status-card or narration
|
|
284
|
+
owner.
|
|
285
|
+
|
|
277
286
|
Exact textual approvals remain canonical and fully supported on every surface. If native capability availability or safety is unknown, use the textual fallback. Host permission, plan approval, native question presentation, timeout/default behavior, hook output and agent messages never carry AGDF gate authority by themselves.
|
|
278
287
|
|
|
279
288
|
### Interaction Locale Contract
|
|
@@ -367,4 +376,3 @@ Missing input, timeout, cancellation and empty input are not decline. Only
|
|
|
367
376
|
`approve` backed by the exact revalidated `Approval: <GateName>` value may
|
|
368
377
|
advance a gate. Clarification, blocked, internal-step and status-only
|
|
369
378
|
interactions must not display gate-approval controls.
|
|
370
|
-
|
|
@@ -109,6 +109,24 @@
|
|
|
109
109
|
"notYetCreated": "not yet created"
|
|
110
110
|
},
|
|
111
111
|
"primary": {
|
|
112
|
+
"readOnlyOrientationDescription": "Read-only check — no new AGDF run and no approval required.",
|
|
113
|
+
"lifecycleResult": {
|
|
114
|
+
"title": "AGDF lifecycle result",
|
|
115
|
+
"statusTitle": "AGDF status",
|
|
116
|
+
"result": "Result",
|
|
117
|
+
"surface": "Surface",
|
|
118
|
+
"scope": "Installation scope",
|
|
119
|
+
"version": "AGDF version",
|
|
120
|
+
"verification": "Verification",
|
|
121
|
+
"restart": "Restart required",
|
|
122
|
+
"installation": "Installation",
|
|
123
|
+
"repository": "Repository",
|
|
124
|
+
"delivery": "Delivery",
|
|
125
|
+
"next": "Next action",
|
|
126
|
+
"yes": "yes",
|
|
127
|
+
"no": "no",
|
|
128
|
+
"unknown": "unknown"
|
|
129
|
+
},
|
|
112
130
|
"status": {
|
|
113
131
|
"open": "open",
|
|
114
132
|
"blocked": "blocked",
|
|
@@ -278,6 +296,24 @@
|
|
|
278
296
|
"notYetCreated": "noch nicht erstellt"
|
|
279
297
|
},
|
|
280
298
|
"primary": {
|
|
299
|
+
"readOnlyOrientationDescription": "Read-only Prüfung – kein neuer AGDF-Run und keine Freigabe erforderlich.",
|
|
300
|
+
"lifecycleResult": {
|
|
301
|
+
"title": "AGDF-Lifecycle-Ergebnis",
|
|
302
|
+
"statusTitle": "AGDF-Status",
|
|
303
|
+
"result": "Ergebnis",
|
|
304
|
+
"surface": "Oberfläche",
|
|
305
|
+
"scope": "Installationsscope",
|
|
306
|
+
"version": "AGDF-Version",
|
|
307
|
+
"verification": "Verifikation",
|
|
308
|
+
"restart": "Neustart erforderlich",
|
|
309
|
+
"installation": "Installation",
|
|
310
|
+
"repository": "Repository",
|
|
311
|
+
"delivery": "Delivery",
|
|
312
|
+
"next": "Nächste Aktion",
|
|
313
|
+
"yes": "ja",
|
|
314
|
+
"no": "nein",
|
|
315
|
+
"unknown": "unbekannt"
|
|
316
|
+
},
|
|
281
317
|
"status": {
|
|
282
318
|
"open": "offen",
|
|
283
319
|
"blocked": "blockiert",
|
|
@@ -274,6 +274,15 @@ Surface adapter rules:
|
|
|
274
274
|
|
|
275
275
|
Native structured questions are for real decision points. Prefer repository inspection over clarification and do not show them for status reporting, discoverable facts, routine read-only work or repeated non-ready gate prompts.
|
|
276
276
|
|
|
277
|
+
### Read-only request orientation
|
|
278
|
+
|
|
279
|
+
For a newly classified read-only request that does not require a run decision, render the localized
|
|
280
|
+
`primary.readOnlyOrientationDescription` sentence exactly once before the findings. This is a visible
|
|
281
|
+
orientation, not durable state: do not create a run, write control files, request gate approval or
|
|
282
|
+
repeat the sentence later in the same request. Existing-run status inspection remains read-only and
|
|
283
|
+
uses the existing status projection; this branch must not create a second status-card or narration
|
|
284
|
+
owner.
|
|
285
|
+
|
|
277
286
|
Exact textual approvals remain canonical and fully supported on every surface. If native capability availability or safety is unknown, use the textual fallback. Host permission, plan approval, native question presentation, timeout/default behavior, hook output and agent messages never carry AGDF gate authority by themselves.
|
|
278
287
|
|
|
279
288
|
### Interaction Locale Contract
|
|
@@ -367,4 +376,3 @@ Missing input, timeout, cancellation and empty input are not decline. Only
|
|
|
367
376
|
`approve` backed by the exact revalidated `Approval: <GateName>` value may
|
|
368
377
|
advance a gate. Clarification, blocked, internal-step and status-only
|
|
369
378
|
interactions must not display gate-approval controls.
|
|
370
|
-
|
|
@@ -64,6 +64,11 @@ Use the Runtime Contract's Native Interaction Contract to classify every candida
|
|
|
64
64
|
|
|
65
65
|
Prefer inspection over asking. Do not invoke a structured question for status reporting, facts available in the repository, routine read-only work or a gate that is not ready. Do not repeatedly prompt for the same non-ready gate.
|
|
66
66
|
|
|
67
|
+
For a fresh request classified as read-only with no required run decision, emit the locale pack's
|
|
68
|
+
`primary.readOnlyOrientationDescription` sentence once before the findings. This path creates no run, persists
|
|
69
|
+
nothing, requests no approval and reuses the existing status projection. Do not repeat the sentence
|
|
70
|
+
during the same request.
|
|
71
|
+
|
|
67
72
|
For a ready `gate_approval`:
|
|
68
73
|
|
|
69
74
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
@@ -15,10 +15,10 @@ This file is the living control pointer for active AGDF delivery work. Detailed
|
|
|
15
15
|
9. Historical artefacts remain linkable but do not override the current pointer.
|
|
16
16
|
10. If an item is superseded, mark the replacement.
|
|
17
17
|
11. If active work grows beyond roughly ten items, triage the backlog.
|
|
18
|
-
12. `Status` must be one of the human-readable labels `create-agdf/
|
|
19
|
-
13. `Artefacts` link labels must be one of
|
|
18
|
+
12. `Status` must be one of the human-readable labels normalized by `create-agdf/lib/control-evaluation/shared.js`: Needs UR, Awaiting Brownfield Review, Awaiting PRD, Awaiting PRD Approval, Awaiting SD, Awaiting SD Approval, Awaiting TP, Awaiting TP Approval, In Progress, Blocked, Awaiting QA, Awaiting UAT, Completed, Superseded, Abandoned. The parser module is authoritative; this list only mirrors it for readability.
|
|
19
|
+
13. `Artefacts` link labels must be one of the labels normalized by the same parser module: `ur`, `brownfield`, `prd`, `sd`, `tp`, `qa`, `or` (case-insensitive). Do not invent other labels such as a raw artefact filename.
|
|
20
20
|
14. After writing or updating this file, run `doctor --json` (or the locally available equivalent) and resolve any `AGDF_BACKLOG_STATUS_UNKNOWN` or `AGDF_BACKLOG_ARTEFACT_LABEL_UNKNOWN` finding before treating the edit as done.
|
|
21
|
-
15. A `Work item` cell may optionally start with a bracketed scope tag, one of `[framework-maintenance]` or `[external-delivery]`, so the split between AGDF's own maintenance work and external product delivery stays visible at a glance. The tag is never required retroactively on `Completed / Superseded Pointers` rows. `create-agdf/
|
|
21
|
+
15. A `Work item` cell may optionally start with a bracketed scope tag, one of `[framework-maintenance]` or `[external-delivery]`, so the split between AGDF's own maintenance work and external product delivery stays visible at a glance. The tag is never required retroactively on `Completed / Superseded Pointers` rows. `create-agdf/lib/control-evaluation/shared.js` is authoritative for the accepted values.
|
|
22
22
|
16. After writing or updating this file, also resolve any `AGDF_BACKLOG_SCOPE_LABEL_UNKNOWN` finding, which fires only for a present-but-unrecognized bracketed tag, never for an absent one.
|
|
23
23
|
|
|
24
24
|
## Active Backlog
|
|
@@ -109,6 +109,24 @@
|
|
|
109
109
|
"notYetCreated": "not yet created"
|
|
110
110
|
},
|
|
111
111
|
"primary": {
|
|
112
|
+
"readOnlyOrientationDescription": "Read-only check — no new AGDF run and no approval required.",
|
|
113
|
+
"lifecycleResult": {
|
|
114
|
+
"title": "AGDF lifecycle result",
|
|
115
|
+
"statusTitle": "AGDF status",
|
|
116
|
+
"result": "Result",
|
|
117
|
+
"surface": "Surface",
|
|
118
|
+
"scope": "Installation scope",
|
|
119
|
+
"version": "AGDF version",
|
|
120
|
+
"verification": "Verification",
|
|
121
|
+
"restart": "Restart required",
|
|
122
|
+
"installation": "Installation",
|
|
123
|
+
"repository": "Repository",
|
|
124
|
+
"delivery": "Delivery",
|
|
125
|
+
"next": "Next action",
|
|
126
|
+
"yes": "yes",
|
|
127
|
+
"no": "no",
|
|
128
|
+
"unknown": "unknown"
|
|
129
|
+
},
|
|
112
130
|
"status": {
|
|
113
131
|
"open": "open",
|
|
114
132
|
"blocked": "blocked",
|
|
@@ -278,6 +296,24 @@
|
|
|
278
296
|
"notYetCreated": "noch nicht erstellt"
|
|
279
297
|
},
|
|
280
298
|
"primary": {
|
|
299
|
+
"readOnlyOrientationDescription": "Read-only Prüfung – kein neuer AGDF-Run und keine Freigabe erforderlich.",
|
|
300
|
+
"lifecycleResult": {
|
|
301
|
+
"title": "AGDF-Lifecycle-Ergebnis",
|
|
302
|
+
"statusTitle": "AGDF-Status",
|
|
303
|
+
"result": "Ergebnis",
|
|
304
|
+
"surface": "Oberfläche",
|
|
305
|
+
"scope": "Installationsscope",
|
|
306
|
+
"version": "AGDF-Version",
|
|
307
|
+
"verification": "Verifikation",
|
|
308
|
+
"restart": "Neustart erforderlich",
|
|
309
|
+
"installation": "Installation",
|
|
310
|
+
"repository": "Repository",
|
|
311
|
+
"delivery": "Delivery",
|
|
312
|
+
"next": "Nächste Aktion",
|
|
313
|
+
"yes": "ja",
|
|
314
|
+
"no": "nein",
|
|
315
|
+
"unknown": "unbekannt"
|
|
316
|
+
},
|
|
281
317
|
"status": {
|
|
282
318
|
"open": "offen",
|
|
283
319
|
"blocked": "blockiert",
|
|
@@ -274,6 +274,15 @@ Surface adapter rules:
|
|
|
274
274
|
|
|
275
275
|
Native structured questions are for real decision points. Prefer repository inspection over clarification and do not show them for status reporting, discoverable facts, routine read-only work or repeated non-ready gate prompts.
|
|
276
276
|
|
|
277
|
+
### Read-only request orientation
|
|
278
|
+
|
|
279
|
+
For a newly classified read-only request that does not require a run decision, render the localized
|
|
280
|
+
`primary.readOnlyOrientationDescription` sentence exactly once before the findings. This is a visible
|
|
281
|
+
orientation, not durable state: do not create a run, write control files, request gate approval or
|
|
282
|
+
repeat the sentence later in the same request. Existing-run status inspection remains read-only and
|
|
283
|
+
uses the existing status projection; this branch must not create a second status-card or narration
|
|
284
|
+
owner.
|
|
285
|
+
|
|
277
286
|
Exact textual approvals remain canonical and fully supported on every surface. If native capability availability or safety is unknown, use the textual fallback. Host permission, plan approval, native question presentation, timeout/default behavior, hook output and agent messages never carry AGDF gate authority by themselves.
|
|
278
287
|
|
|
279
288
|
### Interaction Locale Contract
|
|
@@ -367,4 +376,3 @@ Missing input, timeout, cancellation and empty input are not decline. Only
|
|
|
367
376
|
`approve` backed by the exact revalidated `Approval: <GateName>` value may
|
|
368
377
|
advance a gate. Clarification, blocked, internal-step and status-only
|
|
369
378
|
interactions must not display gate-approval controls.
|
|
370
|
-
|
|
@@ -64,6 +64,11 @@ Use the Runtime Contract's Native Interaction Contract to classify every candida
|
|
|
64
64
|
|
|
65
65
|
Prefer inspection over asking. Do not invoke a structured question for status reporting, facts available in the repository, routine read-only work or a gate that is not ready. Do not repeatedly prompt for the same non-ready gate.
|
|
66
66
|
|
|
67
|
+
For a fresh request classified as read-only with no required run decision, emit the locale pack's
|
|
68
|
+
`primary.readOnlyOrientationDescription` sentence once before the findings. This path creates no run, persists
|
|
69
|
+
nothing, requests no approval and reuses the existing status projection. Do not repeat the sentence
|
|
70
|
+
during the same request.
|
|
71
|
+
|
|
67
72
|
For a ready `gate_approval`:
|
|
68
73
|
|
|
69
74
|
1. Resolve exactly one selected run and evaluate the current gate.
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import {
|
|
4
|
+
createRun,
|
|
5
|
+
migrateLegacy,
|
|
6
|
+
resolveRuns,
|
|
7
|
+
writeLegacyProjection,
|
|
8
|
+
} from "../control-state/index.js";
|
|
9
|
+
import {
|
|
10
|
+
buildStatusCard,
|
|
11
|
+
evaluateGateCheck,
|
|
12
|
+
postApprovalTransition,
|
|
13
|
+
printGateCheckReport,
|
|
14
|
+
} from "../control-evaluation/gate-check.js";
|
|
15
|
+
import {
|
|
16
|
+
evaluateDeliveryMap,
|
|
17
|
+
printDeliveryMapReport,
|
|
18
|
+
} from "../control-evaluation/delivery-map.js";
|
|
19
|
+
import { evaluateDoctor, printDoctorReport } from "../control-evaluation/doctor.js";
|
|
20
|
+
import {
|
|
21
|
+
defaultOpenCodeConfigDir,
|
|
22
|
+
evaluateOpenCodeStatus,
|
|
23
|
+
installOpenCodeGlobalPlugin,
|
|
24
|
+
installOpenCodeGlobalSurface,
|
|
25
|
+
printOpenCodeStatus,
|
|
26
|
+
} from "../installers/opencode.js";
|
|
27
|
+
import {
|
|
28
|
+
installClaudeGlobalPlugin,
|
|
29
|
+
installCodexGlobalPlugin,
|
|
30
|
+
} from "../installers/plugin-installers.js";
|
|
31
|
+
import {
|
|
32
|
+
applyLifecyclePlan,
|
|
33
|
+
planGlobalUninstall,
|
|
34
|
+
planRepositoryDisable,
|
|
35
|
+
verifyGlobalUninstall,
|
|
36
|
+
verifyRepositoryDisabled,
|
|
37
|
+
} from "../lifecycle/operations.js";
|
|
38
|
+
import { printGeneralStatus, printLifecycleResult } from "../lifecycle/presentation.js";
|
|
39
|
+
import { createLifecycleResult, lifecycleFailure } from "../lifecycle/result.js";
|
|
40
|
+
import { evaluateGeneralStatus } from "../lifecycle/status.js";
|
|
41
|
+
import { agdfFragmentPath, generatedFilesForTarget, openCodeConfigFragmentPath } from "../scaffold/plan.js";
|
|
42
|
+
import { printNextSteps } from "../scaffold/presentation.js";
|
|
43
|
+
import { assertGeneratedWritePlan, removeOwnedLegacyOpenCodeAgents, writeGeneratedFile } from "../scaffold/write.js";
|
|
44
|
+
import { renderUsage, resolveCommand, validateCommandOptions } from "./command-registry.js";
|
|
45
|
+
import { executeDeliveryPathSearch } from "./delivery-path-search-command.js";
|
|
46
|
+
import { CliUsageError, parseArgs } from "./parse-args.js";
|
|
47
|
+
|
|
48
|
+
const deliveryMapDependencies = Object.freeze({
|
|
49
|
+
evaluateDoctor,
|
|
50
|
+
buildStatusCard,
|
|
51
|
+
postApprovalTransition,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function createHandlers({ io, env, exec }) {
|
|
55
|
+
const installerAdapters = exec ? { exec, io } : { io };
|
|
56
|
+
const scaffoldHandler = (options) => runScaffold(options, io);
|
|
57
|
+
return new Map([
|
|
58
|
+
["codex-repo", scaffoldHandler],
|
|
59
|
+
["copilot", scaffoldHandler],
|
|
60
|
+
["opencode-repo", scaffoldHandler],
|
|
61
|
+
["both", scaffoldHandler],
|
|
62
|
+
["init", scaffoldHandler],
|
|
63
|
+
["config", scaffoldHandler],
|
|
64
|
+
["run-create", (options) => {
|
|
65
|
+
io.log(createRun(options.dir, options.runId));
|
|
66
|
+
return 0;
|
|
67
|
+
}],
|
|
68
|
+
["run-migrate", (options) => {
|
|
69
|
+
io.log(JSON.stringify(migrateLegacy(options.dir, options.runId), null, 2));
|
|
70
|
+
return 0;
|
|
71
|
+
}],
|
|
72
|
+
["run-render-legacy", (options) => {
|
|
73
|
+
const selected = resolveRuns(options.dir, { runIdArg: options.runId });
|
|
74
|
+
const output = join(options.dir, ".agdf", "control", "AGDF_RUN.md");
|
|
75
|
+
writeLegacyProjection(output, selected.run.path);
|
|
76
|
+
io.log(output);
|
|
77
|
+
return 0;
|
|
78
|
+
}],
|
|
79
|
+
["doctor", (options) => {
|
|
80
|
+
const report = evaluateDoctor(options.dir, options);
|
|
81
|
+
printDoctorReport(report, options.json, io);
|
|
82
|
+
return report.status === "block" ? 2 : 0;
|
|
83
|
+
}],
|
|
84
|
+
["gate-check", (options) => {
|
|
85
|
+
const report = evaluateGateCheck(options.dir, options);
|
|
86
|
+
printGateCheckReport(report, options.json, options.statusCard, io);
|
|
87
|
+
return report.status === "blocked" ? 2 : 0;
|
|
88
|
+
}],
|
|
89
|
+
["delivery-map", (options) => {
|
|
90
|
+
const report = evaluateDeliveryMap(options.dir, options, deliveryMapDependencies);
|
|
91
|
+
printDeliveryMapReport(report, options.json, io);
|
|
92
|
+
return report.status === "block" ? 2 : 0;
|
|
93
|
+
}],
|
|
94
|
+
["delivery-path-search", async (options) => {
|
|
95
|
+
try {
|
|
96
|
+
const result = await executeDeliveryPathSearch(options, io);
|
|
97
|
+
return result.status === "recommendation" ? 0 : 2;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
io.error(`Delivery Path Search failed: ${error.message}`);
|
|
100
|
+
return 2;
|
|
101
|
+
}
|
|
102
|
+
}],
|
|
103
|
+
["opencode-status", (options) => {
|
|
104
|
+
const configDir = env.OPENCODE_CONFIG_DIR || defaultOpenCodeConfigDir();
|
|
105
|
+
const report = evaluateOpenCodeStatus(options.dir, configDir);
|
|
106
|
+
printOpenCodeStatus(report, options.json, io);
|
|
107
|
+
return report.status === "configured" ? 0 : 1;
|
|
108
|
+
}],
|
|
109
|
+
["status", (options) => {
|
|
110
|
+
const report = evaluateGeneralStatus(options.dir, {
|
|
111
|
+
...options,
|
|
112
|
+
configDir: env.OPENCODE_CONFIG_DIR || defaultOpenCodeConfigDir(),
|
|
113
|
+
}, { exec });
|
|
114
|
+
printGeneralStatus(report, { json: options.json, locale: options.language.chat_language, io });
|
|
115
|
+
return report.installation.status === "healthy" ? 0 : 1;
|
|
116
|
+
}],
|
|
117
|
+
["disable", (options) => runDisable(options, { io, exec })],
|
|
118
|
+
["uninstall", (options) => runUninstall(options, { io, env, exec })],
|
|
119
|
+
["codex", (options) => {
|
|
120
|
+
try {
|
|
121
|
+
const installed = installCodexGlobalPlugin(installerAdapters);
|
|
122
|
+
printLifecycleResult(installResult(installed, {
|
|
123
|
+
restartRequired: true,
|
|
124
|
+
nextAction: "Restart Codex, then run npx --yes @agdf/cli@latest codex-repo in a repository where AGDF should be active.",
|
|
125
|
+
}), { json: options.json, locale: options.language.chat_language, io });
|
|
126
|
+
return 0;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
printInstallFailure("codex", error, options, io);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
}],
|
|
132
|
+
["claude", (options) => {
|
|
133
|
+
try {
|
|
134
|
+
const installed = installClaudeGlobalPlugin(installerAdapters);
|
|
135
|
+
printLifecycleResult(installResult(installed, {
|
|
136
|
+
restartRequired: true,
|
|
137
|
+
nextAction: "Restart Claude Code, then verify the plugin in a new session.",
|
|
138
|
+
}), { json: options.json, locale: options.language.chat_language, io });
|
|
139
|
+
return 0;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
printInstallFailure("claude", error, options, io);
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
}],
|
|
145
|
+
["opencode", (options) => {
|
|
146
|
+
const configDir = options.dirExplicit ? options.dir : defaultOpenCodeConfigDir();
|
|
147
|
+
try {
|
|
148
|
+
const result = runLifecyclePhase("plugin_operation", () => installOpenCodeGlobalPlugin(configDir));
|
|
149
|
+
runLifecyclePhase("global_surface", () => installOpenCodeGlobalSurface(configDir));
|
|
150
|
+
const report = runLifecyclePhase("verification", () => evaluateOpenCodeStatus(options.dir, configDir, result.transition));
|
|
151
|
+
const verificationHealthy = report.status === "configured"
|
|
152
|
+
&& report.package.version_status === "current"
|
|
153
|
+
&& report.global_native_surface.complete;
|
|
154
|
+
printLifecycleResult(createLifecycleResult({
|
|
155
|
+
operation: result.transition.status === "updated" ? "update" : "install",
|
|
156
|
+
result: verificationHealthy ? "success" : "partial",
|
|
157
|
+
surface: "opencode",
|
|
158
|
+
scope: "global",
|
|
159
|
+
version: {
|
|
160
|
+
expected: report.package.expected_version,
|
|
161
|
+
installed: report.package.installed_version,
|
|
162
|
+
previous: result.transition.previous_version,
|
|
163
|
+
status: report.package.version_status === "current" ? "verified" : "unknown",
|
|
164
|
+
transition: result.transition.status,
|
|
165
|
+
},
|
|
166
|
+
verification: { status: verificationHealthy ? "healthy" : "degraded", evidence: [report.global_config.path, report.global_native_surface.path] },
|
|
167
|
+
restart: { required: true, reason: "host_reload" },
|
|
168
|
+
next_action: { kind: "restart", text: "Restart OpenCode so it loads the updated global plugin config." },
|
|
169
|
+
}), { json: options.json, locale: options.language.chat_language, io });
|
|
170
|
+
return verificationHealthy ? 0 : 1;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
printInstallFailure("opencode", error, options, io);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
175
|
+
}],
|
|
176
|
+
]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function runLifecyclePhase(phase, operation) {
|
|
180
|
+
try {
|
|
181
|
+
return operation();
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (!error.phase) error.phase = phase;
|
|
184
|
+
if (!error.evidence) error.evidence = {};
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function installResult(installed, { restartRequired, nextAction }) {
|
|
190
|
+
return createLifecycleResult({
|
|
191
|
+
operation: "install",
|
|
192
|
+
result: "success",
|
|
193
|
+
surface: installed.surface,
|
|
194
|
+
scope: "global",
|
|
195
|
+
version: { expected: installed.expectedVersion, installed: installed.installedVersion, status: installed.installedVersion ? "verified" : "unknown" },
|
|
196
|
+
verification: { status: installed.verificationStatus, evidence: installed.evidence },
|
|
197
|
+
restart: { required: restartRequired, reason: restartRequired ? "host_reload" : "none" },
|
|
198
|
+
next_action: { kind: restartRequired ? "restart" : "prompt", text: nextAction },
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function printInstallFailure(surface, error, options, io) {
|
|
203
|
+
const report = lifecycleFailure({
|
|
204
|
+
operation: "install",
|
|
205
|
+
surface,
|
|
206
|
+
scope: "global",
|
|
207
|
+
phase: error.phase || "plugin_operation",
|
|
208
|
+
message: error.message,
|
|
209
|
+
evidence: [error.evidence ?? {}],
|
|
210
|
+
nextAction: `Resolve the ${error.phase || "plugin operation"} failure and rerun npx --yes @agdf/cli@latest ${surface}.`,
|
|
211
|
+
});
|
|
212
|
+
if (options.json) printLifecycleResult(report, { json: true, io });
|
|
213
|
+
else {
|
|
214
|
+
io.error(error.message);
|
|
215
|
+
printLifecycleResult(report, { locale: options.language.chat_language, io });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function runDisable(options, { io, exec }) {
|
|
220
|
+
try {
|
|
221
|
+
const plan = planRepositoryDisable(options.dir, options.surface);
|
|
222
|
+
const applied = applyLifecyclePlan(plan, exec ? { exec } : {});
|
|
223
|
+
const verified = applied.status === "success" ? verifyRepositoryDisabled(options.dir) : { status: "failed", evidence: [] };
|
|
224
|
+
const result = applied.status === "success" && verified.status !== "healthy" ? "failed" : applied.status;
|
|
225
|
+
const report = createLifecycleResult({
|
|
226
|
+
operation: "disable",
|
|
227
|
+
result,
|
|
228
|
+
surface: options.surface,
|
|
229
|
+
scope: "repository",
|
|
230
|
+
verification: { status: verified.status === "healthy" ? "healthy" : "degraded", evidence: [...applied.completed.map((item) => item.path || item.executable), ...verified.evidence] },
|
|
231
|
+
restart: { required: result === "success", reason: result === "success" ? "host_reload" : "none" },
|
|
232
|
+
next_action: result === "success"
|
|
233
|
+
? { kind: "restart", text: "Restart the host in this repository; global AGDF availability and .agdf/control are retained." }
|
|
234
|
+
: { kind: "verify", text: "Inspect the reported repository configuration failure before retrying disable." },
|
|
235
|
+
changes: applied.completed,
|
|
236
|
+
retained: applied.retained,
|
|
237
|
+
failure: applied.error
|
|
238
|
+
? { phase: "repository_configuration", message: applied.error.message }
|
|
239
|
+
: result === "failed" ? { phase: "verification", message: "Repository disable postcondition was not observed." } : null,
|
|
240
|
+
});
|
|
241
|
+
printLifecycleResult(report, { json: options.json, locale: options.language.chat_language, io });
|
|
242
|
+
return result === "success" ? 0 : 1;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
io.error(error.message);
|
|
245
|
+
return 1;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function runUninstall(options, { io, env, exec }) {
|
|
250
|
+
try {
|
|
251
|
+
const configDir = env.OPENCODE_CONFIG_DIR || defaultOpenCodeConfigDir();
|
|
252
|
+
const plan = planGlobalUninstall(options.surface, { configDir });
|
|
253
|
+
if (!options.confirm) {
|
|
254
|
+
const preview = createLifecycleResult({
|
|
255
|
+
operation: "uninstall", result: "preview", surface: options.surface, scope: "global",
|
|
256
|
+
verification: { status: "unknown", evidence: ["non_mutating_preview"] },
|
|
257
|
+
restart: { required: false },
|
|
258
|
+
next_action: { kind: "confirm", text: `Review this preview, then rerun with --surface ${options.surface} --scope global --confirm; ownership is revalidated before apply.` },
|
|
259
|
+
changes: plan.mutations, retained: plan.retained,
|
|
260
|
+
});
|
|
261
|
+
printLifecycleResult(preview, { json: options.json, locale: options.language.chat_language, io });
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
const applied = applyLifecyclePlan(plan, exec ? { exec } : {});
|
|
265
|
+
const verified = applied.status === "success"
|
|
266
|
+
? verifyGlobalUninstall(plan, options.dir, { configDir, exec })
|
|
267
|
+
: { status: "failed", evidence: [] };
|
|
268
|
+
const result = applied.status === "success" && verified.status !== "healthy" ? "failed" : applied.status;
|
|
269
|
+
const report = createLifecycleResult({
|
|
270
|
+
operation: "uninstall", result, surface: options.surface, scope: "global",
|
|
271
|
+
verification: { status: verified.status === "healthy" ? "healthy" : "degraded", evidence: [...applied.completed.map((item) => item.path || item.executable), ...verified.evidence] },
|
|
272
|
+
restart: { required: result === "success", reason: result === "success" ? "host_reload" : "none" },
|
|
273
|
+
next_action: result === "success"
|
|
274
|
+
? { kind: "restart", text: "Restart the host; the uninstall postcondition has already been verified." }
|
|
275
|
+
: { kind: "verify", text: "Inspect the reported uninstall or verification failure before retrying." },
|
|
276
|
+
changes: applied.completed, retained: applied.retained,
|
|
277
|
+
failure: applied.error
|
|
278
|
+
? { phase: "plugin_operation", message: applied.error.message }
|
|
279
|
+
: result === "failed" ? { phase: "verification", message: "Global uninstall postcondition was not observed." } : null,
|
|
280
|
+
});
|
|
281
|
+
printLifecycleResult(report, { json: options.json, locale: options.language.chat_language, io });
|
|
282
|
+
return result === "success" ? 0 : 1;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
io.error(error.message);
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function runScaffold(options, io) {
|
|
290
|
+
const files = generatedFilesForTarget(options.target, options.dir, options.force, options.language);
|
|
291
|
+
const wroteAgentsFragment = files.some((file) => file.path === agdfFragmentPath);
|
|
292
|
+
const wroteOpenCodeConfigFragment = files.some((file) => file.path === openCodeConfigFragmentPath);
|
|
293
|
+
let removedOpenCodeAgents = [];
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
assertGeneratedWritePlan(options.dir, files, options.force);
|
|
297
|
+
for (const file of files) {
|
|
298
|
+
writeGeneratedFile(options.dir, file.path, file.content, options.force, file.allowOverwrite);
|
|
299
|
+
}
|
|
300
|
+
if (options.target === "opencode-repo") removedOpenCodeAgents = removeOwnedLegacyOpenCodeAgents(options.dir);
|
|
301
|
+
} catch (error) {
|
|
302
|
+
io.error(error.message);
|
|
303
|
+
return 1;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
printNextSteps(options.target, options.dir, files, wroteAgentsFragment, wroteOpenCodeConfigFragment, removedOpenCodeAgents, io);
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function runCli(argv = process.argv.slice(2), adapters = {}) {
|
|
311
|
+
const io = adapters.io ?? console;
|
|
312
|
+
const env = adapters.env ?? process.env;
|
|
313
|
+
let parsed;
|
|
314
|
+
try {
|
|
315
|
+
parsed = parseArgs(argv, adapters.parser);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (!(error instanceof CliUsageError)) throw error;
|
|
318
|
+
io.error(error.message);
|
|
319
|
+
if (error.showUsage) io.log(renderUsage());
|
|
320
|
+
return error.exitCode;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (parsed.kind === "help") {
|
|
324
|
+
io.log(renderUsage());
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let options;
|
|
329
|
+
try {
|
|
330
|
+
options = validateCommandOptions(parsed.options);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
io.error(error instanceof Error ? error.message : String(error));
|
|
333
|
+
return 1;
|
|
334
|
+
}
|
|
335
|
+
const command = resolveCommand(options.target);
|
|
336
|
+
if (!command) throw new Error(`No handler is registered for ${options.target}.`);
|
|
337
|
+
const handler = createHandlers({ io, env, exec: adapters.exec }).get(command.handler);
|
|
338
|
+
if (!handler) throw new Error(`No implementation is registered for ${command.name}.`);
|
|
339
|
+
return await handler(options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export const main = runCli;
|