create-agdf 0.9.0 → 0.9.3

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.
@@ -0,0 +1,97 @@
1
+ import { createHash } from "node:crypto";
2
+ import { lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join, relative, resolve, sep } from "node:path";
5
+
6
+ function schemaError(message) {
7
+ return Object.assign(new Error(message), { code: "EVAL_SCHEMA_INVALID" });
8
+ }
9
+
10
+ export function resolveInside(root, candidate) {
11
+ if (typeof candidate !== "string" || !candidate || candidate.startsWith("/")) throw schemaError(`unsafe eval path: ${candidate}`);
12
+ const target = resolve(root, candidate);
13
+ const rel = relative(resolve(root), target);
14
+ if (!rel || rel === ".." || rel.startsWith(`..${sep}`)) throw schemaError(`unsafe eval path: ${candidate}`);
15
+ return target;
16
+ }
17
+
18
+ function fixtureContent(path, description, controlState) {
19
+ if (path.endsWith(".json")) return `${JSON.stringify({ fixture: description, control_state: controlState }, null, 2)}\n`;
20
+ if (path.endsWith("RUN_STATE.md") && controlState === "ur-approved-structured") return `# AGDF Run State\n\n- schema_version: 1\n- run_id: example\n- lifecycle: active\n- mode: structured_delivery\n- current_gate: PRD\n- control_state: ${controlState}\n\n## Approvals\n\n| Gate | Status | Evidence |\n|---|---|---|\n| UR | approved | Approval: UR |\n| PRD | pending | none |\n`;
21
+ if (path.endsWith("RUN_STATE.md")) return `# AGDF Run State\n\n- schema_version: 1\n- run_id: example\n- lifecycle: active\n- control_state: ${controlState}\n- evidence: ${description}\n`;
22
+ if (path.endsWith("BROWNFIELD_REVIEW.md")) return `# Brownfield Review\n\n- decision: pass\n- mode_slice_decision: structured_delivery\n- scope_reason: Existing runtime owners are affected.\n- evidence: Canonical owners and regression surface inspected.\n- required_next_gate: PRD\n`;
23
+ if (path.endsWith("TP.md")) return `# Approved Task and Test Plan\n\n## Status\n\n- status: approved\n- evidence: ${description}\n\n## Tasks\n\n| task_id | acceptance |\n|---|---|\n| FIXTURE-001 | Preserve the canonical owner and test its boundary. |\n`;
24
+ if (path.endsWith("UR.md")) return `# Approved User Requirements\n\n- status: approved\n- objective: ${description}\n`;
25
+ if (path.endsWith("QA_REPORT.md")) return `# QA Report\n\n- decision: pass\n- evidence: ${description}\n- missing_evidence: none\n`;
26
+ if (path.endsWith("OR.md")) return `# Orchestration Report\n\n- decision: pass\n- delivered: ${description}\n- required_next_step: explicit delivery handoff\n`;
27
+ if (path.endsWith(".md")) return `# Governed Fixture Artefact\n\n${description}\n\nControl state: ${controlState}\n`;
28
+ if (path.includes("test") || path.endsWith(".spec.js")) return `// ${description}\nexport const fixtureTest = true;\n`;
29
+ return `// ${description}\nexport const fixtureState = ${JSON.stringify(controlState)};\n`;
30
+ }
31
+
32
+ export function materializeFixture(catalog, testCase, options = {}) {
33
+ const definition = catalog.repositories?.[testCase.repository_fixture];
34
+ if (!definition || !catalog.control_states?.includes(testCase.control_state_fixture)) throw schemaError(`unknown fixture for ${testCase.case_id}`);
35
+ const root = mkdtempSync(join(options.tmpRoot ?? tmpdir(), "agdf-skill-eval-"));
36
+ try {
37
+ for (const path of definition.files ?? []) {
38
+ const target = resolveInside(root, path);
39
+ mkdirSync(dirname(target), { recursive: true });
40
+ writeFileSync(target, fixtureContent(path, definition.description, testCase.control_state_fixture), "utf8");
41
+ }
42
+ const marker = resolveInside(root, ".agdf/control/EVAL_FIXTURE.md");
43
+ mkdirSync(dirname(marker), { recursive: true });
44
+ writeFileSync(marker, `# Evaluation Fixture\n\nRepository: ${testCase.repository_fixture}\nControl state: ${testCase.control_state_fixture}\n`, "utf8");
45
+ return root;
46
+ } catch (error) {
47
+ rmSync(root, { recursive: true, force: true });
48
+ throw error;
49
+ }
50
+ }
51
+
52
+ export function snapshotWorkspace(root) {
53
+ const canonicalRoot = realpathSync(root);
54
+ const snapshot = new Map();
55
+ function visit(directory) {
56
+ for (const name of readdirSync(directory).sort()) {
57
+ const path = join(directory, name);
58
+ const rel = relative(root, path).split(sep).join("/");
59
+ const stat = lstatSync(path);
60
+ if (stat.isSymbolicLink()) {
61
+ let target;
62
+ try { target = realpathSync(path); } catch { throw schemaError(`broken fixture symlink: ${rel}`); }
63
+ const targetRel = relative(canonicalRoot, target);
64
+ if (targetRel === ".." || targetRel.startsWith(`..${sep}`)) throw schemaError(`fixture symlink escapes workspace: ${rel}`);
65
+ snapshot.set(rel, `symlink:${targetRel.split(sep).join("/")}`);
66
+ } else if (stat.isDirectory()) visit(path);
67
+ else if (stat.isFile()) snapshot.set(rel, createHash("sha256").update(readFileSync(path)).digest("hex"));
68
+ }
69
+ }
70
+ visit(root);
71
+ return snapshot;
72
+ }
73
+
74
+ export function workspaceEvidence(root) {
75
+ const snapshot = snapshotWorkspace(root);
76
+ return [...snapshot.keys()].map((path) => {
77
+ const target = resolveInside(root, path);
78
+ const stat = lstatSync(target);
79
+ return stat.isSymbolicLink() ? `--- ${path} (symlink) ---` : `--- ${path} ---\n${readFileSync(target, "utf8")}`;
80
+ }).join("\n");
81
+ }
82
+
83
+ export function changedPaths(before, after) {
84
+ return [...new Set([...before.keys(), ...after.keys()])].filter((path) => before.get(path) !== after.get(path)).sort();
85
+ }
86
+
87
+ function pathAllowed(path, allowed) {
88
+ return allowed.some((rule) => rule === path || (rule.endsWith("/**") && (path === rule.slice(0, -3) || path.startsWith(rule.slice(0, -2)))));
89
+ }
90
+
91
+ export function mutationViolations(changed, allowed = []) {
92
+ return changed.filter((path) => !pathAllowed(path, allowed));
93
+ }
94
+
95
+ export function disposeFixture(root) {
96
+ rmSync(root, { recursive: true, force: true });
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-agdf",
3
- "version": "0.9.0",
3
+ "version": "0.9.3",
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": [
@@ -24,13 +24,18 @@
24
24
  "test:delivery-path-search": "node ./scripts/delivery-path-search-test.js",
25
25
  "test:delivery-path-search-unit": "node ./scripts/delivery-path-search-unit-test.js",
26
26
  "test:delivery-path-search-generator": "node ./scripts/delivery-path-search-generator-test.js",
27
+ "test:cli-modularization": "node ./scripts/cli-modularization-test.js",
27
28
  "test:control-state": "node ./scripts/control-state-test.js",
28
29
  "test:interaction-presentation": "node ./scripts/interaction-presentation-test.js",
29
30
  "test:verified-change": "node ./scripts/verified-change-test.js",
31
+ "test:runtime-integrity-layout": "node ./scripts/runtime-integrity-layout-test.js",
30
32
  "test:runtime-integrity-negative": "node ./scripts/runtime-integrity-negative-test.js",
33
+ "test:skill-evals": "node ./scripts/skill-evals-test.js",
34
+ "eval:skills": "node ./scripts/run-skill-evals.js",
35
+ "eval:skills:record": "node ./scripts/record-skill-evals.js",
31
36
  "test:release-bootstrap": "node ./scripts/release-bootstrap-smoke-test.js",
32
37
  "test:routing": "npm run sync-package-assets && node ./scripts/test-routing.js",
33
- "smoke-test": "npm run sync-package-assets && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-negative && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
38
+ "smoke-test": "npm run sync-package-assets && npm run test:cli-modularization && npm run test:control-state && npm run test:interaction-presentation && npm run test:verified-change && npm run test:runtime-integrity-layout && npm run test:runtime-integrity-negative && npm run test:skill-evals && npm run eval:skills && npm run test:delivery-path-search && npm run test:delivery-path-search-unit && npm run test:delivery-path-search-generator && node ./scripts/smoke-test.js && node ./scripts/test-routing.js",
34
39
  "prepack": "npm run sync-package-assets"
35
40
  },
36
41
  "keywords": [