@zizzfizzix/aef 0.1.0 → 0.2.0

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.
@@ -5,6 +5,10 @@ Capture is on by default and writes only files here — **zero egress**, always
5
5
  `git diff`. The `improve-framework` skill (feedback-drain mode) batches these into a framework PR;
6
6
  `project`-scoped lessons never land here (they go to `.ai/lessons.md`).
7
7
 
8
+ > **Note:** `improve-framework` is in the `framework` tier (opt-in). Install it by adding
9
+ > `"tiers": ["framework"]` to `framework.config.json` and re-running `aef init`, or by running
10
+ > `aef add framework`.
11
+
8
12
  ## Scope tag
9
13
 
10
14
  Each entry declares a `scope`:
@@ -107,6 +107,7 @@ var FrameworkConfigSchema = z.object({
107
107
  defaultBranch: z.string().optional(),
108
108
  labels: z.array(z.string()).optional()
109
109
  }).strict().optional(),
110
+ tiers: z.array(z.string()).describe('Opt-in tiers to install in addition to the defaults (e.g. ["framework", "automation"]).').optional(),
110
111
  source: z.object({
111
112
  path: z.string().nullable().describe("Local checkout path; if null, clone repo on demand.").optional(),
112
113
  repo: z.string().nullable().optional()
@@ -142,14 +143,20 @@ var TiersSchema = z.object({
142
143
  // src/core/select.ts
143
144
  import { readFileSync as readFileSync2 } from "fs";
144
145
  import { join as join2 } from "path";
146
+ function loadTiers(root) {
147
+ return TiersSchema.parse(JSON.parse(readFileSync2(join2(root, "core/ai/skills/tiers.json"), "utf8")));
148
+ }
145
149
  function selectSkills(root, config) {
146
- const tiers = TiersSchema.parse(JSON.parse(readFileSync2(join2(root, "core/ai/skills/tiers.json"), "utf8")));
150
+ const tiers = loadTiers(root);
147
151
  const requires = tiers.requires ?? {};
152
+ const skipped = [];
153
+ for (const t of config.tiers ?? [])
154
+ if (!(t in tiers.tiers)) skipped.push(`tier '${t}' (unknown \u2014 not in tiers.json)`);
155
+ const activeTiers = [...tiers.default, ...config.tiers ?? []];
148
156
  const candidates = [];
149
- for (const t of tiers.default)
157
+ for (const t of activeTiers)
150
158
  for (const s of tiers.tiers[t]?.skills ?? []) if (!candidates.includes(s)) candidates.push(s);
151
159
  const skills = [];
152
- const skipped = [];
153
160
  for (const s of candidates) {
154
161
  const axis = requires[s];
155
162
  if (axis && !config[axis]) skipped.push(`${s} (needs ${axis})`);
@@ -201,7 +208,8 @@ export {
201
208
  FrameworkConfigSchema,
202
209
  AdapterSchema,
203
210
  TiersSchema,
211
+ loadTiers,
204
212
  selectSkills,
205
213
  mergeFile
206
214
  };
207
- //# sourceMappingURL=chunk-YENPV3OD.js.map
215
+ //# sourceMappingURL=chunk-QEUDIAAQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/render.ts","../src/core/contracts.ts","../src/core/select.ts","../src/core/merge.ts"],"sourcesContent":["// Pure skill renderer — the heart of the framework. No I/O beyond reading source\n// files; returns { rendered, manifest, digest }. Kept dependency-light (node builtins\n// + a type-only contract import) because it runs in consumer repos, in CI, and inside\n// the improve-framework skill.\n//\n// Properties (proven in docs/render-model.md and asserted by the gate): convergence,\n// determinism, provenance, per-skill digest scoping.\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport type { FrameworkConfig } from './contracts.js'\n\nconst AXES = ['orm', 'ui', 'stack'] as const\ntype Axis = (typeof AXES)[number]\n\nconst sha = (s: string): string => createHash('sha256').update(s).digest('hex').slice(0, 12)\nconst norm = (s: string): string => s.replace(/\\r\\n/g, '\\n')\n\nconst OPEN = /^<!--\\s*SLOT:([\\w.-]+)\\s*-->$/\nconst CLOSE = /^<!--\\s*\\/SLOT:([\\w.-]+)\\s*-->$/\n\n// Shared, single source of truth for what a slot marker is — imported by the gate so\n// the gate and the renderer can never disagree (a slot is a marker ALONE ON ITS OWN\n// LINE; `SLOT:` mentioned in prose/backticks is not a slot and is never touched).\nexport const SLOT_OPEN = OPEN\nexport const SLOT_ANY = /^<!--\\s*\\/?SLOT:[\\w.-]+\\s*-->$/\n\nexport interface InputRef {\n path: string\n sha: string\n}\n\nexport interface Region {\n source: string\n adapter: string | null\n slot: string | null\n startLine: number\n endLine: number\n}\n\nexport interface RenderManifest {\n skill: string\n selection: Partial<Record<Axis, string>>\n digest: string\n note: string\n inputs: InputRef[]\n regions: Region[]\n}\n\nexport interface RenderResult {\n rendered: string\n manifest: RenderManifest\n digest: string\n}\n\ninterface SlotProvider {\n adapter: string\n axis: Axis\n source: string\n content: string\n}\n\nexport function renderSkill(root: string, config: FrameworkConfig, skill: string): RenderResult {\n const rel = (p: string): string => relative(root, p)\n\n // Active adapter per axis -> slot lookup (no inputs counted here).\n const selected: Partial<Record<Axis, string>> = {}\n for (const axis of AXES) {\n const value = config[axis]\n if (value) selected[axis] = value\n }\n\n const slotProviders: Record<string, SlotProvider> = {}\n for (const [axis, adapter] of Object.entries(selected) as [Axis, string][]) {\n const adDir = join(root, 'adapters', axis, adapter)\n const adPath = join(adDir, 'adapter.json')\n if (!existsSync(adPath)) continue\n const ad = JSON.parse(readFileSync(adPath, 'utf8')) as { slots?: Record<string, string> }\n for (const [slot, file] of Object.entries(ad.slots ?? {})) {\n const fp = join(adDir, file)\n const content = norm(readFileSync(fp, 'utf8')).replace(/\\s+$/, '') + '\\n'\n slotProviders[slot] = { adapter, axis, source: rel(fp), content }\n }\n }\n\n const genPath = join(root, 'core/ai/skills', skill, 'SKILL.md')\n const generic = norm(readFileSync(genPath, 'utf8'))\n\n // Inputs = only files that actually compose THIS skill (generic + filled fragments).\n const inputs: InputRef[] = [{ path: rel(genPath), sha: sha(generic) }]\n const seenInput = new Set<string>([rel(genPath)])\n\n const lines = generic.split('\\n')\n const out: string[] = []\n const regions: Region[] = []\n let bodyLine = 0\n const push = (line: string, source: string, meta?: { adapter: string; slot: string }): void => {\n out.push(line)\n bodyLine++\n const last = regions[regions.length - 1]\n const slot = meta?.slot ?? null\n if (last && last.source === source && last.slot === slot && last.endLine === bodyLine - 1) {\n last.endLine = bodyLine\n } else {\n regions.push({ source, adapter: meta?.adapter ?? null, slot, startLine: bodyLine, endLine: bodyLine })\n }\n }\n\n let i = 0\n while (i < lines.length) {\n const current = lines[i] as string\n const open = current.match(OPEN)\n if (open) {\n const slot = open[1] as string\n let j = i + 1\n while (\n j < lines.length &&\n !(CLOSE.test(lines[j] as string) && (lines[j] as string).match(CLOSE)![1] === slot)\n )\n j++\n const provider = slotProviders[slot]\n if (provider) {\n if (!seenInput.has(provider.source)) {\n seenInput.add(provider.source)\n inputs.push({ path: provider.source, sha: sha(provider.content) })\n }\n for (const cl of provider.content.replace(/\\n$/, '').split('\\n')) {\n push(cl, provider.source, { adapter: provider.adapter, slot })\n }\n } // no provider (axis unselected, or active adapter omits this slot) -> prune\n i = j + 1\n continue\n }\n push(current, rel(genPath))\n i++\n }\n\n const sortedInputs = [...inputs].sort((a, b) => a.path.localeCompare(b.path))\n const digest = sha(JSON.stringify(sortedInputs))\n const header = `<!-- generated by aef render — do not hand-edit; use the improve-framework skill or edit the framework source. digest:${digest} -->`\n const rendered = header + '\\n\\n' + out.join('\\n') + '\\n'\n\n const manifest: RenderManifest = {\n skill,\n selection: selected,\n digest,\n note: 'region line numbers are body-relative (line 1 = first line after the 2-line header); digest covers consumed inputs only',\n inputs: sortedInputs,\n regions,\n }\n return { rendered, manifest, digest }\n}\n","// Typed contracts — the single source of truth for every config/adapter shape the\n// framework reads. zod gives us (1) runtime validation in the CLI, (2) inferred TS\n// types for the codebase, and (3) JSON Schema (see scripts/gen-schemas.ts) published\n// under schemas/ for editor/CI validation. Change a shape here and regenerate.\nimport { z } from 'zod'\n\nconst slotName = z\n .string()\n .regex(/^[a-z][\\w-]*\\.[a-z][\\w-]*$/, \"slot names are '<axis>.<key>', e.g. 'orm.cheatsheet'\")\n\n/** Per-consumer configuration the renderer, init/sync, and skills all read. */\nexport const FrameworkConfigSchema = z\n .object({\n $schema: z.string().optional(),\n projectName: z.string().describe('Substituted for {{PROJECT_NAME}} in templates.').optional(),\n harnesses: z\n .array(z.string())\n .min(1)\n .describe('Active harness adapters; each wires its files + skills dir.'),\n orm: z.string().nullable().describe('Selected ORM adapter, or null to omit ORM skills.').optional(),\n ui: z.string().nullable().optional(),\n stack: z.string().nullable().optional(),\n paths: z\n .object({\n modulesRoot: z.string().optional(),\n specsRoot: z.string().optional(),\n testsRoot: z.string().optional(),\n })\n .strict()\n .optional(),\n validation: z.array(z.string()).describe('Commands skills run as the verification gate.').optional(),\n git: z\n .object({\n defaultBranch: z.string().optional(),\n labels: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n tiers: z\n .array(z.string())\n .describe('Opt-in tiers to install in addition to the defaults (e.g. [\"framework\", \"automation\"]).')\n .optional(),\n source: z\n .object({\n path: z\n .string()\n .nullable()\n .describe('Local checkout path; if null, clone repo on demand.')\n .optional(),\n repo: z.string().nullable().optional(),\n })\n .strict()\n .describe('Where improve-framework finds the framework SOURCE.')\n .optional(),\n feedback: z\n .object({\n capture: z.boolean().default(true).describe('Write scope-tagged lessons locally; zero egress.'),\n upstream: z\n .object({\n mode: z.enum(['scheduled-pr', 'prompt', 'off']).default('scheduled-pr'),\n channel: z.enum(['pr', 'issue', 'fork']).default('pr'),\n schedule: z.string().default('weekly'),\n sanitize: z.boolean().default(true),\n requireHumanApproval: z.boolean().default(true),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n })\n .strict()\n\n/** An adapter declares which skills it augments and the slot content it provides. */\nexport const AdapterSchema = z\n .object({\n $schema: z.string().optional(),\n name: z.string().describe(\"Adapter id, e.g. 'drizzle'.\"),\n axis: z.string().describe('Variation family: orm | ui | stack | harness.'),\n description: z.string().optional(),\n skillsDir: z.string().describe('Harness only: directory the per-skill links go in.').optional(),\n linkBase: z.string().describe('Harness only: relative link target base from skillsDir.').optional(),\n augments: z.array(z.string()).describe('Skill ids this adapter contributes slot content to.').optional(),\n slots: z\n .record(slotName, z.string())\n .describe('slotName -> fragment path, relative to the adapter dir.')\n .optional(),\n tiers: z\n .array(z.object({ skill: z.string(), tier: z.string() }).strict())\n .describe('Optional skills this adapter adds, and the tier each joins.')\n .optional(),\n })\n .strict()\n\n/** core/ai/skills/tiers.json — declares the default tier set and per-skill axis requirements. */\nexport const TiersSchema = z\n .object({\n default: z.array(z.string()),\n tiers: z.record(z.string(), z.object({ skills: z.array(z.string()) }).passthrough()),\n requires: z.record(z.string(), z.string()).optional(),\n })\n .passthrough()\n\nexport type FrameworkConfig = z.infer<typeof FrameworkConfigSchema>\nexport type Adapter = z.infer<typeof AdapterSchema>\nexport type Tiers = z.infer<typeof TiersSchema>\n","// Tier resolution — turns the configured axis selections into the converged skill set.\n// A skill whose required axis is unconfigured is skipped (reported, not errored).\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { FrameworkConfig, Tiers } from './contracts.js'\nimport { TiersSchema } from './contracts.js'\n\nexport interface SkillSelection {\n skills: string[]\n skipped: string[]\n}\n\nexport function loadTiers(root: string): Tiers {\n return TiersSchema.parse(JSON.parse(readFileSync(join(root, 'core/ai/skills/tiers.json'), 'utf8')))\n}\n\nexport function selectSkills(root: string, config: FrameworkConfig): SkillSelection {\n const tiers = loadTiers(root)\n const requires = tiers.requires ?? {}\n const skipped: string[] = []\n\n for (const t of config.tiers ?? [])\n if (!(t in tiers.tiers)) skipped.push(`tier '${t}' (unknown — not in tiers.json)`)\n\n const activeTiers = [...tiers.default, ...(config.tiers ?? [])]\n const candidates: string[] = []\n for (const t of activeTiers)\n for (const s of tiers.tiers[t]?.skills ?? []) if (!candidates.includes(s)) candidates.push(s)\n\n const skills: string[] = []\n for (const s of candidates) {\n const axis = requires[s]\n if (axis && !config[axis as keyof FrameworkConfig]) skipped.push(`${s} (needs ${axis})`)\n else skills.push(s)\n }\n return { skills: skills.sort(), skipped }\n}\n","// 3-way merge delegated to git — no custom merge engine. BASE = last render,\n// LOCAL = on-disk (with consumer edits), THEIRS = fresh framework render.\n// Returns the merged text and the conflict count git reports.\nimport { writeFileSync, mkdtempSync, rmSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { spawnSync } from 'node:child_process'\n\nexport interface MergeResult {\n merged: string\n conflicts: number\n}\n\nexport function mergeFile(local: string, base: string, theirs: string): MergeResult {\n const dir = mkdtempSync(join(tmpdir(), 'agentic-merge-'))\n const lp = join(dir, 'local')\n const bp = join(dir, 'base')\n const tp = join(dir, 'theirs')\n writeFileSync(lp, local)\n writeFileSync(bp, base)\n writeFileSync(tp, theirs)\n const r = spawnSync(\n 'git',\n [\n 'merge-file',\n '-p',\n '-L',\n 'ours (your local edits)',\n '-L',\n 'base (last sync)',\n '-L',\n 'theirs (framework update)',\n lp,\n bp,\n tp,\n ],\n { encoding: 'utf8' },\n )\n rmSync(dir, { recursive: true, force: true })\n if (r.status === null || r.status < 0 || r.status >= 255)\n throw new Error(`git merge-file failed: ${r.stderr || String(r.error)}`)\n return { merged: r.stdout, conflicts: r.status }\n}\n"],"mappings":";AAOA,SAAS,cAAc,kBAAkB;AACzC,SAAS,MAAM,gBAAgB;AAC/B,SAAS,kBAAkB;AAG3B,IAAM,OAAO,CAAC,OAAO,MAAM,OAAO;AAGlC,IAAM,MAAM,CAAC,MAAsB,WAAW,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC3F,IAAM,OAAO,CAAC,MAAsB,EAAE,QAAQ,SAAS,IAAI;AAE3D,IAAM,OAAO;AACb,IAAM,QAAQ;AAKP,IAAM,YAAY;AAClB,IAAM,WAAW;AAqCjB,SAAS,YAAY,MAAc,QAAyB,OAA6B;AAC9F,QAAM,MAAM,CAAC,MAAsB,SAAS,MAAM,CAAC;AAGnD,QAAM,WAA0C,CAAC;AACjD,aAAW,QAAQ,MAAM;AACvB,UAAM,QAAQ,OAAO,IAAI;AACzB,QAAI,MAAO,UAAS,IAAI,IAAI;AAAA,EAC9B;AAEA,QAAM,gBAA8C,CAAC;AACrD,aAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAuB;AAC1E,UAAM,QAAQ,KAAK,MAAM,YAAY,MAAM,OAAO;AAClD,UAAM,SAAS,KAAK,OAAO,cAAc;AACzC,QAAI,CAAC,WAAW,MAAM,EAAG;AACzB,UAAM,KAAK,KAAK,MAAM,aAAa,QAAQ,MAAM,CAAC;AAClD,eAAW,CAAC,MAAM,IAAI,KAAK,OAAO,QAAQ,GAAG,SAAS,CAAC,CAAC,GAAG;AACzD,YAAM,KAAK,KAAK,OAAO,IAAI;AAC3B,YAAM,UAAU,KAAK,aAAa,IAAI,MAAM,CAAC,EAAE,QAAQ,QAAQ,EAAE,IAAI;AACrE,oBAAc,IAAI,IAAI,EAAE,SAAS,MAAM,QAAQ,IAAI,EAAE,GAAG,QAAQ;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,UAAU,KAAK,MAAM,kBAAkB,OAAO,UAAU;AAC9D,QAAM,UAAU,KAAK,aAAa,SAAS,MAAM,CAAC;AAGlD,QAAM,SAAqB,CAAC,EAAE,MAAM,IAAI,OAAO,GAAG,KAAK,IAAI,OAAO,EAAE,CAAC;AACrE,QAAM,YAAY,oBAAI,IAAY,CAAC,IAAI,OAAO,CAAC,CAAC;AAEhD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,MAAgB,CAAC;AACvB,QAAM,UAAoB,CAAC;AAC3B,MAAI,WAAW;AACf,QAAM,OAAO,CAAC,MAAc,QAAgB,SAAmD;AAC7F,QAAI,KAAK,IAAI;AACb;AACA,UAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,UAAM,OAAO,MAAM,QAAQ;AAC3B,QAAI,QAAQ,KAAK,WAAW,UAAU,KAAK,SAAS,QAAQ,KAAK,YAAY,WAAW,GAAG;AACzF,WAAK,UAAU;AAAA,IACjB,OAAO;AACL,cAAQ,KAAK,EAAE,QAAQ,SAAS,MAAM,WAAW,MAAM,MAAM,WAAW,UAAU,SAAS,SAAS,CAAC;AAAA,IACvG;AAAA,EACF;AAEA,MAAI,IAAI;AACR,SAAO,IAAI,MAAM,QAAQ;AACvB,UAAM,UAAU,MAAM,CAAC;AACvB,UAAM,OAAO,QAAQ,MAAM,IAAI;AAC/B,QAAI,MAAM;AACR,YAAM,OAAO,KAAK,CAAC;AACnB,UAAI,IAAI,IAAI;AACZ,aACE,IAAI,MAAM,UACV,EAAE,MAAM,KAAK,MAAM,CAAC,CAAW,KAAM,MAAM,CAAC,EAAa,MAAM,KAAK,EAAG,CAAC,MAAM;AAE9E;AACF,YAAM,WAAW,cAAc,IAAI;AACnC,UAAI,UAAU;AACZ,YAAI,CAAC,UAAU,IAAI,SAAS,MAAM,GAAG;AACnC,oBAAU,IAAI,SAAS,MAAM;AAC7B,iBAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,KAAK,IAAI,SAAS,OAAO,EAAE,CAAC;AAAA,QACnE;AACA,mBAAW,MAAM,SAAS,QAAQ,QAAQ,OAAO,EAAE,EAAE,MAAM,IAAI,GAAG;AAChE,eAAK,IAAI,SAAS,QAAQ,EAAE,SAAS,SAAS,SAAS,KAAK,CAAC;AAAA,QAC/D;AAAA,MACF;AACA,UAAI,IAAI;AACR;AAAA,IACF;AACA,SAAK,SAAS,IAAI,OAAO,CAAC;AAC1B;AAAA,EACF;AAEA,QAAM,eAAe,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC5E,QAAM,SAAS,IAAI,KAAK,UAAU,YAAY,CAAC;AAC/C,QAAM,SAAS,8HAAyH,MAAM;AAC9I,QAAM,WAAW,SAAS,SAAS,IAAI,KAAK,IAAI,IAAI;AAEpD,QAAM,WAA2B;AAAA,IAC/B;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,QAAQ;AAAA,IACR;AAAA,EACF;AACA,SAAO,EAAE,UAAU,UAAU,OAAO;AACtC;;;ACnJA,SAAS,SAAS;AAElB,IAAM,WAAW,EACd,OAAO,EACP,MAAM,8BAA8B,sDAAsD;AAGtF,IAAM,wBAAwB,EAClC,OAAO;AAAA,EACN,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS,gDAAgD,EAAE,SAAS;AAAA,EAC5F,WAAW,EACR,MAAM,EAAE,OAAO,CAAC,EAChB,IAAI,CAAC,EACL,SAAS,6DAA6D;AAAA,EACzE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD,EAAE,SAAS;AAAA,EAClG,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACnC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtC,OAAO,EACJ,OAAO;AAAA,IACN,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,IAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,+CAA+C,EAAE,SAAS;AAAA,EACnG,KAAK,EACF,OAAO;AAAA,IACN,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,IACnC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACZ,OAAO,EACJ,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,yFAAyF,EAClG,SAAS;AAAA,EACZ,QAAQ,EACL,OAAO;AAAA,IACN,MAAM,EACH,OAAO,EACP,SAAS,EACT,SAAS,qDAAqD,EAC9D,SAAS;AAAA,IACZ,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,CAAC,EACA,OAAO,EACP,SAAS,qDAAqD,EAC9D,SAAS;AAAA,EACZ,UAAU,EACP,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS,kDAAkD;AAAA,IAC9F,UAAU,EACP,OAAO;AAAA,MACN,MAAM,EAAE,KAAK,CAAC,gBAAgB,UAAU,KAAK,CAAC,EAAE,QAAQ,cAAc;AAAA,MACtE,SAAS,EAAE,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,EAAE,QAAQ,IAAI;AAAA,MACrD,UAAU,EAAE,OAAO,EAAE,QAAQ,QAAQ;AAAA,MACrC,UAAU,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,MAClC,sBAAsB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAChD,CAAC,EACA,OAAO,EACP,SAAS;AAAA,EACd,CAAC,EACA,OAAO,EACP,SAAS;AACd,CAAC,EACA,OAAO;AAGH,IAAM,gBAAgB,EAC1B,OAAO;AAAA,EACN,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,MAAM,EAAE,OAAO,EAAE,SAAS,6BAA6B;AAAA,EACvD,MAAM,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,EACzE,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,WAAW,EAAE,OAAO,EAAE,SAAS,oDAAoD,EAAE,SAAS;AAAA,EAC9F,UAAU,EAAE,OAAO,EAAE,SAAS,yDAAyD,EAAE,SAAS;AAAA,EAClG,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,qDAAqD,EAAE,SAAS;AAAA,EACvG,OAAO,EACJ,OAAO,UAAU,EAAE,OAAO,CAAC,EAC3B,SAAS,yDAAyD,EAClE,SAAS;AAAA,EACZ,OAAO,EACJ,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAChE,SAAS,6DAA6D,EACtE,SAAS;AACd,CAAC,EACA,OAAO;AAGH,IAAM,cAAc,EACxB,OAAO;AAAA,EACN,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,EAC3B,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;AAAA,EACnF,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,EAAE,SAAS;AACtD,CAAC,EACA,YAAY;;;ACnGf,SAAS,gBAAAA,qBAAoB;AAC7B,SAAS,QAAAC,aAAY;AASd,SAAS,UAAU,MAAqB;AAC7C,SAAO,YAAY,MAAM,KAAK,MAAMC,cAAaC,MAAK,MAAM,2BAA2B,GAAG,MAAM,CAAC,CAAC;AACpG;AAEO,SAAS,aAAa,MAAc,QAAyC;AAClF,QAAM,QAAQ,UAAU,IAAI;AAC5B,QAAM,WAAW,MAAM,YAAY,CAAC;AACpC,QAAM,UAAoB,CAAC;AAE3B,aAAW,KAAK,OAAO,SAAS,CAAC;AAC/B,QAAI,EAAE,KAAK,MAAM,OAAQ,SAAQ,KAAK,SAAS,CAAC,sCAAiC;AAEnF,QAAM,cAAc,CAAC,GAAG,MAAM,SAAS,GAAI,OAAO,SAAS,CAAC,CAAE;AAC9D,QAAM,aAAuB,CAAC;AAC9B,aAAW,KAAK;AACd,eAAW,KAAK,MAAM,MAAM,CAAC,GAAG,UAAU,CAAC,EAAG,KAAI,CAAC,WAAW,SAAS,CAAC,EAAG,YAAW,KAAK,CAAC;AAE9F,QAAM,SAAmB,CAAC;AAC1B,aAAW,KAAK,YAAY;AAC1B,UAAM,OAAO,SAAS,CAAC;AACvB,QAAI,QAAQ,CAAC,OAAO,IAA6B,EAAG,SAAQ,KAAK,GAAG,CAAC,WAAW,IAAI,GAAG;AAAA,QAClF,QAAO,KAAK,CAAC;AAAA,EACpB;AACA,SAAO,EAAE,QAAQ,OAAO,KAAK,GAAG,QAAQ;AAC1C;;;ACjCA,SAAS,eAAe,aAAa,cAAc;AACnD,SAAS,QAAAC,aAAY;AACrB,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAOnB,SAAS,UAAU,OAAe,MAAc,QAA6B;AAClF,QAAM,MAAM,YAAYA,MAAK,OAAO,GAAG,gBAAgB,CAAC;AACxD,QAAM,KAAKA,MAAK,KAAK,OAAO;AAC5B,QAAM,KAAKA,MAAK,KAAK,MAAM;AAC3B,QAAM,KAAKA,MAAK,KAAK,QAAQ;AAC7B,gBAAc,IAAI,KAAK;AACvB,gBAAc,IAAI,IAAI;AACtB,gBAAc,IAAI,MAAM;AACxB,QAAM,IAAI;AAAA,IACR;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,EAAE,UAAU,OAAO;AAAA,EACrB;AACA,SAAO,KAAK,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC5C,MAAI,EAAE,WAAW,QAAQ,EAAE,SAAS,KAAK,EAAE,UAAU;AACnD,UAAM,IAAI,MAAM,0BAA0B,EAAE,UAAU,OAAO,EAAE,KAAK,CAAC,EAAE;AACzE,SAAO,EAAE,QAAQ,EAAE,QAAQ,WAAW,EAAE,OAAO;AACjD;","names":["readFileSync","join","readFileSync","join","join"]}
package/dist/cli/index.js CHANGED
@@ -2,15 +2,16 @@
2
2
  import {
3
3
  AdapterSchema,
4
4
  FrameworkConfigSchema,
5
+ loadTiers,
5
6
  mergeFile,
6
7
  renderSkill,
7
8
  selectSkills
8
- } from "../chunk-YENPV3OD.js";
9
+ } from "../chunk-QEUDIAAQ.js";
9
10
 
10
11
  // src/cli/index.ts
11
12
  import { readFileSync as readFileSync8 } from "fs";
12
13
  import { Command } from "commander";
13
- import { ZodError } from "zod";
14
+ import { ZodError, ZodIssueCode } from "zod";
14
15
 
15
16
  // src/cli/commands/init.ts
16
17
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
@@ -71,7 +72,7 @@ function writeConventions(root, out, config) {
71
72
  written.push(".ai/lessons.framework.md");
72
73
  }
73
74
  const outbox = join2(root, "core", "ai", "framework-feedback");
74
- if (existsSync(outbox)) {
75
+ if (existsSync(outbox) && config.tiers?.includes("framework")) {
75
76
  cpSync(outbox, join2(ai, "framework-feedback"), { recursive: true });
76
77
  written.push(".ai/framework-feedback/");
77
78
  }
@@ -99,9 +100,33 @@ function writeManifest(out, config, skills) {
99
100
  mkdirSync(join2(out, ".ai"), { recursive: true });
100
101
  writeFileSync(
101
102
  join2(out, ".ai", ".render-manifest.json"),
102
- JSON.stringify({ selection: pick(config, ["orm", "ui", "stack", "harnesses"]), skills }, null, 2) + "\n"
103
+ JSON.stringify(
104
+ { selection: pick(config, ["orm", "ui", "stack", "harnesses", "tiers"]), skills },
105
+ null,
106
+ 2
107
+ ) + "\n"
103
108
  );
104
109
  }
110
+ function harnessSkillsDir(root, harness) {
111
+ const p2 = join2(root, "adapters", "harness", harness, "adapter.json");
112
+ if (!existsSync(p2)) return null;
113
+ const ad = AdapterSchema.parse(JSON.parse(readFileSync(p2, "utf8")));
114
+ return ad.skillsDir ?? null;
115
+ }
116
+ function wireNewSkills(root, out, config, skills) {
117
+ for (const harness of config.harnesses) {
118
+ const adPath = join2(root, "adapters", "harness", harness, "adapter.json");
119
+ if (!existsSync(adPath)) continue;
120
+ const ad = AdapterSchema.parse(JSON.parse(readFileSync(adPath, "utf8")));
121
+ if (!ad.skillsDir || !ad.linkBase) continue;
122
+ const hdir = join2(out, ad.skillsDir);
123
+ mkdirSync(hdir, { recursive: true });
124
+ for (const skill of skills) {
125
+ const link = join2(hdir, skill);
126
+ if (!existsSync(link) && !isLink(link)) symlinkSync(`${ad.linkBase}/${skill}`, link);
127
+ }
128
+ }
129
+ }
105
130
  function wireHarnesses(root, out, config, skills, useCopy) {
106
131
  const aiSkills = join2(out, ".ai", "skills");
107
132
  const wired = [];
@@ -180,7 +205,26 @@ async function runWizard(root) {
180
205
  // src/cli/commands/init.ts
181
206
  async function runInit(opts) {
182
207
  const root = FRAMEWORK_ROOT;
183
- const raw = opts.interactive ? await runWizard(root) : JSON.parse(readFileSync2(opts.config ?? join4(root, "framework.config.example.json"), "utf8"));
208
+ if (opts.config && opts.interactive) {
209
+ throw new Error(
210
+ `--config and --interactive are mutually exclusive. Pass --config to use a file or --interactive to build one via prompts.`
211
+ );
212
+ }
213
+ if (!opts.config && !opts.interactive) {
214
+ throw new Error(
215
+ `--config is required.
216
+
217
+ Usage: aef init --config framework.config.json --out . --copy
218
+
219
+ To get started, create a framework.config.json. Minimum required:
220
+ {
221
+ "harnesses": ["claude-code"]
222
+ }
223
+
224
+ Run 'aef init --help' for all options.`
225
+ );
226
+ }
227
+ const raw = opts.interactive ? await runWizard(root) : JSON.parse(readFileSync2(opts.config, "utf8"));
184
228
  const config = FrameworkConfigSchema.parse(raw);
185
229
  const out = opts.out ?? join4(root, "examples/consumer");
186
230
  const useCopy = Boolean(opts.copy);
@@ -204,7 +248,7 @@ async function runInit(opts) {
204
248
  }
205
249
 
206
250
  // src/cli/commands/sync.ts
207
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
251
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync4, rmSync as rmSync2 } from "fs";
208
252
  import { join as join6, relative as relative2 } from "path";
209
253
 
210
254
  // src/cli/reconcile.ts
@@ -258,15 +302,32 @@ function runSync(opts) {
258
302
  );
259
303
  const manifestPath = join6(out, ".ai", ".render-manifest.json");
260
304
  const manifest = JSON.parse(readFileSync4(manifestPath, "utf8"));
305
+ const { skills: desired } = selectSkills(root, config);
306
+ const desiredSet = new Set(desired);
307
+ const installed = Object.keys(manifest.skills);
308
+ const harnessDirs = /* @__PURE__ */ new Map();
309
+ for (const h of config.harnesses) harnessDirs.set(h, harnessSkillsDir(root, h));
310
+ for (const s of installed) {
311
+ if (desiredSet.has(s)) continue;
312
+ rmSync2(join6(out, ".ai", "skills", s), { recursive: true, force: true });
313
+ rmSync2(join6(out, ".ai", ".base", s), { recursive: true, force: true });
314
+ delete manifest.skills[s];
315
+ for (const dir of harnessDirs.values())
316
+ if (dir) rmSync2(join6(out, dir, s), { recursive: true, force: true });
317
+ }
261
318
  const report = [];
262
319
  let conflicts = 0;
263
- for (const skill of Object.keys(manifest.skills)) {
320
+ const newSkills = [];
321
+ for (const skill of desired) {
322
+ const isNew = !manifest.skills[skill];
264
323
  const r = reconcileSkill(root, out, config, skill);
265
324
  manifest.skills[skill] = { digest: r.digest, inputs: r.inputs };
266
325
  conflicts += r.conflicts;
267
326
  report.push(REPORT[r.status](skill));
327
+ if (isNew) newSkills.push(skill);
268
328
  }
269
- writeFileSync4(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
329
+ if (newSkills.length > 0) wireNewSkills(root, out, config, newSkills);
330
+ writeManifest(out, config, manifest.skills);
270
331
  console.log(`Synced ${relative2(process.cwd(), out) || "."} from framework source`);
271
332
  const fwLessonsSrc = join6(root, "core", "ai", "lessons.framework.md");
272
333
  if (existsSync4(fwLessonsSrc)) {
@@ -288,7 +349,7 @@ ${conflicts} conflict(s) written as <<<<<<< markers. Review with 'git diff', res
288
349
  }
289
350
 
290
351
  // src/cli/commands/dev.ts
291
- import { readFileSync as readFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync as rmSync2, symlinkSync as symlinkSync2 } from "fs";
352
+ import { readFileSync as readFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync2, rmSync as rmSync3, symlinkSync as symlinkSync2 } from "fs";
292
353
  import { join as join7, relative as relative3 } from "path";
293
354
  function runDev() {
294
355
  const root = FRAMEWORK_ROOT;
@@ -311,7 +372,7 @@ function runDev() {
311
372
  const target = relative3(hdir, devSkillsDir);
312
373
  for (const skill of devSkills) {
313
374
  const link = join7(hdir, skill);
314
- if (existsSync5(link) || isLink(link)) rmSync2(link, { recursive: true, force: true });
375
+ if (existsSync5(link) || isLink(link)) rmSync3(link, { recursive: true, force: true });
315
376
  symlinkSync2(join7(target, skill), link);
316
377
  }
317
378
  wired.push(`${harness} -> ${ad.skillsDir}`);
@@ -326,10 +387,17 @@ function runDev() {
326
387
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
327
388
  import { join as join8, relative as relative4 } from "path";
328
389
  function runRender(opts) {
390
+ if (!opts.config) {
391
+ throw new Error(
392
+ `--config is required.
393
+
394
+ Usage: aef render --skill <name> --config framework.config.json
395
+
396
+ Run 'aef render --help' for all options.`
397
+ );
398
+ }
329
399
  const root = FRAMEWORK_ROOT;
330
- const config = FrameworkConfigSchema.parse(
331
- JSON.parse(readFileSync6(opts.config ?? join8(root, "framework.config.example.json"), "utf8"))
332
- );
400
+ const config = FrameworkConfigSchema.parse(JSON.parse(readFileSync6(opts.config, "utf8")));
333
401
  const { rendered, manifest, digest } = renderSkill(root, config, opts.skill);
334
402
  if (opts.out) {
335
403
  const dest = join8(opts.out, opts.skill);
@@ -351,7 +419,7 @@ function runRender(opts) {
351
419
  }
352
420
 
353
421
  // src/cli/commands/adapter.ts
354
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, rmSync as rmSync3 } from "fs";
422
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, rmSync as rmSync4, cpSync as cpSync2 } from "fs";
355
423
  import { join as join9, relative as relative5 } from "path";
356
424
  var AXES = ["orm", "ui", "stack", "harness"];
357
425
  function resolveAxis(root, name) {
@@ -360,12 +428,6 @@ function resolveAxis(root, name) {
360
428
  }
361
429
  throw new Error(`no adapter '${name}' found under adapters/{${AXES.join(",")}}/`);
362
430
  }
363
- function harnessSkillsDir(root, harness) {
364
- const p2 = join9(root, "adapters", "harness", harness, "adapter.json");
365
- if (!existsSync6(p2)) return null;
366
- const ad = AdapterSchema.parse(JSON.parse(readFileSync7(p2, "utf8")));
367
- return ad.skillsDir ?? null;
368
- }
369
431
  function runAdd(name, opts) {
370
432
  mutate(name, "add", opts);
371
433
  }
@@ -378,29 +440,59 @@ function mutate(name, op, opts) {
378
440
  const useCopy = Boolean(opts.copy);
379
441
  const cfgPath = join9(out, "framework.config.json");
380
442
  const raw = JSON.parse(readFileSync7(cfgPath, "utf8"));
381
- const axis = resolveAxis(root, name);
382
- const prevHarnesses = [...FrameworkConfigSchema.parse(raw).harnesses ?? []];
383
- if (axis === "harness") {
384
- const list = new Set(prevHarnesses);
443
+ const prevConfig = FrameworkConfigSchema.parse(raw);
444
+ const prevHarnesses = [...prevConfig.harnesses ?? []];
445
+ const tierDef = loadTiers(root);
446
+ const isTier = name in tierDef.tiers;
447
+ if (isTier) {
448
+ if (tierDef.default.includes(name))
449
+ throw new Error(
450
+ `tier '${name}' is a default tier \u2014 it is always active and cannot be ${op === "add" ? "explicitly opted in" : "removed"}`
451
+ );
452
+ const current = prevConfig.tiers ?? [];
385
453
  if (op === "add") {
386
- list.add(name);
454
+ if (!current.includes(name)) raw.tiers = [...current, name];
387
455
  } else {
388
- if (!list.has(name)) throw new Error(`harness '${name}' is not installed; nothing to remove`);
389
- if (list.size === 1) throw new Error("cannot remove the last harness \u2014 at least one is required");
390
- list.delete(name);
456
+ if (!current.includes(name)) throw new Error(`tier '${name}' is not enabled; nothing to remove`);
457
+ const after = current.filter((t) => t !== name);
458
+ if (after.length > 0) raw.tiers = after;
459
+ else delete raw.tiers;
391
460
  }
392
- raw.harnesses = [...list];
393
- } else if (op === "add") {
394
- raw[axis] = name;
395
461
  } else {
396
- if (raw[axis] !== name) throw new Error(`'${name}' is not the active ${axis} adapter; nothing to remove`);
397
- raw[axis] = null;
462
+ const axis = resolveAxis(root, name);
463
+ if (axis === "harness") {
464
+ const list = new Set(prevHarnesses);
465
+ if (op === "add") {
466
+ list.add(name);
467
+ } else {
468
+ if (!list.has(name)) throw new Error(`harness '${name}' is not installed; nothing to remove`);
469
+ if (list.size === 1) throw new Error("cannot remove the last harness \u2014 at least one is required");
470
+ list.delete(name);
471
+ }
472
+ raw.harnesses = [...list];
473
+ } else if (op === "add") {
474
+ raw[axis] = name;
475
+ } else {
476
+ if (raw[axis] !== name)
477
+ throw new Error(`'${name}' is not the active ${axis} adapter; nothing to remove`);
478
+ raw[axis] = null;
479
+ }
398
480
  }
399
481
  const config = FrameworkConfigSchema.parse(raw);
400
482
  writeFileSync6(cfgPath, JSON.stringify(raw, null, 2) + "\n");
483
+ if (isTier && name === "framework") {
484
+ const outboxDst = join9(out, ".ai", "framework-feedback");
485
+ if (op === "add") {
486
+ const outboxSrc = join9(root, "core", "ai", "framework-feedback");
487
+ if (existsSync6(outboxSrc)) cpSync2(outboxSrc, outboxDst, { recursive: true });
488
+ } else {
489
+ rmSync4(outboxDst, { recursive: true, force: true });
490
+ }
491
+ }
401
492
  const conflicts = reconcile(root, out, config, prevHarnesses, useCopy);
493
+ const kind = isTier ? "tier" : "adapter";
402
494
  console.log(
403
- `${op === "add" ? "Added" : "Removed"} ${name} (${axis}) in ${relative5(process.cwd(), out) || "."}`
495
+ `${op === "add" ? "Added" : "Removed"} ${name} (${kind}) in ${relative5(process.cwd(), out) || "."}`
404
496
  );
405
497
  if (conflicts) {
406
498
  console.log(
@@ -421,11 +513,11 @@ function reconcile(root, out, config, prevHarnesses, useCopy) {
421
513
  dirByHarness.set(h, harnessSkillsDir(root, h));
422
514
  for (const s of installed) {
423
515
  if (selectedSet.has(s)) continue;
424
- rmSync3(join9(out, ".ai", "skills", s), { recursive: true, force: true });
425
- rmSync3(join9(out, ".ai", ".base", s), { recursive: true, force: true });
516
+ rmSync4(join9(out, ".ai", "skills", s), { recursive: true, force: true });
517
+ rmSync4(join9(out, ".ai", ".base", s), { recursive: true, force: true });
426
518
  delete manifest.skills[s];
427
519
  for (const dir of dirByHarness.values())
428
- if (dir) rmSync3(join9(out, dir, s), { recursive: true, force: true });
520
+ if (dir) rmSync4(join9(out, dir, s), { recursive: true, force: true });
429
521
  }
430
522
  let conflicts = 0;
431
523
  for (const s of selected) {
@@ -436,7 +528,7 @@ function reconcile(root, out, config, prevHarnesses, useCopy) {
436
528
  for (const h of prevHarnesses) {
437
529
  if ((config.harnesses ?? []).includes(h)) continue;
438
530
  const dir = dirByHarness.get(h);
439
- if (dir && existsSync6(join9(out, dir))) rmSync3(join9(out, dir), { recursive: true, force: true });
531
+ if (dir && existsSync6(join9(out, dir))) rmSync4(join9(out, dir), { recursive: true, force: true });
440
532
  }
441
533
  wireHarnesses(root, out, config, selected, useCopy);
442
534
  writeManifest(out, config, manifest.skills);
@@ -445,18 +537,55 @@ function reconcile(root, out, config, prevHarnesses, useCopy) {
445
537
 
446
538
  // src/cli/index.ts
447
539
  var { version } = JSON.parse(readFileSync8(new URL("../../package.json", import.meta.url), "utf8"));
540
+ function levenshtein(a, b) {
541
+ const dp = Array.from({ length: b.length + 1 }, (_, j) => j);
542
+ for (let i = 1; i <= a.length; i++) {
543
+ let prev = dp[0];
544
+ dp[0] = i;
545
+ for (let j = 1; j <= b.length; j++) {
546
+ const temp = dp[j];
547
+ dp[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, temp, dp[j - 1]);
548
+ prev = temp;
549
+ }
550
+ }
551
+ return dp[b.length];
552
+ }
553
+ function suggestKey(unknown, valid) {
554
+ let best = null;
555
+ let bestDist = Infinity;
556
+ for (const k of valid) {
557
+ const d = levenshtein(unknown, k);
558
+ if (d < bestDist) {
559
+ bestDist = d;
560
+ best = k;
561
+ }
562
+ }
563
+ return bestDist / Math.max(unknown.length, best.length) <= 0.4 ? best : null;
564
+ }
448
565
  var program = new Command();
449
566
  program.name("aef").description("Render slot-based engineering skills into a consumer repo and keep them in sync.").version(version);
450
- program.command("init").description("Render the configured skill set into a consumer repo and wire harnesses.").option("--config <path>", "path to framework.config.json (defaults to the bundled example)").option("--out <dir>", "consumer directory to write into").option("--copy", "copy rendered skills into harness dirs instead of symlinking").option("-i, --interactive", "build the config interactively via prompts").action((opts) => runInit(opts));
451
- program.command("render").description("Render a single skill to stdout (or --out <dir>) for inspection.").requiredOption("--skill <name>", "skill id to render").option("--config <path>", "path to framework.config.json (defaults to the bundled example)").option("--out <dir>", "write SKILL.md + provenance.json under <dir>/<skill>/ instead of stdout").action((opts) => runRender(opts));
567
+ program.command("init").description("Render the configured skill set into a consumer repo and wire harnesses.").option("--config <path>", "path to framework.config.json (required unless --interactive)").option("--out <dir>", "consumer directory to write into").option("--copy", "copy rendered skills into harness dirs instead of symlinking").option("-i, --interactive", "build the config interactively via prompts").action((opts) => runInit(opts));
568
+ program.command("render").description("Render a single skill to stdout (or --out <dir>) for inspection.").requiredOption("--skill <name>", "skill id to render").option("--config <path>", "path to framework.config.json (required)").option("--out <dir>", "write SKILL.md + provenance.json under <dir>/<skill>/ instead of stdout").action((opts) => runRender(opts));
452
569
  program.command("sync").description("Re-render from framework source and reconcile with local edits (3-way merge).").option("--out <dir>", "consumer directory to sync").action((opts) => runSync(opts));
453
570
  program.command("add").argument("<adapter>", "adapter name to select (axis inferred from its adapter.json)").description("Select an adapter and reconcile the installed skill set (merges local edits).").option("--out <dir>", "consumer directory to update").option("--copy", "copy rendered skills into harness dirs instead of symlinking").action((adapter, opts) => runAdd(adapter, opts));
454
571
  program.command("remove").argument("<adapter>", "adapter name to deselect (axis inferred from its adapter.json)").description("Deselect an adapter; uninstalls skills that required its axis.").option("--out <dir>", "consumer directory to update").option("--copy", "copy rendered skills into harness dirs instead of symlinking").action((adapter, opts) => runRemove(adapter, opts));
455
572
  program.command("dev").description("Meta-install: wire this repo\u2019s harness dirs to dev/ skills for framework development.").action(() => runDev());
573
+ var validRootKeys = Object.keys(FrameworkConfigSchema.shape);
456
574
  program.parseAsync(process.argv).catch((err) => {
457
575
  if (err instanceof ZodError) {
458
576
  console.error("Invalid configuration:");
459
- for (const issue of err.issues) console.error(` - ${issue.path.join(".") || "(root)"}: ${issue.message}`);
577
+ for (const issue of err.issues) {
578
+ if (issue.code === ZodIssueCode.unrecognized_keys && issue.path.length === 0) {
579
+ for (const key of issue.keys) {
580
+ const suggestion = suggestKey(key, validRootKeys);
581
+ const hint = suggestion != null ? ` Did you mean '${suggestion}'?` : "";
582
+ console.error(` - Unknown key '${key}'.${hint}`);
583
+ }
584
+ console.error(` Valid keys: ${validRootKeys.join(", ")}`);
585
+ } else {
586
+ console.error(` - ${issue.path.join(".") || "(root)"}: ${issue.message}`);
587
+ }
588
+ }
460
589
  } else {
461
590
  console.error(err instanceof Error ? err.message : String(err));
462
591
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/root.ts","../../src/cli/consumer-io.ts","../../src/cli/wizard.ts","../../src/cli/commands/sync.ts","../../src/cli/reconcile.ts","../../src/cli/commands/dev.ts","../../src/cli/commands/render.ts","../../src/cli/commands/adapter.ts"],"sourcesContent":["#!/usr/bin/env node\n// aef — CLI for the Agentic Engineering Framework.\n//\n// aef init [--config <cfg>] [--out <dir>] [--copy] [--interactive]\n// aef sync [--out <dir>]\n// aef add <adapter> [--out <dir>] [--copy]\n// aef remove <adapter> [--out <dir>] [--copy]\n// aef dev\nimport { readFileSync } from 'node:fs'\nimport { Command } from 'commander'\nimport { ZodError } from 'zod'\nimport { runInit } from './commands/init.js'\nimport { runSync } from './commands/sync.js'\nimport { runDev } from './commands/dev.js'\nimport { runRender } from './commands/render.js'\nimport { runAdd, runRemove } from './commands/adapter.js'\n\n// Single source of truth for the version: read the package's own package.json at\n// runtime so it never drifts from release-please's bump (dev: src/cli/, built:\n// dist/cli/ — both resolve to the package root).\nconst { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as {\n version: string\n}\n\nconst program = new Command()\n\nprogram\n .name('aef')\n .description('Render slot-based engineering skills into a consumer repo and keep them in sync.')\n .version(version)\n\nprogram\n .command('init')\n .description('Render the configured skill set into a consumer repo and wire harnesses.')\n .option('--config <path>', 'path to framework.config.json (defaults to the bundled example)')\n .option('--out <dir>', 'consumer directory to write into')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .option('-i, --interactive', 'build the config interactively via prompts')\n .action((opts) => runInit(opts))\n\nprogram\n .command('render')\n .description('Render a single skill to stdout (or --out <dir>) for inspection.')\n .requiredOption('--skill <name>', 'skill id to render')\n .option('--config <path>', 'path to framework.config.json (defaults to the bundled example)')\n .option('--out <dir>', 'write SKILL.md + provenance.json under <dir>/<skill>/ instead of stdout')\n .action((opts) => runRender(opts))\n\nprogram\n .command('sync')\n .description('Re-render from framework source and reconcile with local edits (3-way merge).')\n .option('--out <dir>', 'consumer directory to sync')\n .action((opts) => runSync(opts))\n\nprogram\n .command('add')\n .argument('<adapter>', 'adapter name to select (axis inferred from its adapter.json)')\n .description('Select an adapter and reconcile the installed skill set (merges local edits).')\n .option('--out <dir>', 'consumer directory to update')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .action((adapter, opts) => runAdd(adapter, opts))\n\nprogram\n .command('remove')\n .argument('<adapter>', 'adapter name to deselect (axis inferred from its adapter.json)')\n .description('Deselect an adapter; uninstalls skills that required its axis.')\n .option('--out <dir>', 'consumer directory to update')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .action((adapter, opts) => runRemove(adapter, opts))\n\nprogram\n .command('dev')\n .description('Meta-install: wire this repo’s harness dirs to dev/ skills for framework development.')\n .action(() => runDev())\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n if (err instanceof ZodError) {\n console.error('Invalid configuration:')\n for (const issue of err.issues) console.error(` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n } else {\n console.error(err instanceof Error ? err.message : String(err))\n }\n process.exit(1)\n})\n","// `aef init` — render the configured skill set into <out>/.ai/skills/, snapshot a\n// BASE under <out>/.ai/.base/, write the render manifest (sync's base), persist the\n// resolved config, and wire each harness.\nimport { readFileSync, writeFileSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { renderSkill } from '../../core/render.js'\nimport { selectSkills } from '../../core/select.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport {\n writeSkill,\n writeBase,\n writeManifest,\n wireHarnesses,\n writeConventions,\n type ManifestSkills,\n} from '../consumer-io.js'\nimport { runWizard } from '../wizard.js'\n\nexport interface InitOptions {\n config?: string\n out?: string\n copy?: boolean\n interactive?: boolean\n}\n\nexport async function runInit(opts: InitOptions): Promise<void> {\n const root = FRAMEWORK_ROOT\n\n // `raw` is what we persist to framework.config.json (byte-stable); `config` is the\n // zod-validated view the renderer consumes.\n const raw: unknown = opts.interactive\n ? await runWizard(root)\n : JSON.parse(readFileSync(opts.config ?? join(root, 'framework.config.example.json'), 'utf8'))\n const config = FrameworkConfigSchema.parse(raw)\n\n const out = opts.out ?? join(root, 'examples/consumer')\n const useCopy = Boolean(opts.copy)\n\n const { skills, skipped } = selectSkills(root, config)\n\n const manifestSkills: ManifestSkills = {}\n for (const skill of skills) {\n const { rendered, manifest, digest } = renderSkill(root, config, skill)\n writeSkill(out, skill, rendered, manifest)\n writeBase(out, skill, rendered)\n manifestSkills[skill] = { digest, inputs: manifest.inputs }\n }\n writeManifest(out, config, manifestSkills)\n writeFileSync(join(out, 'framework.config.json'), JSON.stringify(raw, null, 2) + '\\n')\n\n const conventions = writeConventions(root, out, config)\n const wired = wireHarnesses(root, out, config, skills, useCopy)\n\n console.log(`Initialised agentic framework into ${relative(process.cwd(), out) || '.'}`)\n console.log(` skills installed: ${skills.join(', ') || '(none)'}`)\n console.log(` conventions: ${conventions.join(', ')}`)\n if (skipped.length) console.log(` skipped (axis not configured): ${skipped.join(', ')}`)\n console.log(` harnesses wired: ${wired.join(' | ') || '(none)'}`)\n}\n","import { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\n\n// The framework SOURCE root (where core/, adapters/, schemas/ live). Resolves the\n// same whether running from src/cli/ via tsx or from dist/cli/ after a build —\n// both are two levels below the package root.\nexport const FRAMEWORK_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..')\n","// Filesystem side of init/sync: writing a consumer's .ai/ tree and wiring harnesses.\n// Kept separate from the command wiring so the behaviour is unit-testable.\nimport {\n readFileSync,\n writeFileSync,\n mkdirSync,\n existsSync,\n lstatSync,\n rmSync,\n cpSync,\n symlinkSync,\n} from 'node:fs'\nimport { join } from 'node:path'\nimport { AdapterSchema, type FrameworkConfig } from '../core/contracts.js'\nimport type { InputRef, RenderManifest } from '../core/render.js'\n\nexport const isLink = (p: string): boolean => {\n try {\n return lstatSync(p).isSymbolicLink()\n } catch {\n return false\n }\n}\n\nexport function pick<T extends Record<string, unknown>>(o: T, keys: string[]): Record<string, unknown> {\n const r: Record<string, unknown> = {}\n for (const k of keys) if (k in o) r[k] = o[k]\n return r\n}\n\n/**\n * Install the generic `core/ai` conventions into a consumer: the specs/qa/runs scaffolding,\n * a starter lessons.md, a CLAUDE.md, and AGENTS.md rendered from the template with\n * {{PROJECT_NAME}} substituted. Idempotent — safe to re-run. Returns the paths written.\n */\nexport function writeConventions(root: string, out: string, config: FrameworkConfig): string[] {\n const projectName = config.projectName ?? 'your project'\n const sub = (s: string): string => s.split('{{PROJECT_NAME}}').join(projectName)\n const ai = join(out, '.ai')\n mkdirSync(ai, { recursive: true })\n const written: string[] = []\n\n for (const dir of ['specs', 'qa', 'runs']) {\n const src = join(root, 'core', 'ai', dir)\n if (existsSync(src)) {\n cpSync(src, join(ai, dir), { recursive: true })\n written.push(`.ai/${dir}/`)\n }\n }\n // Substitute {{PROJECT_NAME}} in the one convention file that carries it.\n const specsReadme = join(ai, 'specs', 'README.md')\n if (existsSync(specsReadme)) writeFileSync(specsReadme, sub(readFileSync(specsReadme, 'utf8')))\n\n const lessonsSrc = join(root, 'core', 'ai', 'lessons.md')\n if (existsSync(lessonsSrc)) {\n writeFileSync(join(ai, 'lessons.md'), sub(readFileSync(lessonsSrc, 'utf8')))\n written.push('.ai/lessons.md')\n }\n // Synced-read-only generic lessons + the local-only framework-feedback outbox (decision #8).\n const fwLessons = join(root, 'core', 'ai', 'lessons.framework.md')\n if (existsSync(fwLessons)) {\n writeFileSync(join(ai, 'lessons.framework.md'), readFileSync(fwLessons, 'utf8'))\n written.push('.ai/lessons.framework.md')\n }\n const outbox = join(root, 'core', 'ai', 'framework-feedback')\n if (existsSync(outbox)) {\n cpSync(outbox, join(ai, 'framework-feedback'), { recursive: true })\n written.push('.ai/framework-feedback/')\n }\n\n const tpl = join(root, 'core', 'AGENTS.md.template')\n if (existsSync(tpl)) {\n writeFileSync(join(out, 'AGENTS.md'), sub(readFileSync(tpl, 'utf8')))\n written.push('AGENTS.md')\n }\n writeFileSync(join(out, 'CLAUDE.md'), '@AGENTS.md\\n')\n written.push('CLAUDE.md')\n return written\n}\n\nexport function writeSkill(out: string, skill: string, rendered: string, manifest: RenderManifest): void {\n const dest = join(out, '.ai', 'skills', skill)\n mkdirSync(dest, { recursive: true })\n writeFileSync(join(dest, 'SKILL.md'), rendered)\n writeFileSync(join(dest, 'provenance.json'), JSON.stringify(manifest, null, 2) + '\\n')\n}\n\nexport function writeBase(out: string, skill: string, rendered: string): void {\n const d = join(out, '.ai', '.base', skill)\n mkdirSync(d, { recursive: true })\n writeFileSync(join(d, 'SKILL.md'), rendered)\n}\n\nexport type ManifestSkills = Record<string, { digest: string; inputs: InputRef[] }>\n\nexport function writeManifest(out: string, config: FrameworkConfig, skills: ManifestSkills): void {\n mkdirSync(join(out, '.ai'), { recursive: true })\n writeFileSync(\n join(out, '.ai', '.render-manifest.json'),\n JSON.stringify({ selection: pick(config, ['orm', 'ui', 'stack', 'harnesses']), skills }, null, 2) + '\\n',\n )\n}\n\nexport function wireHarnesses(\n root: string,\n out: string,\n config: FrameworkConfig,\n skills: string[],\n useCopy: boolean,\n): string[] {\n const aiSkills = join(out, '.ai', 'skills')\n const wired: string[] = []\n for (const harness of config.harnesses) {\n const adPath = join(root, 'adapters', 'harness', harness, 'adapter.json')\n if (!existsSync(adPath)) {\n console.warn(`! no harness adapter '${harness}', skipping`)\n continue\n }\n const ad = AdapterSchema.parse(JSON.parse(readFileSync(adPath, 'utf8')))\n if (!ad.skillsDir || !ad.linkBase) {\n console.warn(`! harness adapter '${harness}' is missing skillsDir/linkBase, skipping`)\n continue\n }\n const hdir = join(out, ad.skillsDir)\n mkdirSync(hdir, { recursive: true })\n for (const skill of skills) {\n const link = join(hdir, skill)\n if (existsSync(link) || isLink(link)) rmSync(link, { recursive: true, force: true })\n if (useCopy) cpSync(join(aiSkills, skill), link, { recursive: true })\n else symlinkSync(`${ad.linkBase}/${skill}`, link)\n }\n wired.push(`${harness} -> ${ad.skillsDir} (${useCopy ? 'copy' : 'symlink'})`)\n }\n return wired\n}\n","// Interactive `init` wizard (opt-in via --interactive). Discovers the available\n// adapters on disk and builds a framework.config object via @clack/prompts.\nimport { readdirSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport * as p from '@clack/prompts'\n\nfunction listAdapters(root: string, axis: string): string[] {\n const dir = join(root, 'adapters', axis)\n if (!existsSync(dir)) return []\n return readdirSync(dir).filter((n) => existsSync(join(dir, n, 'adapter.json')))\n}\n\nfunction bail<T>(value: T | symbol): T {\n if (p.isCancel(value)) {\n p.cancel('Cancelled — nothing written.')\n process.exit(1)\n }\n return value as T\n}\n\nconst axisOptions = (root: string, axis: string): { value: string | null; label: string }[] => [\n { value: null, label: 'none' },\n ...listAdapters(root, axis).map((v) => ({ value: v, label: v })),\n]\n\nexport async function runWizard(root: string): Promise<Record<string, unknown>> {\n p.intro('aef init')\n\n const projectName = bail(\n await p.text({ message: 'Project name', placeholder: 'my-app', defaultValue: 'my-app' }),\n )\n const harnesses = bail(\n await p.multiselect({\n message: 'Which AI harnesses should be wired?',\n options: listAdapters(root, 'harness').map((v) => ({ value: v, label: v })),\n required: true,\n }),\n )\n const orm = bail(\n await p.select({ message: 'ORM adapter', options: axisOptions(root, 'orm'), initialValue: null }),\n )\n const ui = bail(\n await p.select({ message: 'UI adapter', options: axisOptions(root, 'ui'), initialValue: null }),\n )\n\n p.outro('Configuration ready.')\n\n return {\n $schema: './schemas/framework.config.schema.json',\n projectName,\n harnesses,\n orm,\n ui,\n }\n}\n","// `aef sync` — re-render from the (updated) framework source and reconcile with\n// local edits via a git-native 3-way merge (BASE = last render, LOCAL = on-disk,\n// NEW = fresh render). Exit code 2 signals unresolved conflicts written to the tree.\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { reconcileSkill, type ReconcileStatus } from '../reconcile.js'\nimport type { ManifestSkills } from '../consumer-io.js'\n\ninterface RenderManifestFile {\n selection: unknown\n skills: ManifestSkills\n}\n\nconst REPORT: Record<ReconcileStatus, (s: string) => string> = {\n installed: (s) => ` + ${s}: installed`,\n unchanged: (s) => ` = ${s}: framework unchanged`,\n forwarded: (s) => ` ↑ ${s}: updated (no local edits)`,\n merged: (s) => ` ⇄ ${s}: merged local edits + framework update (clean)`,\n conflict: (s) => ` ⇄ ${s}: merged — CONFLICT(S), resolve in working tree`,\n}\n\nexport interface SyncOptions {\n out?: string\n}\n\nexport function runSync(opts: SyncOptions): void {\n const root = FRAMEWORK_ROOT\n const out = opts.out ?? join(root, 'examples/consumer')\n const config = FrameworkConfigSchema.parse(\n JSON.parse(readFileSync(join(out, 'framework.config.json'), 'utf8')),\n )\n const manifestPath = join(out, '.ai', '.render-manifest.json')\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as RenderManifestFile\n\n const report: string[] = []\n let conflicts = 0\n for (const skill of Object.keys(manifest.skills)) {\n const r = reconcileSkill(root, out, config, skill)\n manifest.skills[skill] = { digest: r.digest, inputs: r.inputs }\n conflicts += r.conflicts\n report.push(REPORT[r.status](skill))\n }\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\\n')\n\n console.log(`Synced ${relative(process.cwd(), out) || '.'} from framework source`)\n // Refresh synced read-only framework lessons (loop closure, decision #8).\n const fwLessonsSrc = join(root, 'core', 'ai', 'lessons.framework.md')\n if (existsSync(fwLessonsSrc)) {\n const dest = join(out, '.ai', 'lessons.framework.md')\n const next = readFileSync(fwLessonsSrc, 'utf8')\n if (!existsSync(dest) || readFileSync(dest, 'utf8') !== next) {\n writeFileSync(dest, next)\n report.push(' ↑ lessons.framework.md: refreshed (synced, read-only)')\n }\n }\n\n report.forEach((l) => console.log(l))\n if (conflicts) {\n console.log(\n `\\n${conflicts} conflict(s) written as <<<<<<< markers. Review with 'git diff', resolve, commit.`,\n )\n process.exitCode = 2\n }\n}\n","// Shared per-skill reconcile: render a skill from the current framework source and\n// fold the result into a consumer, preserving local edits via a git-native 3-way merge.\n// Used by both `aef sync` and `aef add`/`remove` so the reconcile semantics —\n// fast-forward, clean merge, conflict, and provenance refresh — live in exactly one place.\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { renderSkill, type InputRef } from '../core/render.js'\nimport { mergeFile } from '../core/merge.js'\nimport type { FrameworkConfig } from '../core/contracts.js'\nimport { writeSkill, writeBase } from './consumer-io.js'\n\nexport type ReconcileStatus = 'installed' | 'unchanged' | 'forwarded' | 'merged' | 'conflict'\n\nexport interface ReconcileResult {\n status: ReconcileStatus\n digest: string\n inputs: InputRef[]\n conflicts: number\n}\n\n/**\n * Reconcile one skill in `out` against the current render:\n * - not yet on disk → install fresh (+ BASE),\n * - framework unchanged → leave LOCAL alone (local edits kept),\n * - framework changed, no edits → fast-forward to NEW,\n * - both changed → 3-way merge (conflicts land as <<<<<<< markers).\n * Always refreshes provenance.json to the fresh render and returns the new digest/inputs\n * so the caller can update the render manifest.\n */\nexport function reconcileSkill(\n root: string,\n out: string,\n config: FrameworkConfig,\n skill: string,\n): ReconcileResult {\n const { rendered: NEW, manifest, digest } = renderSkill(root, config, skill)\n const skillMd = join(out, '.ai', 'skills', skill, 'SKILL.md')\n\n if (!existsSync(skillMd)) {\n writeSkill(out, skill, NEW, manifest)\n writeBase(out, skill, NEW)\n return { status: 'installed', digest, inputs: manifest.inputs, conflicts: 0 }\n }\n\n const baseMd = join(out, '.ai', '.base', skill, 'SKILL.md')\n const BASE = existsSync(baseMd) ? readFileSync(baseMd, 'utf8') : NEW\n const LOCAL = readFileSync(skillMd, 'utf8')\n\n let status: ReconcileStatus = 'unchanged'\n let conflicts = 0\n if (NEW !== BASE) {\n if (LOCAL === BASE) {\n writeFileSync(skillMd, NEW)\n status = 'forwarded'\n } else {\n const { merged, conflicts: n } = mergeFile(LOCAL, BASE, NEW)\n writeFileSync(skillMd, merged)\n conflicts = n\n status = n ? 'conflict' : 'merged'\n }\n writeBase(out, skill, NEW)\n }\n // Provenance always tracks the fresh render (improve-framework reads it to route edits).\n writeFileSync(\n join(out, '.ai', 'skills', skill, 'provenance.json'),\n JSON.stringify(manifest, null, 2) + '\\n',\n )\n return { status, digest, inputs: manifest.inputs, conflicts }\n}\n","// `aef dev` — the \"meta\" install for developing THIS framework. Wires the repo's\n// own harness skill dirs to dev/ skills (source symlinks). Installs the toolchain, not\n// the shipped product; harness dirs are gitignored.\nimport { readFileSync, readdirSync, existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { AdapterSchema } from '../../core/contracts.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { isLink } from '../consumer-io.js'\n\nexport function runDev(): void {\n const root = FRAMEWORK_ROOT\n const devSkillsDir = join(root, 'dev', 'skills')\n if (!existsSync(devSkillsDir)) {\n console.error('no dev/skills/ found')\n process.exit(1)\n }\n const devSkills = readdirSync(devSkillsDir).filter((d) => existsSync(join(devSkillsDir, d, 'SKILL.md')))\n const harnessRoot = join(root, 'adapters', 'harness')\n const harnesses = readdirSync(harnessRoot).filter((d) => existsSync(join(harnessRoot, d, 'adapter.json')))\n\n const wired: string[] = []\n for (const harness of harnesses) {\n const ad = AdapterSchema.parse(\n JSON.parse(readFileSync(join(harnessRoot, harness, 'adapter.json'), 'utf8')),\n )\n if (!ad.skillsDir) continue\n const hdir = join(root, ad.skillsDir)\n mkdirSync(hdir, { recursive: true })\n const target = relative(hdir, devSkillsDir) // robust relative link target\n for (const skill of devSkills) {\n const link = join(hdir, skill)\n if (existsSync(link) || isLink(link)) rmSync(link, { recursive: true, force: true })\n symlinkSync(join(target, skill), link)\n }\n wired.push(`${harness} -> ${ad.skillsDir}`)\n }\n console.log('Framework dev install (meta):')\n console.log(` dev skills: ${devSkills.join(', ') || '(none)'}`)\n console.log(` harnesses : ${wired.join(' | ')}`)\n console.log(' (harness dirs are gitignored; re-run after adding a dev skill)')\n}\n","// `aef render` — render a single skill to stdout (or --out dir) for inspection.\n// The lightweight counterpart to `init`; handy while authoring skills/adapters and\n// the render step the improve-framework skill points at.\nimport { readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { renderSkill } from '../../core/render.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\n\nexport interface RenderOptions {\n skill: string\n config?: string\n out?: string\n}\n\nexport function runRender(opts: RenderOptions): void {\n const root = FRAMEWORK_ROOT\n const config = FrameworkConfigSchema.parse(\n JSON.parse(readFileSync(opts.config ?? join(root, 'framework.config.example.json'), 'utf8')),\n )\n const { rendered, manifest, digest } = renderSkill(root, config, opts.skill)\n\n if (opts.out) {\n const dest = join(opts.out, opts.skill)\n mkdirSync(dest, { recursive: true })\n writeFileSync(join(dest, 'SKILL.md'), rendered)\n writeFileSync(join(dest, 'provenance.json'), JSON.stringify(manifest, null, 2) + '\\n')\n console.error(\n `Rendered '${opts.skill}' (orm=${manifest.selection.orm ?? 'none'}) -> ${relative(root, dest)}`,\n )\n console.error(\n ` digest ${digest} · ${manifest.regions.length} regions · ${manifest.inputs.length} inputs`,\n )\n } else {\n // Body to stdout (pipeable); summary to stderr.\n process.stdout.write(rendered)\n console.error(\n ` digest ${digest} · ${manifest.regions.length} regions · ${manifest.inputs.length} inputs`,\n )\n }\n}\n","// `aef add <adapter>` / `aef remove <adapter>` — change an axis selection in a\n// consumer's framework.config.json and reconcile the installed skill set:\n// • newly-selected skills are rendered fresh (+ BASE snapshot),\n// • skills no longer selected are uninstalled (skill dir, BASE, harness links),\n// • surviving skills are 3-way merged (like sync) so adapter-content changes flow in\n// without clobbering local edits.\n// The adapter's axis is read from its adapter.json, so the command is `add <name>`.\nimport { readFileSync, writeFileSync, existsSync, rmSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema, AdapterSchema, type FrameworkConfig } from '../../core/contracts.js'\nimport { selectSkills } from '../../core/select.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { writeManifest, wireHarnesses, type ManifestSkills } from '../consumer-io.js'\nimport { reconcileSkill } from '../reconcile.js'\n\nconst AXES = ['orm', 'ui', 'stack', 'harness'] as const\n\ninterface ManifestFile {\n selection: unknown\n skills: ManifestSkills\n}\n\nexport interface AdapterCmdOptions {\n out?: string\n copy?: boolean\n}\n\n/** Find which axis an adapter name belongs to by locating its adapter.json on disk. */\nfunction resolveAxis(root: string, name: string): (typeof AXES)[number] {\n for (const axis of AXES) {\n if (existsSync(join(root, 'adapters', axis, name, 'adapter.json'))) return axis\n }\n throw new Error(`no adapter '${name}' found under adapters/{${AXES.join(',')}}/`)\n}\n\nfunction harnessSkillsDir(root: string, harness: string): string | null {\n const p = join(root, 'adapters', 'harness', harness, 'adapter.json')\n if (!existsSync(p)) return null\n const ad = AdapterSchema.parse(JSON.parse(readFileSync(p, 'utf8')))\n return ad.skillsDir ?? null\n}\n\nexport function runAdd(name: string, opts: AdapterCmdOptions): void {\n mutate(name, 'add', opts)\n}\n\nexport function runRemove(name: string, opts: AdapterCmdOptions): void {\n mutate(name, 'remove', opts)\n}\n\nfunction mutate(name: string, op: 'add' | 'remove', opts: AdapterCmdOptions): void {\n const root = FRAMEWORK_ROOT\n const out = opts.out ?? join(root, 'examples/consumer')\n const useCopy = Boolean(opts.copy)\n const cfgPath = join(out, 'framework.config.json')\n\n const raw = JSON.parse(readFileSync(cfgPath, 'utf8')) as Record<string, unknown>\n const axis = resolveAxis(root, name)\n const prevHarnesses = [...(FrameworkConfigSchema.parse(raw).harnesses ?? [])]\n\n if (axis === 'harness') {\n const list = new Set(prevHarnesses)\n if (op === 'add') {\n list.add(name)\n } else {\n if (!list.has(name)) throw new Error(`harness '${name}' is not installed; nothing to remove`)\n if (list.size === 1) throw new Error('cannot remove the last harness — at least one is required')\n list.delete(name)\n }\n raw.harnesses = [...list]\n } else if (op === 'add') {\n raw[axis] = name\n } else {\n // remove: only clear the axis if this adapter is the one selected\n if (raw[axis] !== name) throw new Error(`'${name}' is not the active ${axis} adapter; nothing to remove`)\n raw[axis] = null\n }\n\n const config = FrameworkConfigSchema.parse(raw)\n writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + '\\n')\n\n const conflicts = reconcile(root, out, config, prevHarnesses, useCopy)\n console.log(\n `${op === 'add' ? 'Added' : 'Removed'} ${name} (${axis}) in ${relative(process.cwd(), out) || '.'}`,\n )\n if (conflicts) {\n console.log(\n `\\n${conflicts} conflict(s) written as <<<<<<< markers — review with 'git diff', resolve, commit.`,\n )\n process.exitCode = 2\n }\n}\n\nfunction reconcile(\n root: string,\n out: string,\n config: FrameworkConfig,\n prevHarnesses: string[],\n useCopy: boolean,\n): number {\n const manifestPath = join(out, '.ai', '.render-manifest.json')\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as ManifestFile\n\n const { skills: selected } = selectSkills(root, config)\n const selectedSet = new Set(selected)\n const installed = Object.keys(manifest.skills)\n\n // Resolve each harness's skills dir once (used when removing skills and harnesses).\n const dirByHarness = new Map<string, string | null>()\n for (const h of new Set([...prevHarnesses, ...(config.harnesses ?? [])]))\n dirByHarness.set(h, harnessSkillsDir(root, h))\n\n // 1. Uninstall skills no longer selected.\n for (const s of installed) {\n if (selectedSet.has(s)) continue\n rmSync(join(out, '.ai', 'skills', s), { recursive: true, force: true })\n rmSync(join(out, '.ai', '.base', s), { recursive: true, force: true })\n delete manifest.skills[s]\n for (const dir of dirByHarness.values())\n if (dir) rmSync(join(out, dir, s), { recursive: true, force: true })\n }\n\n // 2. Install new + 3-way-merge surviving skills (shared with `sync`).\n let conflicts = 0\n for (const s of selected) {\n const r = reconcileSkill(root, out, config, s)\n manifest.skills[s] = { digest: r.digest, inputs: r.inputs }\n conflicts += r.conflicts\n }\n\n // 3. Drop skills dirs for harnesses removed entirely, then re-wire the surviving set.\n for (const h of prevHarnesses) {\n if ((config.harnesses ?? []).includes(h)) continue\n const dir = dirByHarness.get(h)\n if (dir && existsSync(join(out, dir))) rmSync(join(out, dir), { recursive: true, force: true })\n }\n wireHarnesses(root, out, config, selected, useCopy)\n\n writeManifest(out, config, manifest.skills)\n return conflicts\n}\n"],"mappings":"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","names":["readFileSync","readFileSync","writeFileSync","join","join","p","join","existsSync","join","readFileSync","join","writeFileSync","readFileSync","writeFileSync","existsSync","join","relative","readFileSync","writeFileSync","existsSync","join","join","existsSync","readFileSync","writeFileSync","join","readFileSync","writeFileSync","relative","existsSync","readFileSync","readdirSync","existsSync","mkdirSync","rmSync","symlinkSync","join","relative","join","existsSync","readdirSync","readFileSync","mkdirSync","relative","rmSync","symlinkSync","readFileSync","writeFileSync","mkdirSync","join","relative","readFileSync","join","mkdirSync","writeFileSync","relative","readFileSync","writeFileSync","existsSync","rmSync","join","relative","existsSync","join","p","readFileSync","writeFileSync","relative","rmSync","readFileSync"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/root.ts","../../src/cli/consumer-io.ts","../../src/cli/wizard.ts","../../src/cli/commands/sync.ts","../../src/cli/reconcile.ts","../../src/cli/commands/dev.ts","../../src/cli/commands/render.ts","../../src/cli/commands/adapter.ts"],"sourcesContent":["#!/usr/bin/env node\n// aef — CLI for the Agentic Engineering Framework.\n//\n// aef init [--config <cfg>] [--out <dir>] [--copy] [--interactive]\n// aef sync [--out <dir>]\n// aef add <adapter> [--out <dir>] [--copy]\n// aef remove <adapter> [--out <dir>] [--copy]\n// aef dev\nimport { readFileSync } from 'node:fs'\nimport { Command } from 'commander'\nimport { ZodError, ZodIssueCode } from 'zod'\nimport { runInit } from './commands/init.js'\nimport { runSync } from './commands/sync.js'\nimport { runDev } from './commands/dev.js'\nimport { runRender } from './commands/render.js'\nimport { runAdd, runRemove } from './commands/adapter.js'\nimport { FrameworkConfigSchema } from '../core/contracts.js'\n\n// Single source of truth for the version: read the package's own package.json at\n// runtime so it never drifts from release-please's bump (dev: src/cli/, built:\n// dist/cli/ — both resolve to the package root).\nconst { version } = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) as {\n version: string\n}\n\nfunction levenshtein(a: string, b: string): number {\n const dp = Array.from({ length: b.length + 1 }, (_, j) => j)\n for (let i = 1; i <= a.length; i++) {\n let prev = dp[0]!\n dp[0] = i\n for (let j = 1; j <= b.length; j++) {\n const temp = dp[j]!\n dp[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, temp, dp[j - 1]!)\n prev = temp\n }\n }\n return dp[b.length]!\n}\n\nfunction suggestKey(unknown: string, valid: string[]): string | null {\n let best: string | null = null\n let bestDist = Infinity\n for (const k of valid) {\n const d = levenshtein(unknown, k)\n if (d < bestDist) {\n bestDist = d\n best = k\n }\n }\n return bestDist / Math.max(unknown.length, best!.length) <= 0.4 ? best : null\n}\n\nconst program = new Command()\n\nprogram\n .name('aef')\n .description('Render slot-based engineering skills into a consumer repo and keep them in sync.')\n .version(version)\n\nprogram\n .command('init')\n .description('Render the configured skill set into a consumer repo and wire harnesses.')\n .option('--config <path>', 'path to framework.config.json (required unless --interactive)')\n .option('--out <dir>', 'consumer directory to write into')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .option('-i, --interactive', 'build the config interactively via prompts')\n .action((opts) => runInit(opts))\n\nprogram\n .command('render')\n .description('Render a single skill to stdout (or --out <dir>) for inspection.')\n .requiredOption('--skill <name>', 'skill id to render')\n .option('--config <path>', 'path to framework.config.json (required)')\n .option('--out <dir>', 'write SKILL.md + provenance.json under <dir>/<skill>/ instead of stdout')\n .action((opts) => runRender(opts))\n\nprogram\n .command('sync')\n .description('Re-render from framework source and reconcile with local edits (3-way merge).')\n .option('--out <dir>', 'consumer directory to sync')\n .action((opts) => runSync(opts))\n\nprogram\n .command('add')\n .argument('<adapter>', 'adapter name to select (axis inferred from its adapter.json)')\n .description('Select an adapter and reconcile the installed skill set (merges local edits).')\n .option('--out <dir>', 'consumer directory to update')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .action((adapter, opts) => runAdd(adapter, opts))\n\nprogram\n .command('remove')\n .argument('<adapter>', 'adapter name to deselect (axis inferred from its adapter.json)')\n .description('Deselect an adapter; uninstalls skills that required its axis.')\n .option('--out <dir>', 'consumer directory to update')\n .option('--copy', 'copy rendered skills into harness dirs instead of symlinking')\n .action((adapter, opts) => runRemove(adapter, opts))\n\nprogram\n .command('dev')\n .description('Meta-install: wire this repo’s harness dirs to dev/ skills for framework development.')\n .action(() => runDev())\n\nconst validRootKeys = Object.keys(FrameworkConfigSchema.shape)\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n if (err instanceof ZodError) {\n console.error('Invalid configuration:')\n for (const issue of err.issues) {\n if (issue.code === ZodIssueCode.unrecognized_keys && issue.path.length === 0) {\n for (const key of issue.keys) {\n const suggestion = suggestKey(key, validRootKeys)\n const hint = suggestion != null ? ` Did you mean '${suggestion}'?` : ''\n console.error(` - Unknown key '${key}'.${hint}`)\n }\n console.error(` Valid keys: ${validRootKeys.join(', ')}`)\n } else {\n console.error(` - ${issue.path.join('.') || '(root)'}: ${issue.message}`)\n }\n }\n } else {\n console.error(err instanceof Error ? err.message : String(err))\n }\n process.exit(1)\n})\n","// `aef init` — render the configured skill set into <out>/.ai/skills/, snapshot a\n// BASE under <out>/.ai/.base/, write the render manifest (sync's base), persist the\n// resolved config, and wire each harness.\nimport { readFileSync, writeFileSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { renderSkill } from '../../core/render.js'\nimport { selectSkills } from '../../core/select.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport {\n writeSkill,\n writeBase,\n writeManifest,\n wireHarnesses,\n writeConventions,\n type ManifestSkills,\n} from '../consumer-io.js'\nimport { runWizard } from '../wizard.js'\n\nexport interface InitOptions {\n config?: string\n out?: string\n copy?: boolean\n interactive?: boolean\n}\n\nexport async function runInit(opts: InitOptions): Promise<void> {\n const root = FRAMEWORK_ROOT\n\n if (opts.config && opts.interactive) {\n throw new Error(\n `--config and --interactive are mutually exclusive. Pass --config to use a file or --interactive to build one via prompts.`,\n )\n }\n if (!opts.config && !opts.interactive) {\n throw new Error(\n `--config is required.\\n\\nUsage: aef init --config framework.config.json --out . --copy\\n\\nTo get started, create a framework.config.json. Minimum required:\\n {\\n \"harnesses\": [\"claude-code\"]\\n }\\n\\nRun 'aef init --help' for all options.`,\n )\n }\n\n // `raw` is what we persist to framework.config.json (byte-stable); `config` is the\n // zod-validated view the renderer consumes.\n const raw: unknown = opts.interactive\n ? await runWizard(root)\n : JSON.parse(readFileSync(opts.config!, 'utf8'))\n const config = FrameworkConfigSchema.parse(raw)\n\n const out = opts.out ?? join(root, 'examples/consumer')\n const useCopy = Boolean(opts.copy)\n\n const { skills, skipped } = selectSkills(root, config)\n\n const manifestSkills: ManifestSkills = {}\n for (const skill of skills) {\n const { rendered, manifest, digest } = renderSkill(root, config, skill)\n writeSkill(out, skill, rendered, manifest)\n writeBase(out, skill, rendered)\n manifestSkills[skill] = { digest, inputs: manifest.inputs }\n }\n writeManifest(out, config, manifestSkills)\n writeFileSync(join(out, 'framework.config.json'), JSON.stringify(raw, null, 2) + '\\n')\n\n const conventions = writeConventions(root, out, config)\n const wired = wireHarnesses(root, out, config, skills, useCopy)\n\n console.log(`Initialised agentic framework into ${relative(process.cwd(), out) || '.'}`)\n console.log(` skills installed: ${skills.join(', ') || '(none)'}`)\n console.log(` conventions: ${conventions.join(', ')}`)\n if (skipped.length) console.log(` skipped (axis not configured): ${skipped.join(', ')}`)\n console.log(` harnesses wired: ${wired.join(' | ') || '(none)'}`)\n}\n","import { fileURLToPath } from 'node:url'\nimport { dirname, join } from 'node:path'\n\n// The framework SOURCE root (where core/, adapters/, schemas/ live). Resolves the\n// same whether running from src/cli/ via tsx or from dist/cli/ after a build —\n// both are two levels below the package root.\nexport const FRAMEWORK_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..')\n","// Filesystem side of init/sync: writing a consumer's .ai/ tree and wiring harnesses.\n// Kept separate from the command wiring so the behaviour is unit-testable.\nimport {\n readFileSync,\n writeFileSync,\n mkdirSync,\n existsSync,\n lstatSync,\n rmSync,\n cpSync,\n symlinkSync,\n} from 'node:fs'\nimport { join } from 'node:path'\nimport { AdapterSchema, type FrameworkConfig } from '../core/contracts.js'\nimport type { InputRef, RenderManifest } from '../core/render.js'\n\nexport const isLink = (p: string): boolean => {\n try {\n return lstatSync(p).isSymbolicLink()\n } catch {\n return false\n }\n}\n\nexport function pick<T extends Record<string, unknown>>(o: T, keys: string[]): Record<string, unknown> {\n const r: Record<string, unknown> = {}\n for (const k of keys) if (k in o) r[k] = o[k]\n return r\n}\n\n/**\n * Install the generic `core/ai` conventions into a consumer: the specs/qa/runs scaffolding,\n * a starter lessons.md, a CLAUDE.md, and AGENTS.md rendered from the template with\n * {{PROJECT_NAME}} substituted. Idempotent — safe to re-run. Returns the paths written.\n */\nexport function writeConventions(root: string, out: string, config: FrameworkConfig): string[] {\n const projectName = config.projectName ?? 'your project'\n const sub = (s: string): string => s.split('{{PROJECT_NAME}}').join(projectName)\n const ai = join(out, '.ai')\n mkdirSync(ai, { recursive: true })\n const written: string[] = []\n\n for (const dir of ['specs', 'qa', 'runs']) {\n const src = join(root, 'core', 'ai', dir)\n if (existsSync(src)) {\n cpSync(src, join(ai, dir), { recursive: true })\n written.push(`.ai/${dir}/`)\n }\n }\n // Substitute {{PROJECT_NAME}} in the one convention file that carries it.\n const specsReadme = join(ai, 'specs', 'README.md')\n if (existsSync(specsReadme)) writeFileSync(specsReadme, sub(readFileSync(specsReadme, 'utf8')))\n\n const lessonsSrc = join(root, 'core', 'ai', 'lessons.md')\n if (existsSync(lessonsSrc)) {\n writeFileSync(join(ai, 'lessons.md'), sub(readFileSync(lessonsSrc, 'utf8')))\n written.push('.ai/lessons.md')\n }\n // Synced-read-only generic lessons.\n const fwLessons = join(root, 'core', 'ai', 'lessons.framework.md')\n if (existsSync(fwLessons)) {\n writeFileSync(join(ai, 'lessons.framework.md'), readFileSync(fwLessons, 'utf8'))\n written.push('.ai/lessons.framework.md')\n }\n // Framework-feedback outbox: only for consumers who have opted in to the framework tier.\n const outbox = join(root, 'core', 'ai', 'framework-feedback')\n if (existsSync(outbox) && config.tiers?.includes('framework')) {\n cpSync(outbox, join(ai, 'framework-feedback'), { recursive: true })\n written.push('.ai/framework-feedback/')\n }\n\n const tpl = join(root, 'core', 'AGENTS.md.template')\n if (existsSync(tpl)) {\n writeFileSync(join(out, 'AGENTS.md'), sub(readFileSync(tpl, 'utf8')))\n written.push('AGENTS.md')\n }\n writeFileSync(join(out, 'CLAUDE.md'), '@AGENTS.md\\n')\n written.push('CLAUDE.md')\n return written\n}\n\nexport function writeSkill(out: string, skill: string, rendered: string, manifest: RenderManifest): void {\n const dest = join(out, '.ai', 'skills', skill)\n mkdirSync(dest, { recursive: true })\n writeFileSync(join(dest, 'SKILL.md'), rendered)\n writeFileSync(join(dest, 'provenance.json'), JSON.stringify(manifest, null, 2) + '\\n')\n}\n\nexport function writeBase(out: string, skill: string, rendered: string): void {\n const d = join(out, '.ai', '.base', skill)\n mkdirSync(d, { recursive: true })\n writeFileSync(join(d, 'SKILL.md'), rendered)\n}\n\nexport type ManifestSkills = Record<string, { digest: string; inputs: InputRef[] }>\n\nexport function writeManifest(out: string, config: FrameworkConfig, skills: ManifestSkills): void {\n mkdirSync(join(out, '.ai'), { recursive: true })\n writeFileSync(\n join(out, '.ai', '.render-manifest.json'),\n JSON.stringify(\n { selection: pick(config, ['orm', 'ui', 'stack', 'harnesses', 'tiers']), skills },\n null,\n 2,\n ) + '\\n',\n )\n}\n\nexport function harnessSkillsDir(root: string, harness: string): string | null {\n const p = join(root, 'adapters', 'harness', harness, 'adapter.json')\n if (!existsSync(p)) return null\n const ad = AdapterSchema.parse(JSON.parse(readFileSync(p, 'utf8')))\n return ad.skillsDir ?? null\n}\n\nexport function wireNewSkills(root: string, out: string, config: FrameworkConfig, skills: string[]): void {\n for (const harness of config.harnesses) {\n const adPath = join(root, 'adapters', 'harness', harness, 'adapter.json')\n if (!existsSync(adPath)) continue\n const ad = AdapterSchema.parse(JSON.parse(readFileSync(adPath, 'utf8')))\n if (!ad.skillsDir || !ad.linkBase) continue\n const hdir = join(out, ad.skillsDir)\n mkdirSync(hdir, { recursive: true })\n for (const skill of skills) {\n const link = join(hdir, skill)\n if (!existsSync(link) && !isLink(link)) symlinkSync(`${ad.linkBase}/${skill}`, link)\n }\n }\n}\n\nexport function wireHarnesses(\n root: string,\n out: string,\n config: FrameworkConfig,\n skills: string[],\n useCopy: boolean,\n): string[] {\n const aiSkills = join(out, '.ai', 'skills')\n const wired: string[] = []\n for (const harness of config.harnesses) {\n const adPath = join(root, 'adapters', 'harness', harness, 'adapter.json')\n if (!existsSync(adPath)) {\n console.warn(`! no harness adapter '${harness}', skipping`)\n continue\n }\n const ad = AdapterSchema.parse(JSON.parse(readFileSync(adPath, 'utf8')))\n if (!ad.skillsDir || !ad.linkBase) {\n console.warn(`! harness adapter '${harness}' is missing skillsDir/linkBase, skipping`)\n continue\n }\n const hdir = join(out, ad.skillsDir)\n mkdirSync(hdir, { recursive: true })\n for (const skill of skills) {\n const link = join(hdir, skill)\n if (existsSync(link) || isLink(link)) rmSync(link, { recursive: true, force: true })\n if (useCopy) cpSync(join(aiSkills, skill), link, { recursive: true })\n else symlinkSync(`${ad.linkBase}/${skill}`, link)\n }\n wired.push(`${harness} -> ${ad.skillsDir} (${useCopy ? 'copy' : 'symlink'})`)\n }\n return wired\n}\n","// Interactive `init` wizard (opt-in via --interactive). Discovers the available\n// adapters on disk and builds a framework.config object via @clack/prompts.\nimport { readdirSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport * as p from '@clack/prompts'\n\nfunction listAdapters(root: string, axis: string): string[] {\n const dir = join(root, 'adapters', axis)\n if (!existsSync(dir)) return []\n return readdirSync(dir).filter((n) => existsSync(join(dir, n, 'adapter.json')))\n}\n\nfunction bail<T>(value: T | symbol): T {\n if (p.isCancel(value)) {\n p.cancel('Cancelled — nothing written.')\n process.exit(1)\n }\n return value as T\n}\n\nconst axisOptions = (root: string, axis: string): { value: string | null; label: string }[] => [\n { value: null, label: 'none' },\n ...listAdapters(root, axis).map((v) => ({ value: v, label: v })),\n]\n\nexport async function runWizard(root: string): Promise<Record<string, unknown>> {\n p.intro('aef init')\n\n const projectName = bail(\n await p.text({ message: 'Project name', placeholder: 'my-app', defaultValue: 'my-app' }),\n )\n const harnesses = bail(\n await p.multiselect({\n message: 'Which AI harnesses should be wired?',\n options: listAdapters(root, 'harness').map((v) => ({ value: v, label: v })),\n required: true,\n }),\n )\n const orm = bail(\n await p.select({ message: 'ORM adapter', options: axisOptions(root, 'orm'), initialValue: null }),\n )\n const ui = bail(\n await p.select({ message: 'UI adapter', options: axisOptions(root, 'ui'), initialValue: null }),\n )\n\n p.outro('Configuration ready.')\n\n return {\n $schema: './schemas/framework.config.schema.json',\n projectName,\n harnesses,\n orm,\n ui,\n }\n}\n","// `aef sync` — re-render from the (updated) framework source and reconcile with\n// local edits via a git-native 3-way merge (BASE = last render, LOCAL = on-disk,\n// NEW = fresh render). Exit code 2 signals unresolved conflicts written to the tree.\nimport { readFileSync, writeFileSync, existsSync, rmSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { selectSkills } from '../../core/select.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { reconcileSkill, type ReconcileStatus } from '../reconcile.js'\nimport { writeManifest, wireNewSkills, harnessSkillsDir, type ManifestSkills } from '../consumer-io.js'\n\ninterface RenderManifestFile {\n selection: unknown\n skills: ManifestSkills\n}\n\nconst REPORT: Record<ReconcileStatus, (s: string) => string> = {\n installed: (s) => ` + ${s}: installed`,\n unchanged: (s) => ` = ${s}: framework unchanged`,\n forwarded: (s) => ` ↑ ${s}: updated (no local edits)`,\n merged: (s) => ` ⇄ ${s}: merged local edits + framework update (clean)`,\n conflict: (s) => ` ⇄ ${s}: merged — CONFLICT(S), resolve in working tree`,\n}\n\nexport interface SyncOptions {\n out?: string\n}\n\nexport function runSync(opts: SyncOptions): void {\n const root = FRAMEWORK_ROOT\n const out = opts.out ?? join(root, 'examples/consumer')\n const config = FrameworkConfigSchema.parse(\n JSON.parse(readFileSync(join(out, 'framework.config.json'), 'utf8')),\n )\n const manifestPath = join(out, '.ai', '.render-manifest.json')\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as RenderManifestFile\n\n const { skills: desired } = selectSkills(root, config)\n const desiredSet = new Set(desired)\n const installed = Object.keys(manifest.skills)\n\n const harnessDirs = new Map<string, string | null>()\n for (const h of config.harnesses) harnessDirs.set(h, harnessSkillsDir(root, h))\n\n // Remove skills no longer in the desired set (tier removed or axis cleared).\n for (const s of installed) {\n if (desiredSet.has(s)) continue\n rmSync(join(out, '.ai', 'skills', s), { recursive: true, force: true })\n rmSync(join(out, '.ai', '.base', s), { recursive: true, force: true })\n delete manifest.skills[s]\n for (const dir of harnessDirs.values())\n if (dir) rmSync(join(out, dir, s), { recursive: true, force: true })\n }\n\n const report: string[] = []\n let conflicts = 0\n const newSkills: string[] = []\n for (const skill of desired) {\n const isNew = !manifest.skills[skill]\n const r = reconcileSkill(root, out, config, skill)\n manifest.skills[skill] = { digest: r.digest, inputs: r.inputs }\n conflicts += r.conflicts\n report.push(REPORT[r.status](skill))\n if (isNew) newSkills.push(skill)\n }\n\n if (newSkills.length > 0) wireNewSkills(root, out, config, newSkills)\n\n writeManifest(out, config, manifest.skills)\n\n console.log(`Synced ${relative(process.cwd(), out) || '.'} from framework source`)\n // Refresh synced read-only framework lessons (loop closure, decision #8).\n const fwLessonsSrc = join(root, 'core', 'ai', 'lessons.framework.md')\n if (existsSync(fwLessonsSrc)) {\n const dest = join(out, '.ai', 'lessons.framework.md')\n const next = readFileSync(fwLessonsSrc, 'utf8')\n if (!existsSync(dest) || readFileSync(dest, 'utf8') !== next) {\n writeFileSync(dest, next)\n report.push(' ↑ lessons.framework.md: refreshed (synced, read-only)')\n }\n }\n\n report.forEach((l) => console.log(l))\n if (conflicts) {\n console.log(\n `\\n${conflicts} conflict(s) written as <<<<<<< markers. Review with 'git diff', resolve, commit.`,\n )\n process.exitCode = 2\n }\n}\n","// Shared per-skill reconcile: render a skill from the current framework source and\n// fold the result into a consumer, preserving local edits via a git-native 3-way merge.\n// Used by both `aef sync` and `aef add`/`remove` so the reconcile semantics —\n// fast-forward, clean merge, conflict, and provenance refresh — live in exactly one place.\nimport { readFileSync, writeFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { renderSkill, type InputRef } from '../core/render.js'\nimport { mergeFile } from '../core/merge.js'\nimport type { FrameworkConfig } from '../core/contracts.js'\nimport { writeSkill, writeBase } from './consumer-io.js'\n\nexport type ReconcileStatus = 'installed' | 'unchanged' | 'forwarded' | 'merged' | 'conflict'\n\nexport interface ReconcileResult {\n status: ReconcileStatus\n digest: string\n inputs: InputRef[]\n conflicts: number\n}\n\n/**\n * Reconcile one skill in `out` against the current render:\n * - not yet on disk → install fresh (+ BASE),\n * - framework unchanged → leave LOCAL alone (local edits kept),\n * - framework changed, no edits → fast-forward to NEW,\n * - both changed → 3-way merge (conflicts land as <<<<<<< markers).\n * Always refreshes provenance.json to the fresh render and returns the new digest/inputs\n * so the caller can update the render manifest.\n */\nexport function reconcileSkill(\n root: string,\n out: string,\n config: FrameworkConfig,\n skill: string,\n): ReconcileResult {\n const { rendered: NEW, manifest, digest } = renderSkill(root, config, skill)\n const skillMd = join(out, '.ai', 'skills', skill, 'SKILL.md')\n\n if (!existsSync(skillMd)) {\n writeSkill(out, skill, NEW, manifest)\n writeBase(out, skill, NEW)\n return { status: 'installed', digest, inputs: manifest.inputs, conflicts: 0 }\n }\n\n const baseMd = join(out, '.ai', '.base', skill, 'SKILL.md')\n const BASE = existsSync(baseMd) ? readFileSync(baseMd, 'utf8') : NEW\n const LOCAL = readFileSync(skillMd, 'utf8')\n\n let status: ReconcileStatus = 'unchanged'\n let conflicts = 0\n if (NEW !== BASE) {\n if (LOCAL === BASE) {\n writeFileSync(skillMd, NEW)\n status = 'forwarded'\n } else {\n const { merged, conflicts: n } = mergeFile(LOCAL, BASE, NEW)\n writeFileSync(skillMd, merged)\n conflicts = n\n status = n ? 'conflict' : 'merged'\n }\n writeBase(out, skill, NEW)\n }\n // Provenance always tracks the fresh render (improve-framework reads it to route edits).\n writeFileSync(\n join(out, '.ai', 'skills', skill, 'provenance.json'),\n JSON.stringify(manifest, null, 2) + '\\n',\n )\n return { status, digest, inputs: manifest.inputs, conflicts }\n}\n","// `aef dev` — the \"meta\" install for developing THIS framework. Wires the repo's\n// own harness skill dirs to dev/ skills (source symlinks). Installs the toolchain, not\n// the shipped product; harness dirs are gitignored.\nimport { readFileSync, readdirSync, existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { AdapterSchema } from '../../core/contracts.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { isLink } from '../consumer-io.js'\n\nexport function runDev(): void {\n const root = FRAMEWORK_ROOT\n const devSkillsDir = join(root, 'dev', 'skills')\n if (!existsSync(devSkillsDir)) {\n console.error('no dev/skills/ found')\n process.exit(1)\n }\n const devSkills = readdirSync(devSkillsDir).filter((d) => existsSync(join(devSkillsDir, d, 'SKILL.md')))\n const harnessRoot = join(root, 'adapters', 'harness')\n const harnesses = readdirSync(harnessRoot).filter((d) => existsSync(join(harnessRoot, d, 'adapter.json')))\n\n const wired: string[] = []\n for (const harness of harnesses) {\n const ad = AdapterSchema.parse(\n JSON.parse(readFileSync(join(harnessRoot, harness, 'adapter.json'), 'utf8')),\n )\n if (!ad.skillsDir) continue\n const hdir = join(root, ad.skillsDir)\n mkdirSync(hdir, { recursive: true })\n const target = relative(hdir, devSkillsDir) // robust relative link target\n for (const skill of devSkills) {\n const link = join(hdir, skill)\n if (existsSync(link) || isLink(link)) rmSync(link, { recursive: true, force: true })\n symlinkSync(join(target, skill), link)\n }\n wired.push(`${harness} -> ${ad.skillsDir}`)\n }\n console.log('Framework dev install (meta):')\n console.log(` dev skills: ${devSkills.join(', ') || '(none)'}`)\n console.log(` harnesses : ${wired.join(' | ')}`)\n console.log(' (harness dirs are gitignored; re-run after adding a dev skill)')\n}\n","// `aef render` — render a single skill to stdout (or --out dir) for inspection.\n// The lightweight counterpart to `init`; handy while authoring skills/adapters and\n// the render step the improve-framework skill points at.\nimport { readFileSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema } from '../../core/contracts.js'\nimport { renderSkill } from '../../core/render.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\n\nexport interface RenderOptions {\n skill: string\n config?: string\n out?: string\n}\n\nexport function runRender(opts: RenderOptions): void {\n if (!opts.config) {\n throw new Error(\n `--config is required.\\n\\nUsage: aef render --skill <name> --config framework.config.json\\n\\nRun 'aef render --help' for all options.`,\n )\n }\n const root = FRAMEWORK_ROOT\n const config = FrameworkConfigSchema.parse(JSON.parse(readFileSync(opts.config, 'utf8')))\n const { rendered, manifest, digest } = renderSkill(root, config, opts.skill)\n\n if (opts.out) {\n const dest = join(opts.out, opts.skill)\n mkdirSync(dest, { recursive: true })\n writeFileSync(join(dest, 'SKILL.md'), rendered)\n writeFileSync(join(dest, 'provenance.json'), JSON.stringify(manifest, null, 2) + '\\n')\n console.error(\n `Rendered '${opts.skill}' (orm=${manifest.selection.orm ?? 'none'}) -> ${relative(root, dest)}`,\n )\n console.error(\n ` digest ${digest} · ${manifest.regions.length} regions · ${manifest.inputs.length} inputs`,\n )\n } else {\n // Body to stdout (pipeable); summary to stderr.\n process.stdout.write(rendered)\n console.error(\n ` digest ${digest} · ${manifest.regions.length} regions · ${manifest.inputs.length} inputs`,\n )\n }\n}\n","// `aef add <adapter>` / `aef remove <adapter>` — change an axis selection in a\n// consumer's framework.config.json and reconcile the installed skill set:\n// • newly-selected skills are rendered fresh (+ BASE snapshot),\n// • skills no longer selected are uninstalled (skill dir, BASE, harness links),\n// • surviving skills are 3-way merged (like sync) so adapter-content changes flow in\n// without clobbering local edits.\n// The adapter's axis is read from its adapter.json, so the command is `add <name>`.\nimport { readFileSync, writeFileSync, existsSync, rmSync, cpSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { FrameworkConfigSchema, type FrameworkConfig } from '../../core/contracts.js'\nimport { selectSkills, loadTiers } from '../../core/select.js'\nimport { FRAMEWORK_ROOT } from '../root.js'\nimport { writeManifest, wireHarnesses, harnessSkillsDir, type ManifestSkills } from '../consumer-io.js'\nimport { reconcileSkill } from '../reconcile.js'\n\nconst AXES = ['orm', 'ui', 'stack', 'harness'] as const\n\ninterface ManifestFile {\n selection: unknown\n skills: ManifestSkills\n}\n\nexport interface AdapterCmdOptions {\n out?: string\n copy?: boolean\n}\n\n/** Find which axis an adapter name belongs to by locating its adapter.json on disk. */\nfunction resolveAxis(root: string, name: string): (typeof AXES)[number] {\n for (const axis of AXES) {\n if (existsSync(join(root, 'adapters', axis, name, 'adapter.json'))) return axis\n }\n throw new Error(`no adapter '${name}' found under adapters/{${AXES.join(',')}}/`)\n}\n\nexport function runAdd(name: string, opts: AdapterCmdOptions): void {\n mutate(name, 'add', opts)\n}\n\nexport function runRemove(name: string, opts: AdapterCmdOptions): void {\n mutate(name, 'remove', opts)\n}\n\nfunction mutate(name: string, op: 'add' | 'remove', opts: AdapterCmdOptions): void {\n const root = FRAMEWORK_ROOT\n const out = opts.out ?? join(root, 'examples/consumer')\n const useCopy = Boolean(opts.copy)\n const cfgPath = join(out, 'framework.config.json')\n\n const raw = JSON.parse(readFileSync(cfgPath, 'utf8')) as Record<string, unknown>\n const prevConfig = FrameworkConfigSchema.parse(raw)\n const prevHarnesses = [...(prevConfig.harnesses ?? [])]\n\n const tierDef = loadTiers(root)\n const isTier = name in tierDef.tiers\n\n if (isTier) {\n if (tierDef.default.includes(name))\n throw new Error(\n `tier '${name}' is a default tier — it is always active and cannot be ${op === 'add' ? 'explicitly opted in' : 'removed'}`,\n )\n const current = prevConfig.tiers ?? []\n if (op === 'add') {\n if (!current.includes(name)) raw.tiers = [...current, name]\n } else {\n if (!current.includes(name)) throw new Error(`tier '${name}' is not enabled; nothing to remove`)\n const after = current.filter((t) => t !== name)\n if (after.length > 0) raw.tiers = after\n else delete raw.tiers\n }\n } else {\n const axis = resolveAxis(root, name)\n if (axis === 'harness') {\n const list = new Set(prevHarnesses)\n if (op === 'add') {\n list.add(name)\n } else {\n if (!list.has(name)) throw new Error(`harness '${name}' is not installed; nothing to remove`)\n if (list.size === 1) throw new Error('cannot remove the last harness — at least one is required')\n list.delete(name)\n }\n raw.harnesses = [...list]\n } else if (op === 'add') {\n raw[axis] = name\n } else {\n if (raw[axis] !== name)\n throw new Error(`'${name}' is not the active ${axis} adapter; nothing to remove`)\n raw[axis] = null\n }\n }\n\n const config = FrameworkConfigSchema.parse(raw)\n writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + '\\n')\n\n if (isTier && name === 'framework') {\n const outboxDst = join(out, '.ai', 'framework-feedback')\n if (op === 'add') {\n const outboxSrc = join(root, 'core', 'ai', 'framework-feedback')\n if (existsSync(outboxSrc)) cpSync(outboxSrc, outboxDst, { recursive: true })\n } else {\n rmSync(outboxDst, { recursive: true, force: true })\n }\n }\n\n const conflicts = reconcile(root, out, config, prevHarnesses, useCopy)\n const kind = isTier ? 'tier' : 'adapter'\n console.log(\n `${op === 'add' ? 'Added' : 'Removed'} ${name} (${kind}) in ${relative(process.cwd(), out) || '.'}`,\n )\n if (conflicts) {\n console.log(\n `\\n${conflicts} conflict(s) written as <<<<<<< markers — review with 'git diff', resolve, commit.`,\n )\n process.exitCode = 2\n }\n}\n\nfunction reconcile(\n root: string,\n out: string,\n config: FrameworkConfig,\n prevHarnesses: string[],\n useCopy: boolean,\n): number {\n const manifestPath = join(out, '.ai', '.render-manifest.json')\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as ManifestFile\n\n const { skills: selected } = selectSkills(root, config)\n const selectedSet = new Set(selected)\n const installed = Object.keys(manifest.skills)\n\n // Resolve each harness's skills dir once (used when removing skills and harnesses).\n const dirByHarness = new Map<string, string | null>()\n for (const h of new Set([...prevHarnesses, ...(config.harnesses ?? [])]))\n dirByHarness.set(h, harnessSkillsDir(root, h))\n\n // 1. Uninstall skills no longer selected.\n for (const s of installed) {\n if (selectedSet.has(s)) continue\n rmSync(join(out, '.ai', 'skills', s), { recursive: true, force: true })\n rmSync(join(out, '.ai', '.base', s), { recursive: true, force: true })\n delete manifest.skills[s]\n for (const dir of dirByHarness.values())\n if (dir) rmSync(join(out, dir, s), { recursive: true, force: true })\n }\n\n // 2. Install new + 3-way-merge surviving skills (shared with `sync`).\n let conflicts = 0\n for (const s of selected) {\n const r = reconcileSkill(root, out, config, s)\n manifest.skills[s] = { digest: r.digest, inputs: r.inputs }\n conflicts += r.conflicts\n }\n\n // 3. Drop skills dirs for harnesses removed entirely, then re-wire the surviving set.\n for (const h of prevHarnesses) {\n if ((config.harnesses ?? []).includes(h)) continue\n const dir = dirByHarness.get(h)\n if (dir && existsSync(join(out, dir))) rmSync(join(out, dir), { recursive: true, force: true })\n }\n wireHarnesses(root, out, config, selected, useCopy)\n\n writeManifest(out, config, manifest.skills)\n return conflicts\n}\n"],"mappings":"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","names":["readFileSync","readFileSync","writeFileSync","join","join","p","join","existsSync","join","readFileSync","join","writeFileSync","readFileSync","writeFileSync","existsSync","rmSync","join","relative","readFileSync","writeFileSync","existsSync","join","join","existsSync","readFileSync","writeFileSync","join","readFileSync","rmSync","relative","existsSync","writeFileSync","readFileSync","readdirSync","existsSync","mkdirSync","rmSync","symlinkSync","join","relative","join","existsSync","readdirSync","readFileSync","mkdirSync","relative","rmSync","symlinkSync","readFileSync","writeFileSync","mkdirSync","join","relative","readFileSync","join","mkdirSync","writeFileSync","relative","readFileSync","writeFileSync","existsSync","rmSync","cpSync","join","relative","existsSync","join","readFileSync","writeFileSync","cpSync","rmSync","relative","readFileSync"]}
package/dist/index.d.ts CHANGED
@@ -32,6 +32,7 @@ declare const FrameworkConfigSchema: z.ZodObject<{
32
32
  defaultBranch?: string | undefined;
33
33
  labels?: string[] | undefined;
34
34
  }>>;
35
+ tiers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
35
36
  source: z.ZodOptional<z.ZodObject<{
36
37
  path: z.ZodOptional<z.ZodNullable<z.ZodString>>;
37
38
  repo: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -99,6 +100,7 @@ declare const FrameworkConfigSchema: z.ZodObject<{
99
100
  defaultBranch?: string | undefined;
100
101
  labels?: string[] | undefined;
101
102
  } | undefined;
103
+ tiers?: string[] | undefined;
102
104
  source?: {
103
105
  path?: string | null | undefined;
104
106
  repo?: string | null | undefined;
@@ -130,6 +132,7 @@ declare const FrameworkConfigSchema: z.ZodObject<{
130
132
  defaultBranch?: string | undefined;
131
133
  labels?: string[] | undefined;
132
134
  } | undefined;
135
+ tiers?: string[] | undefined;
133
136
  source?: {
134
137
  path?: string | null | undefined;
135
138
  repo?: string | null | undefined;
@@ -169,28 +172,28 @@ declare const AdapterSchema: z.ZodObject<{
169
172
  name: string;
170
173
  axis: string;
171
174
  $schema?: string | undefined;
175
+ tiers?: {
176
+ skill: string;
177
+ tier: string;
178
+ }[] | undefined;
172
179
  description?: string | undefined;
173
180
  skillsDir?: string | undefined;
174
181
  linkBase?: string | undefined;
175
182
  augments?: string[] | undefined;
176
183
  slots?: Record<string, string> | undefined;
177
- tiers?: {
178
- skill: string;
179
- tier: string;
180
- }[] | undefined;
181
184
  }, {
182
185
  name: string;
183
186
  axis: string;
184
187
  $schema?: string | undefined;
188
+ tiers?: {
189
+ skill: string;
190
+ tier: string;
191
+ }[] | undefined;
185
192
  description?: string | undefined;
186
193
  skillsDir?: string | undefined;
187
194
  linkBase?: string | undefined;
188
195
  augments?: string[] | undefined;
189
196
  slots?: Record<string, string> | undefined;
190
- tiers?: {
191
- skill: string;
192
- tier: string;
193
- }[] | undefined;
194
197
  }>;
195
198
  /** core/ai/skills/tiers.json — declares the default tier set and per-skill axis requirements. */
196
199
  declare const TiersSchema: z.ZodObject<{
@@ -262,6 +265,7 @@ interface SkillSelection {
262
265
  skills: string[];
263
266
  skipped: string[];
264
267
  }
268
+ declare function loadTiers(root: string): Tiers;
265
269
  declare function selectSkills(root: string, config: FrameworkConfig): SkillSelection;
266
270
 
267
271
  interface MergeResult {
@@ -270,4 +274,4 @@ interface MergeResult {
270
274
  }
271
275
  declare function mergeFile(local: string, base: string, theirs: string): MergeResult;
272
276
 
273
- export { type Adapter, AdapterSchema, type FrameworkConfig, FrameworkConfigSchema, type InputRef, type MergeResult, type Region, type RenderManifest, type RenderResult, SLOT_ANY, SLOT_OPEN, type SkillSelection, type Tiers, TiersSchema, mergeFile, renderSkill, selectSkills };
277
+ export { type Adapter, AdapterSchema, type FrameworkConfig, FrameworkConfigSchema, type InputRef, type MergeResult, type Region, type RenderManifest, type RenderResult, SLOT_ANY, SLOT_OPEN, type SkillSelection, type Tiers, TiersSchema, loadTiers, mergeFile, renderSkill, selectSkills };
package/dist/index.js CHANGED
@@ -4,16 +4,18 @@ import {
4
4
  SLOT_ANY,
5
5
  SLOT_OPEN,
6
6
  TiersSchema,
7
+ loadTiers,
7
8
  mergeFile,
8
9
  renderSkill,
9
10
  selectSkills
10
- } from "./chunk-YENPV3OD.js";
11
+ } from "./chunk-QEUDIAAQ.js";
11
12
  export {
12
13
  AdapterSchema,
13
14
  FrameworkConfigSchema,
14
15
  SLOT_ANY,
15
16
  SLOT_OPEN,
16
17
  TiersSchema,
18
+ loadTiers,
17
19
  mergeFile,
18
20
  renderSkill,
19
21
  selectSkills
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zizzfizzix/aef",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Slot-based, adapter-driven engineering skills for AI coding harnesses.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -76,6 +76,13 @@
76
76
  },
77
77
  "additionalProperties": false
78
78
  },
79
+ "tiers": {
80
+ "type": "array",
81
+ "items": {
82
+ "type": "string"
83
+ },
84
+ "description": "Opt-in tiers to install in addition to the defaults (e.g. [\"framework\", \"automation\"])."
85
+ },
79
86
  "source": {
80
87
  "type": "object",
81
88
  "properties": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/render.ts","../src/core/contracts.ts","../src/core/select.ts","../src/core/merge.ts"],"sourcesContent":["// Pure skill renderer — the heart of the framework. No I/O beyond reading source\n// files; returns { rendered, manifest, digest }. Kept dependency-light (node builtins\n// + a type-only contract import) because it runs in consumer repos, in CI, and inside\n// the improve-framework skill.\n//\n// Properties (proven in docs/render-model.md and asserted by the gate): convergence,\n// determinism, provenance, per-skill digest scoping.\nimport { readFileSync, existsSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport type { FrameworkConfig } from './contracts.js'\n\nconst AXES = ['orm', 'ui', 'stack'] as const\ntype Axis = (typeof AXES)[number]\n\nconst sha = (s: string): string => createHash('sha256').update(s).digest('hex').slice(0, 12)\nconst norm = (s: string): string => s.replace(/\\r\\n/g, '\\n')\n\nconst OPEN = /^<!--\\s*SLOT:([\\w.-]+)\\s*-->$/\nconst CLOSE = /^<!--\\s*\\/SLOT:([\\w.-]+)\\s*-->$/\n\n// Shared, single source of truth for what a slot marker is — imported by the gate so\n// the gate and the renderer can never disagree (a slot is a marker ALONE ON ITS OWN\n// LINE; `SLOT:` mentioned in prose/backticks is not a slot and is never touched).\nexport const SLOT_OPEN = OPEN\nexport const SLOT_ANY = /^<!--\\s*\\/?SLOT:[\\w.-]+\\s*-->$/\n\nexport interface InputRef {\n path: string\n sha: string\n}\n\nexport interface Region {\n source: string\n adapter: string | null\n slot: string | null\n startLine: number\n endLine: number\n}\n\nexport interface RenderManifest {\n skill: string\n selection: Partial<Record<Axis, string>>\n digest: string\n note: string\n inputs: InputRef[]\n regions: Region[]\n}\n\nexport interface RenderResult {\n rendered: string\n manifest: RenderManifest\n digest: string\n}\n\ninterface SlotProvider {\n adapter: string\n axis: Axis\n source: string\n content: string\n}\n\nexport function renderSkill(root: string, config: FrameworkConfig, skill: string): RenderResult {\n const rel = (p: string): string => relative(root, p)\n\n // Active adapter per axis -> slot lookup (no inputs counted here).\n const selected: Partial<Record<Axis, string>> = {}\n for (const axis of AXES) {\n const value = config[axis]\n if (value) selected[axis] = value\n }\n\n const slotProviders: Record<string, SlotProvider> = {}\n for (const [axis, adapter] of Object.entries(selected) as [Axis, string][]) {\n const adDir = join(root, 'adapters', axis, adapter)\n const adPath = join(adDir, 'adapter.json')\n if (!existsSync(adPath)) continue\n const ad = JSON.parse(readFileSync(adPath, 'utf8')) as { slots?: Record<string, string> }\n for (const [slot, file] of Object.entries(ad.slots ?? {})) {\n const fp = join(adDir, file)\n const content = norm(readFileSync(fp, 'utf8')).replace(/\\s+$/, '') + '\\n'\n slotProviders[slot] = { adapter, axis, source: rel(fp), content }\n }\n }\n\n const genPath = join(root, 'core/ai/skills', skill, 'SKILL.md')\n const generic = norm(readFileSync(genPath, 'utf8'))\n\n // Inputs = only files that actually compose THIS skill (generic + filled fragments).\n const inputs: InputRef[] = [{ path: rel(genPath), sha: sha(generic) }]\n const seenInput = new Set<string>([rel(genPath)])\n\n const lines = generic.split('\\n')\n const out: string[] = []\n const regions: Region[] = []\n let bodyLine = 0\n const push = (line: string, source: string, meta?: { adapter: string; slot: string }): void => {\n out.push(line)\n bodyLine++\n const last = regions[regions.length - 1]\n const slot = meta?.slot ?? null\n if (last && last.source === source && last.slot === slot && last.endLine === bodyLine - 1) {\n last.endLine = bodyLine\n } else {\n regions.push({ source, adapter: meta?.adapter ?? null, slot, startLine: bodyLine, endLine: bodyLine })\n }\n }\n\n let i = 0\n while (i < lines.length) {\n const current = lines[i] as string\n const open = current.match(OPEN)\n if (open) {\n const slot = open[1] as string\n let j = i + 1\n while (\n j < lines.length &&\n !(CLOSE.test(lines[j] as string) && (lines[j] as string).match(CLOSE)![1] === slot)\n )\n j++\n const provider = slotProviders[slot]\n if (provider) {\n if (!seenInput.has(provider.source)) {\n seenInput.add(provider.source)\n inputs.push({ path: provider.source, sha: sha(provider.content) })\n }\n for (const cl of provider.content.replace(/\\n$/, '').split('\\n')) {\n push(cl, provider.source, { adapter: provider.adapter, slot })\n }\n } // no provider (axis unselected, or active adapter omits this slot) -> prune\n i = j + 1\n continue\n }\n push(current, rel(genPath))\n i++\n }\n\n const sortedInputs = [...inputs].sort((a, b) => a.path.localeCompare(b.path))\n const digest = sha(JSON.stringify(sortedInputs))\n const header = `<!-- generated by aef render — do not hand-edit; use the improve-framework skill or edit the framework source. digest:${digest} -->`\n const rendered = header + '\\n\\n' + out.join('\\n') + '\\n'\n\n const manifest: RenderManifest = {\n skill,\n selection: selected,\n digest,\n note: 'region line numbers are body-relative (line 1 = first line after the 2-line header); digest covers consumed inputs only',\n inputs: sortedInputs,\n regions,\n }\n return { rendered, manifest, digest }\n}\n","// Typed contracts — the single source of truth for every config/adapter shape the\n// framework reads. zod gives us (1) runtime validation in the CLI, (2) inferred TS\n// types for the codebase, and (3) JSON Schema (see scripts/gen-schemas.ts) published\n// under schemas/ for editor/CI validation. Change a shape here and regenerate.\nimport { z } from 'zod'\n\nconst slotName = z\n .string()\n .regex(/^[a-z][\\w-]*\\.[a-z][\\w-]*$/, \"slot names are '<axis>.<key>', e.g. 'orm.cheatsheet'\")\n\n/** Per-consumer configuration the renderer, init/sync, and skills all read. */\nexport const FrameworkConfigSchema = z\n .object({\n $schema: z.string().optional(),\n projectName: z.string().describe('Substituted for {{PROJECT_NAME}} in templates.').optional(),\n harnesses: z\n .array(z.string())\n .min(1)\n .describe('Active harness adapters; each wires its files + skills dir.'),\n orm: z.string().nullable().describe('Selected ORM adapter, or null to omit ORM skills.').optional(),\n ui: z.string().nullable().optional(),\n stack: z.string().nullable().optional(),\n paths: z\n .object({\n modulesRoot: z.string().optional(),\n specsRoot: z.string().optional(),\n testsRoot: z.string().optional(),\n })\n .strict()\n .optional(),\n validation: z.array(z.string()).describe('Commands skills run as the verification gate.').optional(),\n git: z\n .object({\n defaultBranch: z.string().optional(),\n labels: z.array(z.string()).optional(),\n })\n .strict()\n .optional(),\n source: z\n .object({\n path: z\n .string()\n .nullable()\n .describe('Local checkout path; if null, clone repo on demand.')\n .optional(),\n repo: z.string().nullable().optional(),\n })\n .strict()\n .describe('Where improve-framework finds the framework SOURCE.')\n .optional(),\n feedback: z\n .object({\n capture: z.boolean().default(true).describe('Write scope-tagged lessons locally; zero egress.'),\n upstream: z\n .object({\n mode: z.enum(['scheduled-pr', 'prompt', 'off']).default('scheduled-pr'),\n channel: z.enum(['pr', 'issue', 'fork']).default('pr'),\n schedule: z.string().default('weekly'),\n sanitize: z.boolean().default(true),\n requireHumanApproval: z.boolean().default(true),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional(),\n })\n .strict()\n\n/** An adapter declares which skills it augments and the slot content it provides. */\nexport const AdapterSchema = z\n .object({\n $schema: z.string().optional(),\n name: z.string().describe(\"Adapter id, e.g. 'drizzle'.\"),\n axis: z.string().describe('Variation family: orm | ui | stack | harness.'),\n description: z.string().optional(),\n skillsDir: z.string().describe('Harness only: directory the per-skill links go in.').optional(),\n linkBase: z.string().describe('Harness only: relative link target base from skillsDir.').optional(),\n augments: z.array(z.string()).describe('Skill ids this adapter contributes slot content to.').optional(),\n slots: z\n .record(slotName, z.string())\n .describe('slotName -> fragment path, relative to the adapter dir.')\n .optional(),\n tiers: z\n .array(z.object({ skill: z.string(), tier: z.string() }).strict())\n .describe('Optional skills this adapter adds, and the tier each joins.')\n .optional(),\n })\n .strict()\n\n/** core/ai/skills/tiers.json — declares the default tier set and per-skill axis requirements. */\nexport const TiersSchema = z\n .object({\n default: z.array(z.string()),\n tiers: z.record(z.string(), z.object({ skills: z.array(z.string()) }).passthrough()),\n requires: z.record(z.string(), z.string()).optional(),\n })\n .passthrough()\n\nexport type FrameworkConfig = z.infer<typeof FrameworkConfigSchema>\nexport type Adapter = z.infer<typeof AdapterSchema>\nexport type Tiers = z.infer<typeof TiersSchema>\n","// Tier resolution — turns the configured axis selections into the converged skill set.\n// A skill whose required axis is unconfigured is skipped (reported, not errored).\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { FrameworkConfig } from './contracts.js'\nimport { TiersSchema } from './contracts.js'\n\nexport interface SkillSelection {\n skills: string[]\n skipped: string[]\n}\n\nexport function selectSkills(root: string, config: FrameworkConfig): SkillSelection {\n const tiers = TiersSchema.parse(JSON.parse(readFileSync(join(root, 'core/ai/skills/tiers.json'), 'utf8')))\n const requires = tiers.requires ?? {}\n\n const candidates: string[] = []\n for (const t of tiers.default)\n for (const s of tiers.tiers[t]?.skills ?? []) if (!candidates.includes(s)) candidates.push(s)\n\n const skills: string[] = []\n const skipped: string[] = []\n for (const s of candidates) {\n const axis = requires[s]\n if (axis && !config[axis as keyof FrameworkConfig]) skipped.push(`${s} (needs ${axis})`)\n else skills.push(s)\n }\n return { skills: skills.sort(), skipped }\n}\n","// 3-way merge delegated to git — no custom merge engine. BASE = last render,\n// LOCAL = on-disk (with consumer edits), THEIRS = fresh framework render.\n// Returns the merged text and the conflict count git reports.\nimport { writeFileSync, mkdtempSync, rmSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { tmpdir } from 'node:os'\nimport { spawnSync } from 'node:child_process'\n\nexport interface MergeResult {\n merged: string\n conflicts: number\n}\n\nexport function mergeFile(local: string, base: string, theirs: string): MergeResult {\n const dir = mkdtempSync(join(tmpdir(), 'agentic-merge-'))\n const lp = join(dir, 'local')\n const bp = join(dir, 'base')\n const tp = join(dir, 'theirs')\n writeFileSync(lp, local)\n writeFileSync(bp, base)\n writeFileSync(tp, theirs)\n const r = spawnSync(\n 'git',\n [\n 'merge-file',\n '-p',\n '-L',\n 'ours (your local edits)',\n '-L',\n 'base (last sync)',\n '-L',\n 'theirs (framework update)',\n lp,\n bp,\n tp,\n ],\n { encoding: 'utf8' },\n )\n rmSync(dir, { recursive: true, force: true })\n if (r.status === null || r.status < 0 || r.status >= 255)\n throw new Error(`git merge-file failed: ${r.stderr || String(r.error)}`)\n return { merged: r.stdout, conflicts: r.status }\n}\n"],"mappings":"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","names":["readFileSync","join","readFileSync","join","join"]}