From b92ca45fc5891181fb7edd786fff80064b70fe24 Mon Sep 17 00:00:00 2001 From: B499OU Date: Mon, 22 Jun 2026 14:40:09 +0200 Subject: [PATCH 1/2] Save uncommitted changes --- zava-storefront/package-lock.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 zava-storefront/package-lock.json diff --git a/zava-storefront/package-lock.json b/zava-storefront/package-lock.json new file mode 100644 index 0000000..37c41b8 --- /dev/null +++ b/zava-storefront/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "zava-storefront", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 3c8316bec74a0b5ca4cc513c78ec287b91a8b169 Mon Sep 17 00:00:00 2001 From: B499OU Date: Mon, 22 Jun 2026 15:28:01 +0200 Subject: [PATCH 2/2] feat(skills): add build-and-pr and framework-modernizer skills - Add .github/skills/build-and-pr skill: given a feature brief or review findings, implements the change on a new branch, loads team guidelines (security, architecture, docs), runs npm run lint and npm test (fixing failures before continuing), then opens or updates a PR. Out-of-scope items noted in PR description rather than coded. - Add framework-modernizer skill with full evals suite, breaking- changes reference, classifier rubric, and phased plan template. - Add my-skill placeholder stub as install harness target. - Mirror both skills into .apm/skills/ for APM module resolution. - Update apm.lock.yaml to reflect new skill registrations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .agents/skills/framework-modernizer/SKILL.md | 99 ++++++++++ .../framework-modernizer/evals/README.md | 75 +++++++ .../framework-modernizer/evals/evals.json | 48 +++++ .../evals/expected/findings.txt | 10 + .../evals/fixtures/express4-app/package.json | 10 + .../evals/fixtures/express4-app/server.js | 43 ++++ .../skills/framework-modernizer/evals/run.js | 98 ++++++++++ .../framework-modernizer/evals/triggers.json | 36 ++++ .../framework-modernizer/references/DESIGN.md | 148 ++++++++++++++ .../references/classifier-rubric.md | 29 +++ .../express-4-to-5-breaking-changes.md | 107 ++++++++++ .../references/phased-plan-template.md | 77 ++++++++ .agents/skills/my-skill/SKILL.md | 59 ++++++ .apm/skills/framework-modernizer/SKILL.md | 99 ++++++++++ .../framework-modernizer/evals/README.md | 75 +++++++ .../framework-modernizer/evals/evals.json | 48 +++++ .../evals/expected/findings.txt | 10 + .../evals/fixtures/express4-app/package.json | 10 + .../evals/fixtures/express4-app/server.js | 43 ++++ .apm/skills/framework-modernizer/evals/run.js | 98 ++++++++++ .../framework-modernizer/evals/triggers.json | 36 ++++ .../framework-modernizer/references/DESIGN.md | 148 ++++++++++++++ .../references/classifier-rubric.md | 29 +++ .../express-4-to-5-breaking-changes.md | 107 ++++++++++ .../references/phased-plan-template.md | 77 ++++++++ .apm/skills/my-skill/SKILL.md | 59 ++++++ .github/skills/build-and-pr/SKILL.md | 183 ++++++++++++++++++ .../build-and-pr/assets/pr-body-template.md | 46 +++++ apm.lock.yaml | 65 +++++-- 29 files changed, 1960 insertions(+), 12 deletions(-) create mode 100644 .agents/skills/framework-modernizer/SKILL.md create mode 100644 .agents/skills/framework-modernizer/evals/README.md create mode 100644 .agents/skills/framework-modernizer/evals/evals.json create mode 100644 .agents/skills/framework-modernizer/evals/expected/findings.txt create mode 100644 .agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json create mode 100644 .agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js create mode 100644 .agents/skills/framework-modernizer/evals/run.js create mode 100644 .agents/skills/framework-modernizer/evals/triggers.json create mode 100644 .agents/skills/framework-modernizer/references/DESIGN.md create mode 100644 .agents/skills/framework-modernizer/references/classifier-rubric.md create mode 100644 .agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md create mode 100644 .agents/skills/framework-modernizer/references/phased-plan-template.md create mode 100644 .agents/skills/my-skill/SKILL.md create mode 100644 .apm/skills/framework-modernizer/SKILL.md create mode 100644 .apm/skills/framework-modernizer/evals/README.md create mode 100644 .apm/skills/framework-modernizer/evals/evals.json create mode 100644 .apm/skills/framework-modernizer/evals/expected/findings.txt create mode 100644 .apm/skills/framework-modernizer/evals/fixtures/express4-app/package.json create mode 100644 .apm/skills/framework-modernizer/evals/fixtures/express4-app/server.js create mode 100644 .apm/skills/framework-modernizer/evals/run.js create mode 100644 .apm/skills/framework-modernizer/evals/triggers.json create mode 100644 .apm/skills/framework-modernizer/references/DESIGN.md create mode 100644 .apm/skills/framework-modernizer/references/classifier-rubric.md create mode 100644 .apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md create mode 100644 .apm/skills/framework-modernizer/references/phased-plan-template.md create mode 100644 .apm/skills/my-skill/SKILL.md create mode 100644 .github/skills/build-and-pr/SKILL.md create mode 100644 .github/skills/build-and-pr/assets/pr-body-template.md diff --git a/.agents/skills/framework-modernizer/SKILL.md b/.agents/skills/framework-modernizer/SKILL.md new file mode 100644 index 0000000..a46adcb --- /dev/null +++ b/.agents/skills/framework-modernizer/SKILL.md @@ -0,0 +1,99 @@ +--- +name: framework-modernizer +description: >- + Use this skill when the user asks to migrate, upgrade, or modernize a + Node.js codebase from Express 4 to Express 5 — including phrasings like + "bump express to v5", "upgrade express", "migrate to express 5", + "express deprecation warnings", "we're stuck on express 4", or when + preparing a major-version dependency PR involving express ^4.x. + Also fires when the user mentions removed Express APIs (app.del, res.send(status), + the deprecated body-parser bundling), discontinued path-to-regexp 0.x + patterns (e.g. unnamed wildcards `*` or optional segments `:id?`), or + asks for a migration plan / breaking changes audit on an Express 4 repo. + This skill audits, classifies (SAFE / AUTOFIX / MANUAL), applies safe + autofixes, and emits a phased migration plan grounded in the official + Express 5 migration guide. It does NOT run the consumer's tests; it + emits a plan the team executes. +license: UNLICENSED +allowed-tools: Read, Grep, Glob, Edit +--- + +# framework-modernizer + +> **Reference skill — Express 4 → Express 5 migration.** +> Built with [Genesis](https://github.com/DevExpGbb/genesis) as a worked example for the workshop. Read it, fork the pattern, build your own (Next 13→14, React 17→18, Angular 16→17…). + +## When to use this skill + +- The repo's `package.json` has a top-level `express` dependency on `^4.x` and the user wants to move to `^5.x`. +- The user reports deprecation warnings from Express 4 they want resolved. +- A major-version Dependabot/Renovate PR is open and a human asks "is this safe to merge?" + +**Do NOT use this skill when:** +- The repo is not Express (Fastify, Koa, Hapi → wrong tool). +- The Express version is already `^5.x` (no work). +- The user wants you to also write/run tests post-migration (out of scope — emit the plan, the team validates). + +## What this skill does + +1. **Discover.** `Glob` for `package.json` files. `Read` each, confirm `express` is a direct dependency on `^4.x`. Stop early if not found. +2. **Scan.** For each Express 4 app rooted in a discovered `package.json`: + - `Grep -n` the breaking-change patterns from [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md) across `**/*.{js,mjs,cjs,ts}`. + - Collect each hit with its file path, line number, and matched pattern ID (e.g. `BC-001`). +3. **Classify** every finding via [`references/classifier-rubric.md`](references/classifier-rubric.md): + - **SAFE** — no behavior change in v5; informational only. + - **AUTOFIX** — mechanical replacement; this skill applies it via `Edit`. + - **MANUAL** — semantics changed; emit a TODO comment + reference link, do **not** edit. +4. **Apply autofixes.** For each AUTOFIX finding, perform the exact `Edit` specified in the catalog. Print a one-line diff summary per edit. +5. **Emit migration plan.** Write `MIGRATION-PLAN.md` at the repo root using [`references/phased-plan-template.md`](references/phased-plan-template.md). Include three phases: **Phase 1 — Autofixed (this skill)**, **Phase 2 — Manual edits required**, **Phase 3 — Validation checklist**. Cross-reference every MANUAL item back to the official Express 5 migration guide. + +## Outputs + +| Artifact | Where | When | +|---|---|---| +| Per-file `Edit`s for AUTOFIX class | In-place | Step 4 | +| `MIGRATION-PLAN.md` at repo root | New file | Step 5 | +| Console summary: `N safe, M autofixed, K manual` | stdout | End | + +## Constraints + +- **Source-grounded only.** Every finding must trace to a pattern in [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md). Do not invent breaking changes from memory — Express 5 is the reference, not your training data. +- **No package.json bump in this skill.** The migration plan instructs the team to bump `express` to `^5.0.0`; the skill does not rewrite it. (Reason: bumping invalidates the lockfile and triggers a npm install side-effect; that's a deliberate human gate.) +- **No test runs.** Emitting the plan is the deliverable. The team's CI is the oracle. +- **Idempotent.** Re-running the skill on an already-migrated repo emits `0 findings` and no edits. + +## Examples + +### Invocation + +> "Migrate `services/api/` from Express 4 to Express 5." + +### Expected end-state + +``` +Discovered: services/api/package.json (express ^4.18.2) +Scanned: 14 files +Findings: + SAFE × 2 (informational) + AUTOFIX × 3 → applied + MANUAL × 4 → see MIGRATION-PLAN.md + +Wrote: services/api/MIGRATION-PLAN.md +``` + +## How this was designed + +This skill went through the full [Genesis](https://github.com/DevExpGbb/genesis) 8-step process. The handoff packet is in [`references/DESIGN.md`](references/DESIGN.md). Reproducing it for your own framework migration (Next 13→14, React 17→18, etc.): + +1. **Step 1 — intent.** Single capability, single framework pair. Don't try to migrate 5 frameworks in one skill. +2. **Step 2 — components.** PIPELINE pattern: scan → classify → autofix → plan. No fan-out, no panel. +3. **Step 5 — Architecture artifacts.** Three files do the heavy lifting: the **catalog** (cited breaking changes) is loaded as context; the **rubric** (SAFE/AUTOFIX/MANUAL classifier) is the decision boundary; the **skill** orchestrates them. The eval runner is the regression harness — change a regex, watch CI. + +## Evals + +Two layers, each catching different failures (see [`evals/README.md`](evals/README.md) for the full recipe): + +1. **Behavior evals** ([`evals/evals.json`](evals/evals.json), [`evals/triggers.json`](evals/triggers.json)) — inference-based, per [agentskills.io spec](https://agentskills.io/skill-creation/evaluating-skills). Each case runs twice (with_skill / without_skill); the value delta is the evidence. Manual / harness-driven; not in CI. +2. **Catalog regression test** ([`evals/run.js`](evals/run.js)) — pure-Node script that locks the catalog regexes against a deliberate fixture. CI-friendly; exits non-zero on drift. + +Both layers were scaffolded via the [Genesis](https://github.com/DevExpGbb/genesis) skill. diff --git a/.agents/skills/framework-modernizer/evals/README.md b/.agents/skills/framework-modernizer/evals/README.md new file mode 100644 index 0000000..49fe904 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/README.md @@ -0,0 +1,75 @@ +# Evals — framework-modernizer + +This directory holds **two distinct test layers**. Both are valuable; they catch different failures. + +| Layer | What it tests | Output | Runs in CI? | +|---|---|---|---| +| **Behavior evals** ([`evals.json`](./evals.json), [`triggers.json`](./triggers.json)) | The skill's actual LLM behavior. Each case runs twice: with the skill loaded and without it, so the value delta is measurable. | `-workspace/iteration-N//{with_skill,without_skill}/{outputs,timing.json,grading.json}` | No — manual / harness-driven. See "How to run" below. | +| **Catalog regression test** ([`run.js`](./run.js)) | The deterministic substrate the skill depends on. Locks the catalog regexes against the fixture so the team can't silently break detection by editing a regex. | Pure-Node script, exits `0` on green, `1` on drift. | Yes — fast, hermetic, no LLM. | + +The behavior evals are what [agentskills.io calls "evals"](https://agentskills.io/skill-creation/evaluating-skills). The catalog test is a unit test for the catalog file. Both were scaffolded via the [Genesis](https://github.com/DevExpGbb/genesis) skill — Genesis's step-6 EVALS PLAN produces `evals.json` + `triggers.json`; the catalog regression script is independent contributor tooling. + +--- + +## Layer 1 — Behavior evals (the real evals) + +`evals.json` defines 3 content evals + the iteration workflow. `triggers.json` defines ~20 trigger queries (60/40 train/val) for the dispatch description. + +### How to run + +The agentskills.io spec describes the structure but does NOT prescribe a runner — execution is harness-driven. The current canonical recipe: + +1. **Per case in `evals.json`**, spawn TWO subagent runs (or two clean sessions if your harness has no subagent isolation): + - **with_skill**: skill is loaded into the subagent's context. + - **without_skill**: same prompt, no skill — establishes the baseline. +2. Capture each run's outputs into `framework-modernizer-workspace/iteration-N//{with_skill|without_skill}/outputs/`. Capture `timing.json` (`total_tokens`, `duration_ms` — your harness should provide these) alongside. +3. Grade per the case's `assertions[]`. Script-graded assertions (`file_exists`, `contains_all_of`, `no_invented_bc_ids`, `skill_activated`, `skill_declines`, `no_file_writes`) are mechanical. `llm_judge` assertions get a fresh LLM call with the rubric. Write `grading.json`. +4. Aggregate to `framework-modernizer-workspace/iteration-N/benchmark.json`. +5. **Ship gate**: all 3 cases PASS with_skill AND show measurable delta vs without_skill. If `with_skill ≈ without_skill`, the skill is not adding value — redesign or delete. + +For trigger evals: run via the harness's dispatcher (NOT the skill body). The question is whether the dispatch description matches the query. Validation split is the gate (≥0.5 on should-trigger, <0.5 on near-miss should-not-trigger). + +### Iteration discipline (per agentskills.io) + +- **Add assertions AFTER the first run.** You don't know what "good" looks like until you've seen actual output. Iteration 1 is exploratory; iteration 2 hardens with assertions. +- **Iterate the skill body, not the assertions, when assertions fail.** If you're tweaking assertions to pass, you've inverted the test. + +--- + +## Layer 2 — Catalog regression test + +```bash +node .apm/skills/framework-modernizer/evals/run.js +``` + +Exit `0` = all expected findings matched; exit `1` = drift (catalog regex changed, fixture changed, or expected file out of date). CI-friendly. + +### What's here (Layer 2 files) + +| Path | Purpose | +|---|---| +| `run.js` | Pure-Node runner. Re-implements catalog regexes line-by-line and diffs vs `expected/findings.txt`. | +| `fixtures/express4-app/server.js` | Deliberate Express 4 mini-app exercising 8 of the 12 catalog patterns (BC-001, BC-002, BC-006, BC-007, BC-101, BC-102, BC-201, BC-202). Also used by Layer 1 case `fm-c1-explicit-migration`. | +| `fixtures/express4-app/package.json` | Pins `express ^4.18.2` so the fixture is unambiguously v4. | +| `expected/findings.txt` | Ground truth — `BC-IDfileline` rows the runner must reproduce exactly. | + +### Why it works this way + +- **The catalog is the contract.** Every detection regex in [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) must have a corresponding entry in `run.js` and (for any pattern exercised by the fixture) a row in `expected/findings.txt`. +- **The fixture is intentionally broken.** Don't "fix" the v4 patterns — the file's whole purpose is to fail v5 detection so the eval has something to assert on. +- **Annotations use `EXPECT-NNN` form**, not the literal pattern, so comments don't trigger false positives in the regex pass. + +### Extending — add a new BC-NNN pattern + +1. Add the pattern to [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) with: ID, classification, source citation, detect regex, fix. +2. Add `['BC-NNN', /your-regex/]` to the `PATTERNS` array in `run.js`. +3. Add a triggering example to `fixtures/express4-app/server.js` (annotated `// EXPECT-NNN ...`). +4. Add the expected `BC-NNNserver.js` row to `expected/findings.txt`. +5. Run `node .apm/skills/framework-modernizer/evals/run.js` and adjust line numbers if needed. +6. (If the new pattern materially changes skill behavior on the fixture) extend `evals.json` case `fm-c1-explicit-migration` so Layer 1 covers it too. + +--- + +## Forking the pattern (Next 13→14, React 17→18, etc.) + +Same two-layer structure, swap the catalog and fixture. See [`../references/DESIGN.md`](../references/DESIGN.md) for the Genesis handoff packet, and ask Genesis to scaffold the evals for your new skill — it will produce `evals.json` + `triggers.json` per the spec. diff --git a/.agents/skills/framework-modernizer/evals/evals.json b/.agents/skills/framework-modernizer/evals/evals.json new file mode 100644 index 0000000..1b3ee58 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/evals.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://agentskills.io/schemas/evals.json", + "_comment": "Content evals for framework-modernizer per agentskills.io spec. Each case runs TWICE: with the skill loaded (with_skill) and without (without_skill) to make the value delta measurable. Assertions are added AFTER the first iteration (you don't know what 'good' looks like until you see actual output). Grading is LLM-judge for holistic checks + script for mechanical checks. Workspace: framework-modernizer-workspace/iteration-N//{with_skill,without_skill}/{outputs,timing.json,grading.json}", + "skill": "framework-modernizer", + "cases": [ + { + "id": "fm-c1-explicit-migration", + "prompt": "Migrate this Express 4 app to Express 5. Audit for breaking changes, apply safe fixes in place, and write a migration plan I can hand to my team.", + "files": ["fixtures/express4-app/"], + "expected_output": "Discovers ≥8 known patterns from references/express-4-to-5-breaking-changes.md (BC-001, BC-002, BC-006, BC-007, BC-101, BC-102, BC-201, BC-202). Applies AUTOFIX edits in place for the 4 AUTOFIX patterns. Writes MIGRATION-PLAN.md at fixture root with three phases. Every MANUAL item cites the official Express 5 migration guide URL. Console summary reports counts per class.", + "assertions": [ + {"type": "file_exists", "path": "MIGRATION-PLAN.md", "kind": "script"}, + {"type": "contains_all_of", "field": "outputs/MIGRATION-PLAN.md", "values": ["Phase 1", "Phase 2", "Phase 3", "expressjs.com/en/guide/migrating-5"], "kind": "script"}, + {"type": "no_invented_bc_ids", "valid_ids_source": "references/express-4-to-5-breaking-changes.md", "kind": "script"}, + {"type": "llm_judge", "rubric": "Are all autofix edits semantically correct (e.g. app.del → app.delete, res.sendfile → res.sendFile)? PASS only if all 4 AUTOFIX patterns are correctly transformed without breaking surrounding code."} + ] + }, + { + "id": "fm-c2-implicit-migration", + "prompt": "We're stuck on Express 4 and the deprecation warnings keep piling up. What would it take to move to v5?", + "files": ["fixtures/express4-app/"], + "expected_output": "Skill activates despite indirect phrasing (no 'migrate' verb). Produces a structured plan grounded in the catalog. Does NOT fabricate breaking-change IDs from training data. The without_skill baseline is expected to produce vague advice or a hallucinated breaking-change list — that delta is the evidence the skill is adding value.", + "assertions": [ + {"type": "skill_activated", "kind": "script"}, + {"type": "no_invented_bc_ids", "valid_ids_source": "references/express-4-to-5-breaking-changes.md", "kind": "script"}, + {"type": "llm_judge", "rubric": "Compare with_skill vs without_skill output blind. Which one would a senior engineer trust more for an actual migration? PASS if with_skill is judged more grounded, more specific, and less likely to mislead."} + ] + }, + { + "id": "fm-c3-near-miss-decline", + "prompt": "Audit our Fastify codebase for upgrade issues from v4 to v5.", + "files": ["fixtures/express4-app/"], + "expected_output": "Skill correctly declines (per the 'Do NOT use this skill when' clause: 'The repo is not Express'). Surfaces that this skill is Express-only and suggests the user check for a Fastify-specific migration tool. Does NOT attempt to apply Express patterns to Fastify code.", + "assertions": [ + {"type": "skill_declines", "kind": "script"}, + {"type": "no_file_writes", "files": ["MIGRATION-PLAN.md"], "kind": "script"}, + {"type": "llm_judge", "rubric": "Did the skill cleanly hand off without contaminating the user's Fastify context with Express-specific advice? PASS if the decline is explicit and useful."} + ] + } + ], + "iteration_workflow": { + "_comment": "Per agentskills.io: run each case twice (with_skill, without_skill) in subagent-isolated contexts. Capture outputs/, timing.json, grading.json per run. Aggregate to benchmark.json. Add assertions only AFTER the first iteration when you've seen what real output looks like. Iterate the skill body, NOT the assertions, when assertions fail.", + "workspace": "framework-modernizer-workspace/iteration-{N}/{case-id}/{with_skill|without_skill}/", + "outputs_per_run": ["outputs/", "timing.json", "grading.json"], + "aggregate": "framework-modernizer-workspace/iteration-{N}/benchmark.json", + "ship_gate": "All 3 cases PASS with_skill AND show measurable delta vs without_skill. If with_skill ≈ without_skill, the skill is not adding value — redesign or delete." + } +} diff --git a/.agents/skills/framework-modernizer/evals/expected/findings.txt b/.agents/skills/framework-modernizer/evals/expected/findings.txt new file mode 100644 index 0000000..39e6689 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/expected/findings.txt @@ -0,0 +1,10 @@ +# Expected findings on the fixture +# Format: BC-IDfileline +BC-201 server.js 8 +BC-202 server.js 11 +BC-001 server.js 14 +BC-006 server.js 20 +BC-007 server.js 25 +BC-002 server.js 30 +BC-101 server.js 34 +BC-102 server.js 39 diff --git a/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json new file mode 100644 index 0000000..f861082 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json @@ -0,0 +1,10 @@ +{ + "name": "express4-app-fixture", + "version": "0.0.1", + "private": true, + "description": "Deliberate Express 4 fixture with 8 known breaking patterns (3 SAFE, 3 AUTOFIX, 2 MANUAL). Used by framework-modernizer eval runner.", + "main": "server.js", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js new file mode 100644 index 0000000..59a80e6 --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js @@ -0,0 +1,43 @@ +// Deliberate Express 4 fixture for the framework-modernizer eval suite. +// Comment annotations use the EXPECT-* form to avoid accidental regex hits. + +const express = require('express'); +const app = express(); + +// EXPECT-201 SAFE: urlencoded extended default flipped in v5 +app.use(express.urlencoded()); + +// EXPECT-202 SAFE: static dotfiles default flipped in v5 +app.use(express.static('public')); + +// EXPECT-001 AUTOFIX: app.del removed +app.del('/user/:id', (req, res) => { + res.send(`DELETE /user/${req.params.id}`); +}); + +// EXPECT-006 AUTOFIX: magic redirect string removed +app.get('/back', (req, res) => { + res.redirect('back'); +}); + +// EXPECT-007 AUTOFIX: lowercase method renamed +app.get('/file', (req, res) => { + res.sendfile(__dirname + '/public/index.html'); +}); + +// EXPECT-002 AUTOFIX: numeric-only send removed +app.get('/notfound', (req, res) => { + res.send(404); +}); + +// EXPECT-101 MANUAL: unnamed wildcard +app.get('/*', (req, res) => { + res.send('catch-all'); +}); + +// EXPECT-102 MANUAL: optional segment +app.get('/file/:name.:ext?', (req, res) => { + res.send(`name=${req.params.name} ext=${req.params.ext}`); +}); + +module.exports = app; diff --git a/.agents/skills/framework-modernizer/evals/run.js b/.agents/skills/framework-modernizer/evals/run.js new file mode 100644 index 0000000..64f749b --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/run.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node +/* eslint-disable */ +/** + * Eval runner for framework-modernizer. + * + * Validates the catalog regexes against the deliberate fixture by: + * 1. Running each BC-NNN regex from the catalog over every JS file in the fixture + * 2. Emitting actual findings as: BC-IDfileline + * 3. Diffing against evals/expected/findings.txt + * + * Exit 0 on match, 1 on mismatch. CI-friendly. Pure Node, no deps. + */ + +const fs = require('node:fs'); +const path = require('node:path'); + +const SKILL_DIR = path.resolve(__dirname, '..'); +const FIXTURE_DIR = path.join(SKILL_DIR, 'evals/fixtures/express4-app'); +const EXPECTED_FILE = path.join(SKILL_DIR, 'evals/expected/findings.txt'); + +// Catalog patterns. Must stay in sync with +// references/express-4-to-5-breaking-changes.md. +// Detection regexes are line-by-line (the skill scans similarly). +const PATTERNS = [ + ['BC-001', /\bapp\.del\s*\(/], + ['BC-002', /\bres\.send\s*\(\s*\d{3}\s*\)/], + ['BC-006', /\bres\.redirect\s*\(\s*['"]back['"]\s*\)/], + ['BC-007', /\bres\.sendfile\s*\(/], + ['BC-101', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]/], + ['BC-102', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]/], + ['BC-201', /express\.urlencoded\s*\(\s*\)/], + ['BC-202', /express\.static\s*\(/], +]; + +const SOURCE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts']); + +function walk(dir) { + const out = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules') continue; + const full = path.join(dir, entry.name); + if (entry.isDirectory()) out.push(...walk(full)); + else if (SOURCE_EXTS.has(path.extname(entry.name))) out.push(full); + } + return out; +} + +function scan() { + const findings = []; + for (const file of walk(FIXTURE_DIR)) { + const rel = path.relative(FIXTURE_DIR, file); + const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + for (const [id, regex] of PATTERNS) { + if (regex.test(line)) findings.push({ id, file: rel, line: i + 1 }); + } + } + } + return findings; +} + +function serialize(findings) { + return findings + .slice() + .sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.id.localeCompare(b.id)) + .map((f) => `${f.id}\t${f.file}\t${f.line}`) + .join('\n'); +} + +function loadExpected() { + return fs + .readFileSync(EXPECTED_FILE, 'utf8') + .split(/\r?\n/) + .filter((l) => l && !l.startsWith('#')) + .sort((a, b) => { + const [, fa, la] = a.split('\t'); + const [, fb, lb] = b.split('\t'); + return fa.localeCompare(fb) || Number(la) - Number(lb); + }) + .join('\n'); +} + +const actual = serialize(scan()); +const expected = loadExpected(); + +if (actual === expected) { + const count = actual.split('\n').filter(Boolean).length; + console.log(`✅ framework-modernizer catalog regression PASSED (${count} findings match expected)`); + process.exit(0); +} + +console.log('❌ framework-modernizer catalog regression FAILED'); +console.log('--- expected ---'); +console.log(expected); +console.log('--- actual ---'); +console.log(actual); +process.exit(1); diff --git a/.agents/skills/framework-modernizer/evals/triggers.json b/.agents/skills/framework-modernizer/evals/triggers.json new file mode 100644 index 0000000..3e6c57a --- /dev/null +++ b/.agents/skills/framework-modernizer/evals/triggers.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://agentskills.io/schemas/triggers.json", + "_comment": "Trigger evals for the framework-modernizer dispatch description per agentskills.io optimizing-descriptions spec. ~20 queries split 60/40 train/val. Validation split is the ship gate: ≥0.5 trigger rate on should-trigger AND <0.5 trigger rate on near-miss should-NOT-trigger. Run via the harness's dispatcher (NOT the skill body) — the question is whether the description matches.", + "skill": "framework-modernizer", + "split": {"train_pct": 60, "val_pct": 40}, + "should_trigger": [ + {"split": "train", "query": "Bump express to v5"}, + {"split": "train", "query": "Upgrade express to 5"}, + {"split": "train", "query": "Migrate to express 5"}, + {"split": "train", "query": "Express deprecation warnings — fix them"}, + {"split": "train", "query": "We're stuck on express 4"}, + {"split": "train", "query": "Our app uses app.del — is that still ok in modern express?"}, + {"split": "val", "query": "Path-to-regexp is showing warnings about :id? — what changed?"}, + {"split": "val", "query": "Major dependabot PR open: express ^4 → ^5. Is it safe to merge?"}, + {"split": "val", "query": "What breaks if we go from express 4 to 5?"}, + {"split": "val", "query": "Need a migration plan for express 5 upgrade"} + ], + "should_not_trigger": [ + {"split": "train", "query": "Bump express to v4.18.3", "reason": "patch within 4.x — not a major migration"}, + {"split": "train", "query": "Migrate from Fastify to Hapi", "reason": "different framework"}, + {"split": "train", "query": "Upgrade Next.js to 15", "reason": "different framework"}, + {"split": "train", "query": "Add express middleware for auth", "reason": "regular dev work, not migration"}, + {"split": "train", "query": "Why is express slow under load?", "reason": "performance, not migration"}, + {"split": "train", "query": "How do I install express?", "reason": "greenfield setup"}, + {"split": "val", "query": "Configure CORS in express", "reason": "regular dev work"}, + {"split": "val", "query": "Express vs Fastify — which to choose for a new project?", "reason": "advisory, not migration"}, + {"split": "val", "query": "Run npm audit on our express app", "reason": "security audit — different skill"}, + {"split": "val", "query": "Express app has a memory leak — help debug", "reason": "debugging, not migration"} + ], + "ship_gate": { + "_comment": "Validation split is the gate. Train split is for description tuning iterations.", + "should_trigger_rate_min": 0.5, + "should_not_trigger_rate_max": 0.5, + "evaluated_on": "val" + } +} diff --git a/.agents/skills/framework-modernizer/references/DESIGN.md b/.agents/skills/framework-modernizer/references/DESIGN.md new file mode 100644 index 0000000..887fab6 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/DESIGN.md @@ -0,0 +1,148 @@ +# Genesis design handoff packet — framework-modernizer + +> Output of the [Genesis](https://github.com/DevExpGbb/genesis) 8-step design discipline. Persisted here so reviewers (and trainees adapting this pattern to other framework migrations) can reproduce the reasoning. + +## Step 1 — intent + scope + +**Capability:** Audit a Node.js codebase for Express 4 → 5 breaking changes, classify each finding, apply safe autofixes, and emit a phased migration plan grounded in the official Express 5 migration guide. + +**Single Responsibility check:** "audit AND fix AND plan" is one capability — *prepare a codebase for a major-version upgrade*. Splitting would cost more than it saves (audit alone is useless without a plan; autofix alone is dangerous without classification). PASS. + +**Boundary (what it does NOT do):** +- Does not bump `express` in `package.json` (deliberate human gate — bumping invalidates the lockfile). +- Does not run `npm test` (the team's CI is the oracle). +- Does not handle other frameworks (one framework pair per skill — see "Forking this pattern" below). + +**Dispatch description (frontmatter `description`):** Imperative ("Use this skill when…"), names indirect triggers ("bump express to v5", "express deprecation warnings", "stuck on express 4"), declares boundary ("does NOT run consumer's tests"). Mode: BOTH (forced when explicitly invoked, discovery when express ^4.x detected in package.json being discussed). + +## Step 2 — component diagram + +```mermaid +flowchart TD + USER[User request] --> SKILL[framework-modernizer SKILL] + SKILL --> CATALOG[references/
express-4-to-5-breaking-changes.md
ASSET] + SKILL --> RUBRIC[references/
classifier-rubric.md
ASSET] + SKILL --> PLAN_TPL[references/
phased-plan-template.md
ASSET] + SKILL --> PROSE[code-kit/
prose-style.md
RULE - existing] + SKILL --> EVAL[evals/
fixtures + runner
contributor-only] + + classDef new fill:#dfd + classDef existing fill:#ddf + classDef contrib fill:#fed + class SKILL,CATALOG,RUBRIC,PLAN_TPL new + class PROSE existing + class EVAL contrib +``` + +## Step 3 — sequence diagram + +```mermaid +sequenceDiagram + participant U as User + participant S as framework-modernizer + participant FS as filesystem + participant E as Edit tool + + U->>S: "Migrate services/api/ to express 5" + S->>FS: Glob package.json + FS-->>S: paths + S->>FS: Read each, filter express ^4.x + FS-->>S: matched repos + loop for each matched repo + S->>FS: Grep -n catalog patterns across **/*.{js,ts} + FS-->>S: findings list + S->>S: Classify each (SAFE/AUTOFIX/MANUAL via rubric) + loop for each AUTOFIX finding + S->>E: Edit file (deterministic replace) + E-->>S: ack + end + loop for each MANUAL finding + S->>E: Edit file (insert TODO comment ABOVE line) + E-->>S: ack + end + S->>FS: Write MIGRATION-PLAN.md + end + S-->>U: Summary: N safe, M autofixed, K manual +``` + +**Pattern selection (genesis tier order):** + +1. **Refactor patterns:** None apply (greenfield skill). +2. **TIER 3 architectural pattern:** **PIPELINE** (genesis A2). Single-pass, deterministic stages, no fan-out. Anti-patterns inherited: don't add a "fix" stage that re-reads what the "scan" stage already saw (state-loss). +3. **TIER 2 design patterns:** **B4 PLAN MEMENTO** (the MIGRATION-PLAN.md is the persisted plan); **B8 ATTENTION ANCHOR** (catalog file is THE source of truth — every finding must cite a BC-NNN). +4. **TIER 1 idioms:** Loaded only at codegen — not relevant in design. + +**Why not PANEL?** No independent lenses. Classification is mechanical (rubric is deterministic), not a judgment call. PANEL would be over-engineering. + +## Step 3.5 — composition decision + +| Box | Mode | Rationale | +|---|---|---| +| catalog (`express-4-to-5-breaking-changes.md`) | INLINE asset | Skill-specific. No other skill reuses Express 5 patterns. | +| rubric (`classifier-rubric.md`) | INLINE asset | The 3-class taxonomy is specific to migration skills; could be EXTERNAL if a 2nd migration skill ships, but rule-of-three not yet met. | +| plan template (`phased-plan-template.md`) | INLINE asset | Skill-specific output format. | +| prose-style | EXTERNAL (already pinned via `code-kit`) | Cross-cutting style rules; shared across all repo skills. | +| evals fixture + runner | LOCAL SIBLING but **OUTSIDE** distribution boundary | Eval scenarios are maintainer-scope. Trainees should NOT load them at runtime. Lives under `.apm/skills/framework-modernizer/evals/` — `apm pack` excludes by convention. | + +No external modules required → no module-system adapter needed. + +## Step 4 — SoC pass + +| Existing module | Overlap? | +|---|---| +| `code-kit` (style + lint instructions) | No — this skill is task-specific, not style. | +| `review-kit` (PR review) | No — review-kit reviews diffs after the fact; this skill prepares the diff. | +| `secure-baseline` (secret hooks) | No — orthogonal. | + +**Verdict:** Net new capability. No SoC violation. + +## Step 5 — PROSE compliance check + +PROSE = **P**rogressive Disclosure / **R**educed Scope / **O**rchestrated Composition / **S**afety Boundaries / **E**xplicit Hierarchy ([handbook ch.13](https://danielmeppiel.github.io/agentic-sdlc-handbook/handbook/ch13-the-prose-specification.html#the-constraint-model)). One row per constraint: + +| PROSE constraint | How this skill complies | +|---|---| +| **P**rogressive Disclosure | `SKILL.md` is ~80 lines (when-to-use + 5 steps). Catalog, rubric and plan template live under `references/` and are only loaded when the skill is invoked — not at every chat turn. | +| **R**educed Scope | Single capability: "produce a triaged migration plan for one named framework upgrade". Doesn't refactor, doesn't open PRs, doesn't bump anything else. Anything outside that is a separate skill. | +| **O**rchestrated Composition | PIPELINE shape: `discover repo footprint → scan catalog → classify per BC-NNN → emit plan`. Each step is a deterministic call (Grep / Read / templated Edit) wrapped by the LLM. Composes cleanly with `code-kit` (style on the plan output) and `review-kit` (review of the resulting PR). | +| **S**afety Boundaries | `allowed-tools: Read, Grep, Glob, Edit(plan.md)` only — cannot touch source. Catalog is the **only** ground truth for breaking changes; "Constraints" bans inventing BC-NNNs from training data. Eval fixture verifies every finding cites a BC-NNN that exists in the catalog. | +| **E**xplicit Hierarchy | Repo `code-kit` rules > skill-local rubric > skill instructions > prompt. The skill never overrides the repo's house style; it inherits it. | + +**Hallucination countermeasure:** The "Constraints" section bans inventing breaking changes from training data. The catalog is the only source of truth. Eval fixture verifies findings cite a BC-NNN. + +**LLM-physics:** Catalog is ~6.5KB, rubric ~2KB, plan template ~3KB. Total skill loadout ~17KB including SKILL.md. Comfortably under 32KB context-economy budget. + +## Step 6 — handoff packet (this file) + +✅ Component diagram (step 2) +✅ Sequence diagram (step 3) +✅ Pattern named (PIPELINE) + anti-patterns inherited +✅ Composition decisions per box +✅ External modules required: none +✅ Distribution surface: `.apm/skills/framework-modernizer/{SKILL.md, references/*}` ships; `evals/` does not. + +## Step 7 — codegen (separate file, this is `SKILL.md`) + +Done. See `../SKILL.md`. + +## Step 8 — validation + +✅ Diagrams written before SKILL.md body (Rule 1). +✅ No harness-specific syntax in SKILL.md or this design doc (Rule 2). Tools named only generically (`Read`, `Grep`, `Glob`, `Edit`, `Bash`). +✅ Single coherent unit — every section serves the migration capability; no orphan content. +✅ Size budget under 32KB total loadout. +✅ Eval fixture exists and runs (see `../evals/README.md`). + +--- + +## Forking this pattern for other frameworks + +The architecture transfers verbatim. To adapt for, say, **React 17 → 18**: + +1. Replace `express-4-to-5-breaking-changes.md` with `react-17-to-18-breaking-changes.md` — extract from React 18 migration guide. +2. Same rubric (SAFE / AUTOFIX / MANUAL). +3. Same plan template (rename headings). +4. Same PIPELINE pattern, same sequence diagram (only the catalog content changes). +5. New eval fixture with deliberate React 17 patterns. + +The trainee track guide ([`docs/tracks/04-framework-modernizer.md`](../../../../docs/tracks/04-framework-modernizer.md)) walks through this fork explicitly. diff --git a/.agents/skills/framework-modernizer/references/classifier-rubric.md b/.agents/skills/framework-modernizer/references/classifier-rubric.md new file mode 100644 index 0000000..3b83318 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/classifier-rubric.md @@ -0,0 +1,29 @@ +# Classifier rubric + +Every finding from the scan step gets exactly one class. Use this table; do not invent new classes. + +## SAFE +- The pattern compiles and runs in v5 **without code change**. +- Behavior **may differ** (e.g. defaults flipped). Worth surfacing to the team but **do not edit**. +- Examples: `BC-201` (urlencoded extended default), `BC-202` (static dotfiles default). +- **Skill action:** Add to "Phase 3 — Validation checklist" in MIGRATION-PLAN.md. No `Edit` call. + +## AUTOFIX +- The transformation is **textually deterministic** — a single search+replace produces correct v5 code in every reasonable case. +- The transformation is **scope-local** — does not require reading other files or understanding the surrounding control flow. +- Risk of a malformed edit is effectively zero. +- Examples: `BC-001` (`app.del` → `app.delete`), `BC-006` (`'back'` redirect), `BC-007` (`sendfile` → `sendFile`). +- **Skill action:** Apply via `Edit` tool. Log a one-line diff. Add to "Phase 1 — Autofixed" section. + +## MANUAL +- Detection works, but the safe rewrite requires: + - Reading other files (e.g. `req.params[0]` consumers downstream of a wildcard rename), OR + - Multi-token reordering with expression awareness (e.g. `res.send({...obj}, 200)`), OR + - A semantic decision the team owns (e.g. is this `req.param('x')` actually `req.body.x` or `req.query.x`?). +- **Skill action:** Insert a TODO comment **on the line above** the finding. Add to "Phase 2 — Manual edits required" with the file path, line number, and a link to the migration guide section. **Do not edit the offending line itself.** + +## Tie-breakers + +- If a pattern *could* be AUTOFIX but the regex has any chance of matching unrelated code → MANUAL. **Bias to safety.** +- If the official Express team ships a codemod (`@expressjs/...`) for the change but the regex match is ambiguous → MANUAL with TODO that recommends running the codemod. +- Never invent a new class. If a finding fits none of these three, the catalog entry is wrong — fix the catalog. diff --git a/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md b/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md new file mode 100644 index 0000000..d5570d9 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md @@ -0,0 +1,107 @@ +# Express 4 → 5 breaking changes catalog + +> **Source:** Official [Express 5 migration guide](https://expressjs.com/en/guide/migrating-5.html). Every pattern below cites the section heading. Do not extend this catalog with patterns from your training data — fetch the migration guide and add citations. + +This catalog drives the `framework-modernizer` skill. Each entry has: +- **ID** — stable identifier (BC-NNN) for cross-reference +- **Class** — `SAFE` / `AUTOFIX` / `MANUAL` (from [`classifier-rubric.md`](classifier-rubric.md)) +- **Detect** — a `Grep` pattern (PCRE) the skill runs across `**/*.{js,mjs,cjs,ts}` +- **Fix** — for AUTOFIX: the exact `Edit` to apply. For MANUAL: the TODO comment to insert. +- **Source** — anchor in the migration guide + +--- + +## Removed methods (most are AUTOFIX with deterministic codemods) + +### BC-001 — `app.del()` removed +- **Class:** AUTOFIX +- **Detect:** `\bapp\.del\s*\(` +- **Fix:** Replace `app.del(` → `app.delete(`. Same for any `router.del(` → `router.delete(`. +- **Source:** [§ app.del()](https://expressjs.com/en/guide/migrating-5.html#app.del) + +### BC-002 — `res.send(status)` (numeric status as only arg) removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.send\s*\(\s*\d{3}\s*\)` +- **Fix:** Replace `res.send(NNN)` → `res.sendStatus(NNN)`. +- **Source:** [§ res.send(status)](https://expressjs.com/en/guide/migrating-5.html#res.send.status) + +### BC-003 — `res.send(body, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.send\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-003 — express 5 removed res.send(body, status). Rewrite as: res.status(NNN).send(body). See https://expressjs.com/en/guide/migrating-5.html#res.send.body` +- **Why MANUAL:** Detection regex matches but a safe `Edit` requires re-ordering with full token awareness (multiline, expressions). Codemod (`@expressjs/status-send-order`) handles it; we surface the recommendation but don't risk a malformed edit. + +### BC-004 — `res.json(obj, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.json\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-004 — express 5 removed res.json(obj, status). Rewrite as: res.status(NNN).json(obj). See https://expressjs.com/en/guide/migrating-5.html#res.json` + +### BC-005 — `res.redirect(url, status)` arg-order swapped +- **Class:** MANUAL +- **Detect:** `\bres\.redirect\s*\(\s*['"][^'"]+['"]\s*,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-005 — express 5 swapped redirect arg order. Rewrite as: res.redirect(NNN, '/path'). See https://expressjs.com/en/guide/migrating-5.html#res.redirect` + +### BC-006 — `res.redirect('back')` magic string removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.redirect\s*\(\s*['"]back['"]\s*\)` +- **Fix:** Replace `res.redirect('back')` → `res.redirect(req.get('Referrer') || '/')`. Handle both `'back'` and `"back"`. +- **Source:** [§ res.redirect('back')](https://expressjs.com/en/guide/migrating-5.html#magic-redirect) + +### BC-007 — `res.sendfile()` (lowercase) renamed +- **Class:** AUTOFIX +- **Detect:** `\bres\.sendfile\s*\(` +- **Fix:** Replace `res.sendfile(` → `res.sendFile(`. +- **Source:** [§ res.sendfile()](https://expressjs.com/en/guide/migrating-5.html#res.sendfile) + +### BC-008 — `req.param(name)` removed +- **Class:** MANUAL +- **Detect:** `\breq\.param\s*\(` +- **TODO:** `// MANUAL: framework-modernizer BC-008 — express 5 removed req.param(name). Use req.params, req.body, or req.query directly depending on source. See https://expressjs.com/en/guide/migrating-5.html#req.param` + +--- + +## Path-route matching (path-to-regexp 0.x → 8.x) + +### BC-101 — Unnamed wildcard `*` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-101 — express 5 requires named wildcards. Rewrite '*' as '*splat' (or '/{*splat}' to also match root). See https://expressjs.com/en/guide/migrating-5.html#path-syntax` +- **Why MANUAL:** Renaming wildcards requires reading downstream code to understand if `req.params[0]` was used; rename may need to become `req.params.splat`. + +### BC-102 — Optional segment `?` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-102 — express 5 dropped ':param?' optional syntax. Use brace-wrapped form: '/path/{:param}'. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +### BC-103 — Regex char class in route string +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\[[^\]]*\|[^\]]*\][^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-103 — express 5 dropped regex chars '[a|b]' in route strings. Pass an array of paths instead: ['/a/...', '/b/...']. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +--- + +## Behavior changes (mostly SAFE — informational) + +### BC-201 — `express.urlencoded` `extended` default flipped +- **Class:** SAFE +- **Detect:** `express\.urlencoded\s*\(\s*\)` (no-arg form) OR `express\.urlencoded\s*\(\s*\{[^}]*\}\s*\)` (without explicit `extended`) +- **Note:** In v4 `extended` defaulted to `true`; in v5 it defaults to `false`. If your code relies on parsing rich nested objects, set `extended: true` explicitly. **No edit applied** — emit informational line in plan. +- **Source:** [§ express.urlencoded](https://expressjs.com/en/guide/migrating-5.html#express.urlencoded) + +### BC-202 — `express.static` `dotfiles` default flipped to `'ignore'` +- **Class:** SAFE +- **Detect:** `express\.static\s*\(` +- **Note:** v4 served dotfiles by default; v5 ignores them. If serving `.well-known/` etc., set `{ dotfiles: 'allow' }` explicitly. Informational only. +- **Source:** [§ express.static dotfiles](https://expressjs.com/en/guide/migrating-5.html#express.static.dotfiles) + +--- + +## Catalog summary + +| Class | Count | Skill action | +|---|---|---| +| AUTOFIX | 3 (BC-001, BC-006, BC-007) | Apply `Edit` in step 4 | +| MANUAL | 7 (BC-003, BC-004, BC-005, BC-008, BC-101, BC-102, BC-103) | Insert TODO comment + reference link | +| SAFE | 2 (BC-201, BC-202) | Note in MIGRATION-PLAN.md "Phase 3 — Validation checklist" | + +Total: **12 patterns**. The fixture at `evals/fixtures/express4-app/` triggers 6 of these (validated by `evals/expected/findings.txt`). diff --git a/.agents/skills/framework-modernizer/references/phased-plan-template.md b/.agents/skills/framework-modernizer/references/phased-plan-template.md new file mode 100644 index 0000000..8501ce4 --- /dev/null +++ b/.agents/skills/framework-modernizer/references/phased-plan-template.md @@ -0,0 +1,77 @@ +# MIGRATION-PLAN.md template + +The skill writes this file at the consumer repo's root after step 5. Substitute `{...}` placeholders with real values. + +--- + +# Express 4 → 5 Migration Plan + +> Auto-generated by `framework-modernizer` skill on `{ISO date}`. +> Source codebase: `{repo path}` (express `{detected version}`) +> Catalog: [`express-4-to-5-breaking-changes.md`](https://github.com/DevExpGbb/zava-skills-workshop-template/blob/main/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md) + +## Summary + +| Class | Findings | +|---|---| +| SAFE (informational) | `{N}` | +| AUTOFIX (applied by this skill) | `{M}` | +| MANUAL (team must edit) | `{K}` | +| **Total** | `{N+M+K}` | + +--- + +## Phase 1 — Autofixed (this skill) + +The skill applied the following changes. **Review the diff before merging.** + +| Pattern | File | Line | Change | +|---|---|---|---| +| `{BC-NNN}` | `{path}` | `{N}` | `{before → after}` | +| ... | | | | + +If empty, the skill found no AUTOFIX-class patterns. + +--- + +## Phase 2 — Manual edits required + +Each item below has a TODO comment inserted **on the line above** the finding. Address each one before bumping `express` in `package.json`. + +### `{BC-NNN}` — `{title}` + +| File | Line | Recommended action | +|---|---|---| +| `{path}` | `{N}` | `{action}` | +| ... | | | + +**Reference:** [`{section anchor}`]({migration guide URL}) + +(Repeat for each unique BC-NNN.) + +--- + +## Phase 3 — Validation checklist + +Behaviors that work in v5 without code change but **may produce different runtime output**. Confirm each before declaring migration complete. + +- [ ] **`express.urlencoded` `extended` default flipped to `false`** (BC-201). If your handlers parse rich nested form data, set `extended: true` explicitly. Locations: `{file:line, file:line, ...}` +- [ ] **`express.static` `dotfiles` default flipped to `'ignore'`** (BC-202). If serving `.well-known/`, set `{ dotfiles: 'allow' }`. Locations: `{file:line, ...}` +- [ ] **Rejected promises now forwarded to error handler.** Async middleware that previously swallowed errors will now hit your error-handling middleware. Verify your error-handler logs+responds correctly. +- [ ] **`req.body` no longer initialized to `{}` by default.** If you have code like `req.body.foo` without a body parser running, it will throw. Check middleware order. +- [ ] **Run `npm test` and the team's E2E suite.** This skill does not run tests — that is the team's gate. + +--- + +## Recommended next steps (in order) + +1. Review and merge the AUTOFIX edits from Phase 1. +2. Address every Phase 2 TODO. Tackle by BC-NNN class — fix all BC-101s in one PR, all BC-003s in another. +3. Bump `express` in `package.json` from `^4.x` to `^5.0.0`. Run `npm install`. +4. Walk through Phase 3 checklist. +5. Run full test suite + smoke deploy. +6. Delete this `MIGRATION-PLAN.md`. + +--- + +*This plan was generated by [`framework-modernizer`](https://github.com/DevExpGbb/zava-skills-workshop-template/tree/main/.apm/skills/framework-modernizer) — a workshop reference skill built with [Genesis](https://github.com/DevExpGbb/genesis).* diff --git a/.agents/skills/my-skill/SKILL.md b/.agents/skills/my-skill/SKILL.md new file mode 100644 index 0000000..e58910e --- /dev/null +++ b/.agents/skills/my-skill/SKILL.md @@ -0,0 +1,59 @@ +--- +name: my-skill +description: >- + PLACEHOLDER — do not invoke. This folder exists only so `apm install` + has something to resolve during workshop setup. Your real skill belongs + in a NEW folder under .apm/skills//, generated by your + harness from a Genesis design. See docs/tracks/. +license: UNLICENSED +allowed-tools: [] +--- + +# my-skill — placeholder (do NOT edit) + +> 🚫 **Don't fill this file in.** It's a marker, not a skeleton. + +## Why this folder exists + +`apm install` needs a real folder under `.apm/skills/` to bootstrap the +workshop. That's it. The skill **you** ship in this workshop is a *new +folder* you'll create — `.apm/skills/test-improver/`, `.apm/skills/docs-generator/`, +etc. You will not edit this file. + +## The discipline this enforces + +The workshop's hardest lesson: **don't open a text editor and start typing +SKILL.md while squinting at Genesis's prose output**. That's how every +"failed to ship" skill is born. The discipline is: + +1. **Design with Genesis.** In your harness, run `/genesis` with your + chosen [track's prompt](../../../docs/tracks/). Genesis returns an + ASCII architecture diagram + an interface spec. Read it. That's + your contract. +2. **Generate the skill from the design.** Paste Genesis's design back + into chat, then instruct your harness: + + > Generate the <your-skill> skill at `.apm/skills//` + > following the Genesis design above. Use the SKILL.md frontmatter + > conventions from `.github/instructions/prose-style.md` (installed + > by `apm install` from `code-kit`). + + Your harness writes `SKILL.md` (and any references) into the new + folder. **You review the output**, you don't author it line-by-line. +3. **Iterate on the design, not the implementation.** Don't like the + output? Tweak the Genesis prompt (narrow the inputs, add a + constraint), regenerate, re-ask the harness to regenerate the skill. + The implementation is a function of the design — keep the design + sharp and the implementation follows. + +## Why this matters in production + +In a real codebase you will redesign skills as your team's conventions +evolve. If the implementation is hand-typed, every redesign is a +rewrite-from-scratch. If the implementation is *generated from a +persisted design*, every redesign is one Genesis tweak + one regenerate +prompt. **That's the loop you're learning here.** + +See your chosen [track guide](../../../docs/tracks/) for the exact +Genesis prompt + the reference ASCII diagram you should expect to get +back. diff --git a/.apm/skills/framework-modernizer/SKILL.md b/.apm/skills/framework-modernizer/SKILL.md new file mode 100644 index 0000000..a46adcb --- /dev/null +++ b/.apm/skills/framework-modernizer/SKILL.md @@ -0,0 +1,99 @@ +--- +name: framework-modernizer +description: >- + Use this skill when the user asks to migrate, upgrade, or modernize a + Node.js codebase from Express 4 to Express 5 — including phrasings like + "bump express to v5", "upgrade express", "migrate to express 5", + "express deprecation warnings", "we're stuck on express 4", or when + preparing a major-version dependency PR involving express ^4.x. + Also fires when the user mentions removed Express APIs (app.del, res.send(status), + the deprecated body-parser bundling), discontinued path-to-regexp 0.x + patterns (e.g. unnamed wildcards `*` or optional segments `:id?`), or + asks for a migration plan / breaking changes audit on an Express 4 repo. + This skill audits, classifies (SAFE / AUTOFIX / MANUAL), applies safe + autofixes, and emits a phased migration plan grounded in the official + Express 5 migration guide. It does NOT run the consumer's tests; it + emits a plan the team executes. +license: UNLICENSED +allowed-tools: Read, Grep, Glob, Edit +--- + +# framework-modernizer + +> **Reference skill — Express 4 → Express 5 migration.** +> Built with [Genesis](https://github.com/DevExpGbb/genesis) as a worked example for the workshop. Read it, fork the pattern, build your own (Next 13→14, React 17→18, Angular 16→17…). + +## When to use this skill + +- The repo's `package.json` has a top-level `express` dependency on `^4.x` and the user wants to move to `^5.x`. +- The user reports deprecation warnings from Express 4 they want resolved. +- A major-version Dependabot/Renovate PR is open and a human asks "is this safe to merge?" + +**Do NOT use this skill when:** +- The repo is not Express (Fastify, Koa, Hapi → wrong tool). +- The Express version is already `^5.x` (no work). +- The user wants you to also write/run tests post-migration (out of scope — emit the plan, the team validates). + +## What this skill does + +1. **Discover.** `Glob` for `package.json` files. `Read` each, confirm `express` is a direct dependency on `^4.x`. Stop early if not found. +2. **Scan.** For each Express 4 app rooted in a discovered `package.json`: + - `Grep -n` the breaking-change patterns from [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md) across `**/*.{js,mjs,cjs,ts}`. + - Collect each hit with its file path, line number, and matched pattern ID (e.g. `BC-001`). +3. **Classify** every finding via [`references/classifier-rubric.md`](references/classifier-rubric.md): + - **SAFE** — no behavior change in v5; informational only. + - **AUTOFIX** — mechanical replacement; this skill applies it via `Edit`. + - **MANUAL** — semantics changed; emit a TODO comment + reference link, do **not** edit. +4. **Apply autofixes.** For each AUTOFIX finding, perform the exact `Edit` specified in the catalog. Print a one-line diff summary per edit. +5. **Emit migration plan.** Write `MIGRATION-PLAN.md` at the repo root using [`references/phased-plan-template.md`](references/phased-plan-template.md). Include three phases: **Phase 1 — Autofixed (this skill)**, **Phase 2 — Manual edits required**, **Phase 3 — Validation checklist**. Cross-reference every MANUAL item back to the official Express 5 migration guide. + +## Outputs + +| Artifact | Where | When | +|---|---|---| +| Per-file `Edit`s for AUTOFIX class | In-place | Step 4 | +| `MIGRATION-PLAN.md` at repo root | New file | Step 5 | +| Console summary: `N safe, M autofixed, K manual` | stdout | End | + +## Constraints + +- **Source-grounded only.** Every finding must trace to a pattern in [`references/express-4-to-5-breaking-changes.md`](references/express-4-to-5-breaking-changes.md). Do not invent breaking changes from memory — Express 5 is the reference, not your training data. +- **No package.json bump in this skill.** The migration plan instructs the team to bump `express` to `^5.0.0`; the skill does not rewrite it. (Reason: bumping invalidates the lockfile and triggers a npm install side-effect; that's a deliberate human gate.) +- **No test runs.** Emitting the plan is the deliverable. The team's CI is the oracle. +- **Idempotent.** Re-running the skill on an already-migrated repo emits `0 findings` and no edits. + +## Examples + +### Invocation + +> "Migrate `services/api/` from Express 4 to Express 5." + +### Expected end-state + +``` +Discovered: services/api/package.json (express ^4.18.2) +Scanned: 14 files +Findings: + SAFE × 2 (informational) + AUTOFIX × 3 → applied + MANUAL × 4 → see MIGRATION-PLAN.md + +Wrote: services/api/MIGRATION-PLAN.md +``` + +## How this was designed + +This skill went through the full [Genesis](https://github.com/DevExpGbb/genesis) 8-step process. The handoff packet is in [`references/DESIGN.md`](references/DESIGN.md). Reproducing it for your own framework migration (Next 13→14, React 17→18, etc.): + +1. **Step 1 — intent.** Single capability, single framework pair. Don't try to migrate 5 frameworks in one skill. +2. **Step 2 — components.** PIPELINE pattern: scan → classify → autofix → plan. No fan-out, no panel. +3. **Step 5 — Architecture artifacts.** Three files do the heavy lifting: the **catalog** (cited breaking changes) is loaded as context; the **rubric** (SAFE/AUTOFIX/MANUAL classifier) is the decision boundary; the **skill** orchestrates them. The eval runner is the regression harness — change a regex, watch CI. + +## Evals + +Two layers, each catching different failures (see [`evals/README.md`](evals/README.md) for the full recipe): + +1. **Behavior evals** ([`evals/evals.json`](evals/evals.json), [`evals/triggers.json`](evals/triggers.json)) — inference-based, per [agentskills.io spec](https://agentskills.io/skill-creation/evaluating-skills). Each case runs twice (with_skill / without_skill); the value delta is the evidence. Manual / harness-driven; not in CI. +2. **Catalog regression test** ([`evals/run.js`](evals/run.js)) — pure-Node script that locks the catalog regexes against a deliberate fixture. CI-friendly; exits non-zero on drift. + +Both layers were scaffolded via the [Genesis](https://github.com/DevExpGbb/genesis) skill. diff --git a/.apm/skills/framework-modernizer/evals/README.md b/.apm/skills/framework-modernizer/evals/README.md new file mode 100644 index 0000000..49fe904 --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/README.md @@ -0,0 +1,75 @@ +# Evals — framework-modernizer + +This directory holds **two distinct test layers**. Both are valuable; they catch different failures. + +| Layer | What it tests | Output | Runs in CI? | +|---|---|---|---| +| **Behavior evals** ([`evals.json`](./evals.json), [`triggers.json`](./triggers.json)) | The skill's actual LLM behavior. Each case runs twice: with the skill loaded and without it, so the value delta is measurable. | `-workspace/iteration-N//{with_skill,without_skill}/{outputs,timing.json,grading.json}` | No — manual / harness-driven. See "How to run" below. | +| **Catalog regression test** ([`run.js`](./run.js)) | The deterministic substrate the skill depends on. Locks the catalog regexes against the fixture so the team can't silently break detection by editing a regex. | Pure-Node script, exits `0` on green, `1` on drift. | Yes — fast, hermetic, no LLM. | + +The behavior evals are what [agentskills.io calls "evals"](https://agentskills.io/skill-creation/evaluating-skills). The catalog test is a unit test for the catalog file. Both were scaffolded via the [Genesis](https://github.com/DevExpGbb/genesis) skill — Genesis's step-6 EVALS PLAN produces `evals.json` + `triggers.json`; the catalog regression script is independent contributor tooling. + +--- + +## Layer 1 — Behavior evals (the real evals) + +`evals.json` defines 3 content evals + the iteration workflow. `triggers.json` defines ~20 trigger queries (60/40 train/val) for the dispatch description. + +### How to run + +The agentskills.io spec describes the structure but does NOT prescribe a runner — execution is harness-driven. The current canonical recipe: + +1. **Per case in `evals.json`**, spawn TWO subagent runs (or two clean sessions if your harness has no subagent isolation): + - **with_skill**: skill is loaded into the subagent's context. + - **without_skill**: same prompt, no skill — establishes the baseline. +2. Capture each run's outputs into `framework-modernizer-workspace/iteration-N//{with_skill|without_skill}/outputs/`. Capture `timing.json` (`total_tokens`, `duration_ms` — your harness should provide these) alongside. +3. Grade per the case's `assertions[]`. Script-graded assertions (`file_exists`, `contains_all_of`, `no_invented_bc_ids`, `skill_activated`, `skill_declines`, `no_file_writes`) are mechanical. `llm_judge` assertions get a fresh LLM call with the rubric. Write `grading.json`. +4. Aggregate to `framework-modernizer-workspace/iteration-N/benchmark.json`. +5. **Ship gate**: all 3 cases PASS with_skill AND show measurable delta vs without_skill. If `with_skill ≈ without_skill`, the skill is not adding value — redesign or delete. + +For trigger evals: run via the harness's dispatcher (NOT the skill body). The question is whether the dispatch description matches the query. Validation split is the gate (≥0.5 on should-trigger, <0.5 on near-miss should-not-trigger). + +### Iteration discipline (per agentskills.io) + +- **Add assertions AFTER the first run.** You don't know what "good" looks like until you've seen actual output. Iteration 1 is exploratory; iteration 2 hardens with assertions. +- **Iterate the skill body, not the assertions, when assertions fail.** If you're tweaking assertions to pass, you've inverted the test. + +--- + +## Layer 2 — Catalog regression test + +```bash +node .apm/skills/framework-modernizer/evals/run.js +``` + +Exit `0` = all expected findings matched; exit `1` = drift (catalog regex changed, fixture changed, or expected file out of date). CI-friendly. + +### What's here (Layer 2 files) + +| Path | Purpose | +|---|---| +| `run.js` | Pure-Node runner. Re-implements catalog regexes line-by-line and diffs vs `expected/findings.txt`. | +| `fixtures/express4-app/server.js` | Deliberate Express 4 mini-app exercising 8 of the 12 catalog patterns (BC-001, BC-002, BC-006, BC-007, BC-101, BC-102, BC-201, BC-202). Also used by Layer 1 case `fm-c1-explicit-migration`. | +| `fixtures/express4-app/package.json` | Pins `express ^4.18.2` so the fixture is unambiguously v4. | +| `expected/findings.txt` | Ground truth — `BC-IDfileline` rows the runner must reproduce exactly. | + +### Why it works this way + +- **The catalog is the contract.** Every detection regex in [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) must have a corresponding entry in `run.js` and (for any pattern exercised by the fixture) a row in `expected/findings.txt`. +- **The fixture is intentionally broken.** Don't "fix" the v4 patterns — the file's whole purpose is to fail v5 detection so the eval has something to assert on. +- **Annotations use `EXPECT-NNN` form**, not the literal pattern, so comments don't trigger false positives in the regex pass. + +### Extending — add a new BC-NNN pattern + +1. Add the pattern to [`../references/express-4-to-5-breaking-changes.md`](../references/express-4-to-5-breaking-changes.md) with: ID, classification, source citation, detect regex, fix. +2. Add `['BC-NNN', /your-regex/]` to the `PATTERNS` array in `run.js`. +3. Add a triggering example to `fixtures/express4-app/server.js` (annotated `// EXPECT-NNN ...`). +4. Add the expected `BC-NNNserver.js` row to `expected/findings.txt`. +5. Run `node .apm/skills/framework-modernizer/evals/run.js` and adjust line numbers if needed. +6. (If the new pattern materially changes skill behavior on the fixture) extend `evals.json` case `fm-c1-explicit-migration` so Layer 1 covers it too. + +--- + +## Forking the pattern (Next 13→14, React 17→18, etc.) + +Same two-layer structure, swap the catalog and fixture. See [`../references/DESIGN.md`](../references/DESIGN.md) for the Genesis handoff packet, and ask Genesis to scaffold the evals for your new skill — it will produce `evals.json` + `triggers.json` per the spec. diff --git a/.apm/skills/framework-modernizer/evals/evals.json b/.apm/skills/framework-modernizer/evals/evals.json new file mode 100644 index 0000000..1b3ee58 --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/evals.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://agentskills.io/schemas/evals.json", + "_comment": "Content evals for framework-modernizer per agentskills.io spec. Each case runs TWICE: with the skill loaded (with_skill) and without (without_skill) to make the value delta measurable. Assertions are added AFTER the first iteration (you don't know what 'good' looks like until you see actual output). Grading is LLM-judge for holistic checks + script for mechanical checks. Workspace: framework-modernizer-workspace/iteration-N//{with_skill,without_skill}/{outputs,timing.json,grading.json}", + "skill": "framework-modernizer", + "cases": [ + { + "id": "fm-c1-explicit-migration", + "prompt": "Migrate this Express 4 app to Express 5. Audit for breaking changes, apply safe fixes in place, and write a migration plan I can hand to my team.", + "files": ["fixtures/express4-app/"], + "expected_output": "Discovers ≥8 known patterns from references/express-4-to-5-breaking-changes.md (BC-001, BC-002, BC-006, BC-007, BC-101, BC-102, BC-201, BC-202). Applies AUTOFIX edits in place for the 4 AUTOFIX patterns. Writes MIGRATION-PLAN.md at fixture root with three phases. Every MANUAL item cites the official Express 5 migration guide URL. Console summary reports counts per class.", + "assertions": [ + {"type": "file_exists", "path": "MIGRATION-PLAN.md", "kind": "script"}, + {"type": "contains_all_of", "field": "outputs/MIGRATION-PLAN.md", "values": ["Phase 1", "Phase 2", "Phase 3", "expressjs.com/en/guide/migrating-5"], "kind": "script"}, + {"type": "no_invented_bc_ids", "valid_ids_source": "references/express-4-to-5-breaking-changes.md", "kind": "script"}, + {"type": "llm_judge", "rubric": "Are all autofix edits semantically correct (e.g. app.del → app.delete, res.sendfile → res.sendFile)? PASS only if all 4 AUTOFIX patterns are correctly transformed without breaking surrounding code."} + ] + }, + { + "id": "fm-c2-implicit-migration", + "prompt": "We're stuck on Express 4 and the deprecation warnings keep piling up. What would it take to move to v5?", + "files": ["fixtures/express4-app/"], + "expected_output": "Skill activates despite indirect phrasing (no 'migrate' verb). Produces a structured plan grounded in the catalog. Does NOT fabricate breaking-change IDs from training data. The without_skill baseline is expected to produce vague advice or a hallucinated breaking-change list — that delta is the evidence the skill is adding value.", + "assertions": [ + {"type": "skill_activated", "kind": "script"}, + {"type": "no_invented_bc_ids", "valid_ids_source": "references/express-4-to-5-breaking-changes.md", "kind": "script"}, + {"type": "llm_judge", "rubric": "Compare with_skill vs without_skill output blind. Which one would a senior engineer trust more for an actual migration? PASS if with_skill is judged more grounded, more specific, and less likely to mislead."} + ] + }, + { + "id": "fm-c3-near-miss-decline", + "prompt": "Audit our Fastify codebase for upgrade issues from v4 to v5.", + "files": ["fixtures/express4-app/"], + "expected_output": "Skill correctly declines (per the 'Do NOT use this skill when' clause: 'The repo is not Express'). Surfaces that this skill is Express-only and suggests the user check for a Fastify-specific migration tool. Does NOT attempt to apply Express patterns to Fastify code.", + "assertions": [ + {"type": "skill_declines", "kind": "script"}, + {"type": "no_file_writes", "files": ["MIGRATION-PLAN.md"], "kind": "script"}, + {"type": "llm_judge", "rubric": "Did the skill cleanly hand off without contaminating the user's Fastify context with Express-specific advice? PASS if the decline is explicit and useful."} + ] + } + ], + "iteration_workflow": { + "_comment": "Per agentskills.io: run each case twice (with_skill, without_skill) in subagent-isolated contexts. Capture outputs/, timing.json, grading.json per run. Aggregate to benchmark.json. Add assertions only AFTER the first iteration when you've seen what real output looks like. Iterate the skill body, NOT the assertions, when assertions fail.", + "workspace": "framework-modernizer-workspace/iteration-{N}/{case-id}/{with_skill|without_skill}/", + "outputs_per_run": ["outputs/", "timing.json", "grading.json"], + "aggregate": "framework-modernizer-workspace/iteration-{N}/benchmark.json", + "ship_gate": "All 3 cases PASS with_skill AND show measurable delta vs without_skill. If with_skill ≈ without_skill, the skill is not adding value — redesign or delete." + } +} diff --git a/.apm/skills/framework-modernizer/evals/expected/findings.txt b/.apm/skills/framework-modernizer/evals/expected/findings.txt new file mode 100644 index 0000000..39e6689 --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/expected/findings.txt @@ -0,0 +1,10 @@ +# Expected findings on the fixture +# Format: BC-IDfileline +BC-201 server.js 8 +BC-202 server.js 11 +BC-001 server.js 14 +BC-006 server.js 20 +BC-007 server.js 25 +BC-002 server.js 30 +BC-101 server.js 34 +BC-102 server.js 39 diff --git a/.apm/skills/framework-modernizer/evals/fixtures/express4-app/package.json b/.apm/skills/framework-modernizer/evals/fixtures/express4-app/package.json new file mode 100644 index 0000000..f861082 --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/fixtures/express4-app/package.json @@ -0,0 +1,10 @@ +{ + "name": "express4-app-fixture", + "version": "0.0.1", + "private": true, + "description": "Deliberate Express 4 fixture with 8 known breaking patterns (3 SAFE, 3 AUTOFIX, 2 MANUAL). Used by framework-modernizer eval runner.", + "main": "server.js", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/.apm/skills/framework-modernizer/evals/fixtures/express4-app/server.js b/.apm/skills/framework-modernizer/evals/fixtures/express4-app/server.js new file mode 100644 index 0000000..59a80e6 --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/fixtures/express4-app/server.js @@ -0,0 +1,43 @@ +// Deliberate Express 4 fixture for the framework-modernizer eval suite. +// Comment annotations use the EXPECT-* form to avoid accidental regex hits. + +const express = require('express'); +const app = express(); + +// EXPECT-201 SAFE: urlencoded extended default flipped in v5 +app.use(express.urlencoded()); + +// EXPECT-202 SAFE: static dotfiles default flipped in v5 +app.use(express.static('public')); + +// EXPECT-001 AUTOFIX: app.del removed +app.del('/user/:id', (req, res) => { + res.send(`DELETE /user/${req.params.id}`); +}); + +// EXPECT-006 AUTOFIX: magic redirect string removed +app.get('/back', (req, res) => { + res.redirect('back'); +}); + +// EXPECT-007 AUTOFIX: lowercase method renamed +app.get('/file', (req, res) => { + res.sendfile(__dirname + '/public/index.html'); +}); + +// EXPECT-002 AUTOFIX: numeric-only send removed +app.get('/notfound', (req, res) => { + res.send(404); +}); + +// EXPECT-101 MANUAL: unnamed wildcard +app.get('/*', (req, res) => { + res.send('catch-all'); +}); + +// EXPECT-102 MANUAL: optional segment +app.get('/file/:name.:ext?', (req, res) => { + res.send(`name=${req.params.name} ext=${req.params.ext}`); +}); + +module.exports = app; diff --git a/.apm/skills/framework-modernizer/evals/run.js b/.apm/skills/framework-modernizer/evals/run.js new file mode 100644 index 0000000..64f749b --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/run.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node +/* eslint-disable */ +/** + * Eval runner for framework-modernizer. + * + * Validates the catalog regexes against the deliberate fixture by: + * 1. Running each BC-NNN regex from the catalog over every JS file in the fixture + * 2. Emitting actual findings as: BC-IDfileline + * 3. Diffing against evals/expected/findings.txt + * + * Exit 0 on match, 1 on mismatch. CI-friendly. Pure Node, no deps. + */ + +const fs = require('node:fs'); +const path = require('node:path'); + +const SKILL_DIR = path.resolve(__dirname, '..'); +const FIXTURE_DIR = path.join(SKILL_DIR, 'evals/fixtures/express4-app'); +const EXPECTED_FILE = path.join(SKILL_DIR, 'evals/expected/findings.txt'); + +// Catalog patterns. Must stay in sync with +// references/express-4-to-5-breaking-changes.md. +// Detection regexes are line-by-line (the skill scans similarly). +const PATTERNS = [ + ['BC-001', /\bapp\.del\s*\(/], + ['BC-002', /\bres\.send\s*\(\s*\d{3}\s*\)/], + ['BC-006', /\bres\.redirect\s*\(\s*['"]back['"]\s*\)/], + ['BC-007', /\bres\.sendfile\s*\(/], + ['BC-101', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]/], + ['BC-102', /\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]/], + ['BC-201', /express\.urlencoded\s*\(\s*\)/], + ['BC-202', /express\.static\s*\(/], +]; + +const SOURCE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts']); + +function walk(dir) { + const out = []; + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.name === 'node_modules') continue; + const full = path.join(dir, entry.name); + if (entry.isDirectory()) out.push(...walk(full)); + else if (SOURCE_EXTS.has(path.extname(entry.name))) out.push(full); + } + return out; +} + +function scan() { + const findings = []; + for (const file of walk(FIXTURE_DIR)) { + const rel = path.relative(FIXTURE_DIR, file); + const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + for (const [id, regex] of PATTERNS) { + if (regex.test(line)) findings.push({ id, file: rel, line: i + 1 }); + } + } + } + return findings; +} + +function serialize(findings) { + return findings + .slice() + .sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.id.localeCompare(b.id)) + .map((f) => `${f.id}\t${f.file}\t${f.line}`) + .join('\n'); +} + +function loadExpected() { + return fs + .readFileSync(EXPECTED_FILE, 'utf8') + .split(/\r?\n/) + .filter((l) => l && !l.startsWith('#')) + .sort((a, b) => { + const [, fa, la] = a.split('\t'); + const [, fb, lb] = b.split('\t'); + return fa.localeCompare(fb) || Number(la) - Number(lb); + }) + .join('\n'); +} + +const actual = serialize(scan()); +const expected = loadExpected(); + +if (actual === expected) { + const count = actual.split('\n').filter(Boolean).length; + console.log(`✅ framework-modernizer catalog regression PASSED (${count} findings match expected)`); + process.exit(0); +} + +console.log('❌ framework-modernizer catalog regression FAILED'); +console.log('--- expected ---'); +console.log(expected); +console.log('--- actual ---'); +console.log(actual); +process.exit(1); diff --git a/.apm/skills/framework-modernizer/evals/triggers.json b/.apm/skills/framework-modernizer/evals/triggers.json new file mode 100644 index 0000000..3e6c57a --- /dev/null +++ b/.apm/skills/framework-modernizer/evals/triggers.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://agentskills.io/schemas/triggers.json", + "_comment": "Trigger evals for the framework-modernizer dispatch description per agentskills.io optimizing-descriptions spec. ~20 queries split 60/40 train/val. Validation split is the ship gate: ≥0.5 trigger rate on should-trigger AND <0.5 trigger rate on near-miss should-NOT-trigger. Run via the harness's dispatcher (NOT the skill body) — the question is whether the description matches.", + "skill": "framework-modernizer", + "split": {"train_pct": 60, "val_pct": 40}, + "should_trigger": [ + {"split": "train", "query": "Bump express to v5"}, + {"split": "train", "query": "Upgrade express to 5"}, + {"split": "train", "query": "Migrate to express 5"}, + {"split": "train", "query": "Express deprecation warnings — fix them"}, + {"split": "train", "query": "We're stuck on express 4"}, + {"split": "train", "query": "Our app uses app.del — is that still ok in modern express?"}, + {"split": "val", "query": "Path-to-regexp is showing warnings about :id? — what changed?"}, + {"split": "val", "query": "Major dependabot PR open: express ^4 → ^5. Is it safe to merge?"}, + {"split": "val", "query": "What breaks if we go from express 4 to 5?"}, + {"split": "val", "query": "Need a migration plan for express 5 upgrade"} + ], + "should_not_trigger": [ + {"split": "train", "query": "Bump express to v4.18.3", "reason": "patch within 4.x — not a major migration"}, + {"split": "train", "query": "Migrate from Fastify to Hapi", "reason": "different framework"}, + {"split": "train", "query": "Upgrade Next.js to 15", "reason": "different framework"}, + {"split": "train", "query": "Add express middleware for auth", "reason": "regular dev work, not migration"}, + {"split": "train", "query": "Why is express slow under load?", "reason": "performance, not migration"}, + {"split": "train", "query": "How do I install express?", "reason": "greenfield setup"}, + {"split": "val", "query": "Configure CORS in express", "reason": "regular dev work"}, + {"split": "val", "query": "Express vs Fastify — which to choose for a new project?", "reason": "advisory, not migration"}, + {"split": "val", "query": "Run npm audit on our express app", "reason": "security audit — different skill"}, + {"split": "val", "query": "Express app has a memory leak — help debug", "reason": "debugging, not migration"} + ], + "ship_gate": { + "_comment": "Validation split is the gate. Train split is for description tuning iterations.", + "should_trigger_rate_min": 0.5, + "should_not_trigger_rate_max": 0.5, + "evaluated_on": "val" + } +} diff --git a/.apm/skills/framework-modernizer/references/DESIGN.md b/.apm/skills/framework-modernizer/references/DESIGN.md new file mode 100644 index 0000000..887fab6 --- /dev/null +++ b/.apm/skills/framework-modernizer/references/DESIGN.md @@ -0,0 +1,148 @@ +# Genesis design handoff packet — framework-modernizer + +> Output of the [Genesis](https://github.com/DevExpGbb/genesis) 8-step design discipline. Persisted here so reviewers (and trainees adapting this pattern to other framework migrations) can reproduce the reasoning. + +## Step 1 — intent + scope + +**Capability:** Audit a Node.js codebase for Express 4 → 5 breaking changes, classify each finding, apply safe autofixes, and emit a phased migration plan grounded in the official Express 5 migration guide. + +**Single Responsibility check:** "audit AND fix AND plan" is one capability — *prepare a codebase for a major-version upgrade*. Splitting would cost more than it saves (audit alone is useless without a plan; autofix alone is dangerous without classification). PASS. + +**Boundary (what it does NOT do):** +- Does not bump `express` in `package.json` (deliberate human gate — bumping invalidates the lockfile). +- Does not run `npm test` (the team's CI is the oracle). +- Does not handle other frameworks (one framework pair per skill — see "Forking this pattern" below). + +**Dispatch description (frontmatter `description`):** Imperative ("Use this skill when…"), names indirect triggers ("bump express to v5", "express deprecation warnings", "stuck on express 4"), declares boundary ("does NOT run consumer's tests"). Mode: BOTH (forced when explicitly invoked, discovery when express ^4.x detected in package.json being discussed). + +## Step 2 — component diagram + +```mermaid +flowchart TD + USER[User request] --> SKILL[framework-modernizer SKILL] + SKILL --> CATALOG[references/
express-4-to-5-breaking-changes.md
ASSET] + SKILL --> RUBRIC[references/
classifier-rubric.md
ASSET] + SKILL --> PLAN_TPL[references/
phased-plan-template.md
ASSET] + SKILL --> PROSE[code-kit/
prose-style.md
RULE - existing] + SKILL --> EVAL[evals/
fixtures + runner
contributor-only] + + classDef new fill:#dfd + classDef existing fill:#ddf + classDef contrib fill:#fed + class SKILL,CATALOG,RUBRIC,PLAN_TPL new + class PROSE existing + class EVAL contrib +``` + +## Step 3 — sequence diagram + +```mermaid +sequenceDiagram + participant U as User + participant S as framework-modernizer + participant FS as filesystem + participant E as Edit tool + + U->>S: "Migrate services/api/ to express 5" + S->>FS: Glob package.json + FS-->>S: paths + S->>FS: Read each, filter express ^4.x + FS-->>S: matched repos + loop for each matched repo + S->>FS: Grep -n catalog patterns across **/*.{js,ts} + FS-->>S: findings list + S->>S: Classify each (SAFE/AUTOFIX/MANUAL via rubric) + loop for each AUTOFIX finding + S->>E: Edit file (deterministic replace) + E-->>S: ack + end + loop for each MANUAL finding + S->>E: Edit file (insert TODO comment ABOVE line) + E-->>S: ack + end + S->>FS: Write MIGRATION-PLAN.md + end + S-->>U: Summary: N safe, M autofixed, K manual +``` + +**Pattern selection (genesis tier order):** + +1. **Refactor patterns:** None apply (greenfield skill). +2. **TIER 3 architectural pattern:** **PIPELINE** (genesis A2). Single-pass, deterministic stages, no fan-out. Anti-patterns inherited: don't add a "fix" stage that re-reads what the "scan" stage already saw (state-loss). +3. **TIER 2 design patterns:** **B4 PLAN MEMENTO** (the MIGRATION-PLAN.md is the persisted plan); **B8 ATTENTION ANCHOR** (catalog file is THE source of truth — every finding must cite a BC-NNN). +4. **TIER 1 idioms:** Loaded only at codegen — not relevant in design. + +**Why not PANEL?** No independent lenses. Classification is mechanical (rubric is deterministic), not a judgment call. PANEL would be over-engineering. + +## Step 3.5 — composition decision + +| Box | Mode | Rationale | +|---|---|---| +| catalog (`express-4-to-5-breaking-changes.md`) | INLINE asset | Skill-specific. No other skill reuses Express 5 patterns. | +| rubric (`classifier-rubric.md`) | INLINE asset | The 3-class taxonomy is specific to migration skills; could be EXTERNAL if a 2nd migration skill ships, but rule-of-three not yet met. | +| plan template (`phased-plan-template.md`) | INLINE asset | Skill-specific output format. | +| prose-style | EXTERNAL (already pinned via `code-kit`) | Cross-cutting style rules; shared across all repo skills. | +| evals fixture + runner | LOCAL SIBLING but **OUTSIDE** distribution boundary | Eval scenarios are maintainer-scope. Trainees should NOT load them at runtime. Lives under `.apm/skills/framework-modernizer/evals/` — `apm pack` excludes by convention. | + +No external modules required → no module-system adapter needed. + +## Step 4 — SoC pass + +| Existing module | Overlap? | +|---|---| +| `code-kit` (style + lint instructions) | No — this skill is task-specific, not style. | +| `review-kit` (PR review) | No — review-kit reviews diffs after the fact; this skill prepares the diff. | +| `secure-baseline` (secret hooks) | No — orthogonal. | + +**Verdict:** Net new capability. No SoC violation. + +## Step 5 — PROSE compliance check + +PROSE = **P**rogressive Disclosure / **R**educed Scope / **O**rchestrated Composition / **S**afety Boundaries / **E**xplicit Hierarchy ([handbook ch.13](https://danielmeppiel.github.io/agentic-sdlc-handbook/handbook/ch13-the-prose-specification.html#the-constraint-model)). One row per constraint: + +| PROSE constraint | How this skill complies | +|---|---| +| **P**rogressive Disclosure | `SKILL.md` is ~80 lines (when-to-use + 5 steps). Catalog, rubric and plan template live under `references/` and are only loaded when the skill is invoked — not at every chat turn. | +| **R**educed Scope | Single capability: "produce a triaged migration plan for one named framework upgrade". Doesn't refactor, doesn't open PRs, doesn't bump anything else. Anything outside that is a separate skill. | +| **O**rchestrated Composition | PIPELINE shape: `discover repo footprint → scan catalog → classify per BC-NNN → emit plan`. Each step is a deterministic call (Grep / Read / templated Edit) wrapped by the LLM. Composes cleanly with `code-kit` (style on the plan output) and `review-kit` (review of the resulting PR). | +| **S**afety Boundaries | `allowed-tools: Read, Grep, Glob, Edit(plan.md)` only — cannot touch source. Catalog is the **only** ground truth for breaking changes; "Constraints" bans inventing BC-NNNs from training data. Eval fixture verifies every finding cites a BC-NNN that exists in the catalog. | +| **E**xplicit Hierarchy | Repo `code-kit` rules > skill-local rubric > skill instructions > prompt. The skill never overrides the repo's house style; it inherits it. | + +**Hallucination countermeasure:** The "Constraints" section bans inventing breaking changes from training data. The catalog is the only source of truth. Eval fixture verifies findings cite a BC-NNN. + +**LLM-physics:** Catalog is ~6.5KB, rubric ~2KB, plan template ~3KB. Total skill loadout ~17KB including SKILL.md. Comfortably under 32KB context-economy budget. + +## Step 6 — handoff packet (this file) + +✅ Component diagram (step 2) +✅ Sequence diagram (step 3) +✅ Pattern named (PIPELINE) + anti-patterns inherited +✅ Composition decisions per box +✅ External modules required: none +✅ Distribution surface: `.apm/skills/framework-modernizer/{SKILL.md, references/*}` ships; `evals/` does not. + +## Step 7 — codegen (separate file, this is `SKILL.md`) + +Done. See `../SKILL.md`. + +## Step 8 — validation + +✅ Diagrams written before SKILL.md body (Rule 1). +✅ No harness-specific syntax in SKILL.md or this design doc (Rule 2). Tools named only generically (`Read`, `Grep`, `Glob`, `Edit`, `Bash`). +✅ Single coherent unit — every section serves the migration capability; no orphan content. +✅ Size budget under 32KB total loadout. +✅ Eval fixture exists and runs (see `../evals/README.md`). + +--- + +## Forking this pattern for other frameworks + +The architecture transfers verbatim. To adapt for, say, **React 17 → 18**: + +1. Replace `express-4-to-5-breaking-changes.md` with `react-17-to-18-breaking-changes.md` — extract from React 18 migration guide. +2. Same rubric (SAFE / AUTOFIX / MANUAL). +3. Same plan template (rename headings). +4. Same PIPELINE pattern, same sequence diagram (only the catalog content changes). +5. New eval fixture with deliberate React 17 patterns. + +The trainee track guide ([`docs/tracks/04-framework-modernizer.md`](../../../../docs/tracks/04-framework-modernizer.md)) walks through this fork explicitly. diff --git a/.apm/skills/framework-modernizer/references/classifier-rubric.md b/.apm/skills/framework-modernizer/references/classifier-rubric.md new file mode 100644 index 0000000..3b83318 --- /dev/null +++ b/.apm/skills/framework-modernizer/references/classifier-rubric.md @@ -0,0 +1,29 @@ +# Classifier rubric + +Every finding from the scan step gets exactly one class. Use this table; do not invent new classes. + +## SAFE +- The pattern compiles and runs in v5 **without code change**. +- Behavior **may differ** (e.g. defaults flipped). Worth surfacing to the team but **do not edit**. +- Examples: `BC-201` (urlencoded extended default), `BC-202` (static dotfiles default). +- **Skill action:** Add to "Phase 3 — Validation checklist" in MIGRATION-PLAN.md. No `Edit` call. + +## AUTOFIX +- The transformation is **textually deterministic** — a single search+replace produces correct v5 code in every reasonable case. +- The transformation is **scope-local** — does not require reading other files or understanding the surrounding control flow. +- Risk of a malformed edit is effectively zero. +- Examples: `BC-001` (`app.del` → `app.delete`), `BC-006` (`'back'` redirect), `BC-007` (`sendfile` → `sendFile`). +- **Skill action:** Apply via `Edit` tool. Log a one-line diff. Add to "Phase 1 — Autofixed" section. + +## MANUAL +- Detection works, but the safe rewrite requires: + - Reading other files (e.g. `req.params[0]` consumers downstream of a wildcard rename), OR + - Multi-token reordering with expression awareness (e.g. `res.send({...obj}, 200)`), OR + - A semantic decision the team owns (e.g. is this `req.param('x')` actually `req.body.x` or `req.query.x`?). +- **Skill action:** Insert a TODO comment **on the line above** the finding. Add to "Phase 2 — Manual edits required" with the file path, line number, and a link to the migration guide section. **Do not edit the offending line itself.** + +## Tie-breakers + +- If a pattern *could* be AUTOFIX but the regex has any chance of matching unrelated code → MANUAL. **Bias to safety.** +- If the official Express team ships a codemod (`@expressjs/...`) for the change but the regex match is ambiguous → MANUAL with TODO that recommends running the codemod. +- Never invent a new class. If a finding fits none of these three, the catalog entry is wrong — fix the catalog. diff --git a/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md b/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md new file mode 100644 index 0000000..d5570d9 --- /dev/null +++ b/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md @@ -0,0 +1,107 @@ +# Express 4 → 5 breaking changes catalog + +> **Source:** Official [Express 5 migration guide](https://expressjs.com/en/guide/migrating-5.html). Every pattern below cites the section heading. Do not extend this catalog with patterns from your training data — fetch the migration guide and add citations. + +This catalog drives the `framework-modernizer` skill. Each entry has: +- **ID** — stable identifier (BC-NNN) for cross-reference +- **Class** — `SAFE` / `AUTOFIX` / `MANUAL` (from [`classifier-rubric.md`](classifier-rubric.md)) +- **Detect** — a `Grep` pattern (PCRE) the skill runs across `**/*.{js,mjs,cjs,ts}` +- **Fix** — for AUTOFIX: the exact `Edit` to apply. For MANUAL: the TODO comment to insert. +- **Source** — anchor in the migration guide + +--- + +## Removed methods (most are AUTOFIX with deterministic codemods) + +### BC-001 — `app.del()` removed +- **Class:** AUTOFIX +- **Detect:** `\bapp\.del\s*\(` +- **Fix:** Replace `app.del(` → `app.delete(`. Same for any `router.del(` → `router.delete(`. +- **Source:** [§ app.del()](https://expressjs.com/en/guide/migrating-5.html#app.del) + +### BC-002 — `res.send(status)` (numeric status as only arg) removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.send\s*\(\s*\d{3}\s*\)` +- **Fix:** Replace `res.send(NNN)` → `res.sendStatus(NNN)`. +- **Source:** [§ res.send(status)](https://expressjs.com/en/guide/migrating-5.html#res.send.status) + +### BC-003 — `res.send(body, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.send\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-003 — express 5 removed res.send(body, status). Rewrite as: res.status(NNN).send(body). See https://expressjs.com/en/guide/migrating-5.html#res.send.body` +- **Why MANUAL:** Detection regex matches but a safe `Edit` requires re-ordering with full token awareness (multiline, expressions). Codemod (`@expressjs/status-send-order`) handles it; we surface the recommendation but don't risk a malformed edit. + +### BC-004 — `res.json(obj, status)` two-arg signature removed +- **Class:** MANUAL +- **Detect:** `\bres\.json\s*\(\s*[^,)]+,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-004 — express 5 removed res.json(obj, status). Rewrite as: res.status(NNN).json(obj). See https://expressjs.com/en/guide/migrating-5.html#res.json` + +### BC-005 — `res.redirect(url, status)` arg-order swapped +- **Class:** MANUAL +- **Detect:** `\bres\.redirect\s*\(\s*['"][^'"]+['"]\s*,\s*\d{3}\s*\)` +- **TODO:** `// MANUAL: framework-modernizer BC-005 — express 5 swapped redirect arg order. Rewrite as: res.redirect(NNN, '/path'). See https://expressjs.com/en/guide/migrating-5.html#res.redirect` + +### BC-006 — `res.redirect('back')` magic string removed +- **Class:** AUTOFIX +- **Detect:** `\bres\.redirect\s*\(\s*['"]back['"]\s*\)` +- **Fix:** Replace `res.redirect('back')` → `res.redirect(req.get('Referrer') || '/')`. Handle both `'back'` and `"back"`. +- **Source:** [§ res.redirect('back')](https://expressjs.com/en/guide/migrating-5.html#magic-redirect) + +### BC-007 — `res.sendfile()` (lowercase) renamed +- **Class:** AUTOFIX +- **Detect:** `\bres\.sendfile\s*\(` +- **Fix:** Replace `res.sendfile(` → `res.sendFile(`. +- **Source:** [§ res.sendfile()](https://expressjs.com/en/guide/migrating-5.html#res.sendfile) + +### BC-008 — `req.param(name)` removed +- **Class:** MANUAL +- **Detect:** `\breq\.param\s*\(` +- **TODO:** `// MANUAL: framework-modernizer BC-008 — express 5 removed req.param(name). Use req.params, req.body, or req.query directly depending on source. See https://expressjs.com/en/guide/migrating-5.html#req.param` + +--- + +## Path-route matching (path-to-regexp 0.x → 8.x) + +### BC-101 — Unnamed wildcard `*` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\*(?![a-zA-Z_])[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-101 — express 5 requires named wildcards. Rewrite '*' as '*splat' (or '/{*splat}' to also match root). See https://expressjs.com/en/guide/migrating-5.html#path-syntax` +- **Why MANUAL:** Renaming wildcards requires reading downstream code to understand if `req.params[0]` was used; rename may need to become `req.params.splat`. + +### BC-102 — Optional segment `?` no longer supported +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*:[a-zA-Z_]\w*\?[^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-102 — express 5 dropped ':param?' optional syntax. Use brace-wrapped form: '/path/{:param}'. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +### BC-103 — Regex char class in route string +- **Class:** MANUAL +- **Detect:** `\.(?:get|post|put|patch|delete|all|use)\s*\(\s*['"][^'"]*\[[^\]]*\|[^\]]*\][^'"]*['"]` +- **TODO:** `// MANUAL: framework-modernizer BC-103 — express 5 dropped regex chars '[a|b]' in route strings. Pass an array of paths instead: ['/a/...', '/b/...']. See https://expressjs.com/en/guide/migrating-5.html#path-syntax` + +--- + +## Behavior changes (mostly SAFE — informational) + +### BC-201 — `express.urlencoded` `extended` default flipped +- **Class:** SAFE +- **Detect:** `express\.urlencoded\s*\(\s*\)` (no-arg form) OR `express\.urlencoded\s*\(\s*\{[^}]*\}\s*\)` (without explicit `extended`) +- **Note:** In v4 `extended` defaulted to `true`; in v5 it defaults to `false`. If your code relies on parsing rich nested objects, set `extended: true` explicitly. **No edit applied** — emit informational line in plan. +- **Source:** [§ express.urlencoded](https://expressjs.com/en/guide/migrating-5.html#express.urlencoded) + +### BC-202 — `express.static` `dotfiles` default flipped to `'ignore'` +- **Class:** SAFE +- **Detect:** `express\.static\s*\(` +- **Note:** v4 served dotfiles by default; v5 ignores them. If serving `.well-known/` etc., set `{ dotfiles: 'allow' }` explicitly. Informational only. +- **Source:** [§ express.static dotfiles](https://expressjs.com/en/guide/migrating-5.html#express.static.dotfiles) + +--- + +## Catalog summary + +| Class | Count | Skill action | +|---|---|---| +| AUTOFIX | 3 (BC-001, BC-006, BC-007) | Apply `Edit` in step 4 | +| MANUAL | 7 (BC-003, BC-004, BC-005, BC-008, BC-101, BC-102, BC-103) | Insert TODO comment + reference link | +| SAFE | 2 (BC-201, BC-202) | Note in MIGRATION-PLAN.md "Phase 3 — Validation checklist" | + +Total: **12 patterns**. The fixture at `evals/fixtures/express4-app/` triggers 6 of these (validated by `evals/expected/findings.txt`). diff --git a/.apm/skills/framework-modernizer/references/phased-plan-template.md b/.apm/skills/framework-modernizer/references/phased-plan-template.md new file mode 100644 index 0000000..8501ce4 --- /dev/null +++ b/.apm/skills/framework-modernizer/references/phased-plan-template.md @@ -0,0 +1,77 @@ +# MIGRATION-PLAN.md template + +The skill writes this file at the consumer repo's root after step 5. Substitute `{...}` placeholders with real values. + +--- + +# Express 4 → 5 Migration Plan + +> Auto-generated by `framework-modernizer` skill on `{ISO date}`. +> Source codebase: `{repo path}` (express `{detected version}`) +> Catalog: [`express-4-to-5-breaking-changes.md`](https://github.com/DevExpGbb/zava-skills-workshop-template/blob/main/.apm/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md) + +## Summary + +| Class | Findings | +|---|---| +| SAFE (informational) | `{N}` | +| AUTOFIX (applied by this skill) | `{M}` | +| MANUAL (team must edit) | `{K}` | +| **Total** | `{N+M+K}` | + +--- + +## Phase 1 — Autofixed (this skill) + +The skill applied the following changes. **Review the diff before merging.** + +| Pattern | File | Line | Change | +|---|---|---|---| +| `{BC-NNN}` | `{path}` | `{N}` | `{before → after}` | +| ... | | | | + +If empty, the skill found no AUTOFIX-class patterns. + +--- + +## Phase 2 — Manual edits required + +Each item below has a TODO comment inserted **on the line above** the finding. Address each one before bumping `express` in `package.json`. + +### `{BC-NNN}` — `{title}` + +| File | Line | Recommended action | +|---|---|---| +| `{path}` | `{N}` | `{action}` | +| ... | | | + +**Reference:** [`{section anchor}`]({migration guide URL}) + +(Repeat for each unique BC-NNN.) + +--- + +## Phase 3 — Validation checklist + +Behaviors that work in v5 without code change but **may produce different runtime output**. Confirm each before declaring migration complete. + +- [ ] **`express.urlencoded` `extended` default flipped to `false`** (BC-201). If your handlers parse rich nested form data, set `extended: true` explicitly. Locations: `{file:line, file:line, ...}` +- [ ] **`express.static` `dotfiles` default flipped to `'ignore'`** (BC-202). If serving `.well-known/`, set `{ dotfiles: 'allow' }`. Locations: `{file:line, ...}` +- [ ] **Rejected promises now forwarded to error handler.** Async middleware that previously swallowed errors will now hit your error-handling middleware. Verify your error-handler logs+responds correctly. +- [ ] **`req.body` no longer initialized to `{}` by default.** If you have code like `req.body.foo` without a body parser running, it will throw. Check middleware order. +- [ ] **Run `npm test` and the team's E2E suite.** This skill does not run tests — that is the team's gate. + +--- + +## Recommended next steps (in order) + +1. Review and merge the AUTOFIX edits from Phase 1. +2. Address every Phase 2 TODO. Tackle by BC-NNN class — fix all BC-101s in one PR, all BC-003s in another. +3. Bump `express` in `package.json` from `^4.x` to `^5.0.0`. Run `npm install`. +4. Walk through Phase 3 checklist. +5. Run full test suite + smoke deploy. +6. Delete this `MIGRATION-PLAN.md`. + +--- + +*This plan was generated by [`framework-modernizer`](https://github.com/DevExpGbb/zava-skills-workshop-template/tree/main/.apm/skills/framework-modernizer) — a workshop reference skill built with [Genesis](https://github.com/DevExpGbb/genesis).* diff --git a/.apm/skills/my-skill/SKILL.md b/.apm/skills/my-skill/SKILL.md new file mode 100644 index 0000000..e58910e --- /dev/null +++ b/.apm/skills/my-skill/SKILL.md @@ -0,0 +1,59 @@ +--- +name: my-skill +description: >- + PLACEHOLDER — do not invoke. This folder exists only so `apm install` + has something to resolve during workshop setup. Your real skill belongs + in a NEW folder under .apm/skills//, generated by your + harness from a Genesis design. See docs/tracks/. +license: UNLICENSED +allowed-tools: [] +--- + +# my-skill — placeholder (do NOT edit) + +> 🚫 **Don't fill this file in.** It's a marker, not a skeleton. + +## Why this folder exists + +`apm install` needs a real folder under `.apm/skills/` to bootstrap the +workshop. That's it. The skill **you** ship in this workshop is a *new +folder* you'll create — `.apm/skills/test-improver/`, `.apm/skills/docs-generator/`, +etc. You will not edit this file. + +## The discipline this enforces + +The workshop's hardest lesson: **don't open a text editor and start typing +SKILL.md while squinting at Genesis's prose output**. That's how every +"failed to ship" skill is born. The discipline is: + +1. **Design with Genesis.** In your harness, run `/genesis` with your + chosen [track's prompt](../../../docs/tracks/). Genesis returns an + ASCII architecture diagram + an interface spec. Read it. That's + your contract. +2. **Generate the skill from the design.** Paste Genesis's design back + into chat, then instruct your harness: + + > Generate the <your-skill> skill at `.apm/skills//` + > following the Genesis design above. Use the SKILL.md frontmatter + > conventions from `.github/instructions/prose-style.md` (installed + > by `apm install` from `code-kit`). + + Your harness writes `SKILL.md` (and any references) into the new + folder. **You review the output**, you don't author it line-by-line. +3. **Iterate on the design, not the implementation.** Don't like the + output? Tweak the Genesis prompt (narrow the inputs, add a + constraint), regenerate, re-ask the harness to regenerate the skill. + The implementation is a function of the design — keep the design + sharp and the implementation follows. + +## Why this matters in production + +In a real codebase you will redesign skills as your team's conventions +evolve. If the implementation is hand-typed, every redesign is a +rewrite-from-scratch. If the implementation is *generated from a +persisted design*, every redesign is one Genesis tweak + one regenerate +prompt. **That's the loop you're learning here.** + +See your chosen [track guide](../../../docs/tracks/) for the exact +Genesis prompt + the reference ASCII diagram you should expect to get +back. diff --git a/.github/skills/build-and-pr/SKILL.md b/.github/skills/build-and-pr/SKILL.md new file mode 100644 index 0000000..6197d64 --- /dev/null +++ b/.github/skills/build-and-pr/SKILL.md @@ -0,0 +1,183 @@ +--- +name: build-and-pr +description: > + Use this skill when the user provides a work item and wants it implemented as + committed code changes on a new branch with a pull request on the + zava-storefront repository. Trigger when the user says "build this feature", + "implement this", "fix the review comments", "address PR feedback", "create a + PR for this", "open a PR with this change", or pastes a feature brief or set + of review findings and asks for them to be resolved. The skill reads team + security, architecture, and documentation standards before writing any code, + commits the changes to a new branch, runs lint and tests, fixes any failures, + and only opens or updates the PR when all checks are green. Out-of-scope + discoveries are noted in the PR description for a human reviewer. +--- + +# build-and-pr + +Implements a work item -- feature brief or PR review findings -- as commits on a +new branch, then opens or updates a pull request on zava-storefront. Runs +`npm run lint` and `npm test` before opening; fixes failures before proceeding. + +## How to invoke + +Paste the work item text directly into the conversation and ask to "build" or +"implement" it, or to "fix the review comments". Optionally prefix a PR number +if you want to update an existing PR rather than open a new one. + +--- + +## Bundled assets + +- `assets/pr-body-template.md` -- loaded at Stage 5 (PR stage only). + +## Guideline files (loaded at Stage 1) + +The following instruction files apply automatically when working on this repo, +but Stage 1 explicitly re-reads them so their constraints are active in the +planning context: + +- `.github/instructions/secure-coding-base.instructions.md` +- `.github/instructions/ci-cd-golden-paths.instructions.md` +- `.github/instructions/docs-style-guide.instructions.md` + +--- + +## Pipeline + +Work through every stage in order. Reload this plan before starting each stage. +The plan is the truth; recall is not. + +--- + +### Stage 1 -- Read guidelines + +Before writing a single line of code: + +1. Read `.github/instructions/secure-coding-base.instructions.md`. +2. Read `.github/instructions/ci-cd-golden-paths.instructions.md`. +3. Read `.github/instructions/docs-style-guide.instructions.md`. + +Confirm internally that you hold the constraints from all three. They are not +optional; every code change in this pipeline must satisfy them. + +--- + +### Stage 2 -- Plan and scope guard + +1. Decompose the work item into an ordered list of concrete file-change tasks. + Write the list to `plan.md` in the session's plan store. Each task must be: + - A single, completable unit of work. + - Scoped to the work item. If a task looks useful but was not asked for, + it is out of scope -- note it in a separate "Out of scope" section of + `plan.md`; do not build it. + +2. **Scope guard (mandatory):** Read the work item again. For each task in your + list, ask: "Does completing this task directly fulfill part of the stated + work item?" If the answer is no, move it to the out-of-scope list. + +3. Name a branch: `feat/` for new features, `fix/` for bug fixes, + `chore/` for housekeeping. Use lower-kebab-case. 25 chars max. + +4. If the work item references a PR number to update (e.g. "PR #42"), record it + in `plan.md` under `update_pr`. + +--- + +### Stage 3 -- Implement + +Reload `plan.md` before starting. + +1. Create the branch: + ``` + git checkout -b + ``` + +2. Work through each task in `plan.md` in dependency order: + - Read the existing file(s) before editing. + - Apply only the change the task requires; do not refactor unrelated code. + - Obey every constraint from the three guideline files loaded in Stage 1. + Specifically: + - **Security:** no secrets in code; parameterized queries only; + authenticate + authorize at every new handler; default-deny. + - **CI/CD/Architecture:** follow the golden-path patterns; no bespoke + pipeline additions. + - **Docs:** update inline docstrings and relevant markdown only where the + change makes existing docs incorrect; follow the style guide. + - After each logically complete task, commit: + ``` + git add + git commit -m "(): + + Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" + ``` + Use Conventional Commits: `feat`, `fix`, `chore`, `docs`, `test`, `refactor`. + +3. Mark each task done in `plan.md` as you finish it. + +--- + +### Stage 4 -- Verify (A9 Supervised Execution) + +Reload `plan.md` before starting. + +Run checks and interpret results deterministically -- do not assert "it probably +passes". The tool output is the ground truth. + +**Check sequence (repeat up to 3 times):** + +1. Run lint: + ``` + npm run lint + ``` +2. Run tests: + ``` + npm test + ``` +3. Interpret results: + - **All green** -> proceed to Stage 5. + - **Red lint** -> fix only the lines the linter flags; commit the fix; + go to step 1. Count this as one retry. + - **Red tests** -> read the failure message; fix the root cause (not the + symptom); commit the fix; go to step 1. Count this as one retry. + - **3 retries exhausted without green** -> stop fixing. Record the + remaining failures in `plan.md` under `check_failures`. Proceed to + Stage 5; the PR description will surface them for a human. + +Do not skip this stage. Do not assert checks pass without running them. + +--- + +### Stage 5 -- Open or update PR + +Reload `plan.md` and `assets/pr-body-template.md` before starting. + +1. Fill in the PR body template using the plan's task list, commit history, + and any out-of-scope or check-failure notes. + +2. **New PR** (no `update_pr` in plan.md): + ``` + git push -u origin + gh pr create --title "" --body "<body>" + ``` + +3. **Update existing PR** (`update_pr: <number>` in plan.md): + ``` + git push origin <branch-name> + gh pr edit <number> --body "<updated body>" + ``` + +4. Output the PR URL to the user. Done. + +--- + +## Hard stops + +- **Secrets in code**: if any task requires putting a credential, token, or key + into source -- stop. Explain the secure alternative (Key Vault ref, env var, + OIDC) and do not proceed until the work item is revised. +- **Scope creep**: if Stage 3 reveals a coupling that makes the work item + impossible without a large refactor, stop at the scope boundary. Commit + what you have, note the blocker in the PR description, and open the PR as a + draft so a human can decide. +- **3 check failures**: surface in PR; do not keep retrying silently. diff --git a/.github/skills/build-and-pr/assets/pr-body-template.md b/.github/skills/build-and-pr/assets/pr-body-template.md new file mode 100644 index 0000000..e8dd9d8 --- /dev/null +++ b/.github/skills/build-and-pr/assets/pr-body-template.md @@ -0,0 +1,46 @@ +# PR body template + +Use this template when composing the pull request description at Stage 5. +Fill in every section; do not skip sections even if they are short. + +--- + +```markdown +## Summary + +<!-- One paragraph: what this PR does and why. Written for a human reviewer + who has not read the work item. --> +<SUMMARY> + +## Changes + +<!-- Bullet list of concrete file/code changes. One bullet per logical change. + E.g. "Added /health route in src/routes/health.ts" --> +<CHANGES> + +## Testing + +<!-- How was this verified? Reference the npm test output. --> +- `npm run lint`: <PASS | FAIL -- see notes below> +- `npm test`: <PASS | FAIL -- see notes below> + +## Out of scope (noted for follow-up) + +<!-- Items discovered during implementation that were NOT in the work item. + Leave blank if none. A human reviewer decides whether to pick these up. --> +<OUT_OF_SCOPE_ITEMS or "None"> + +## Check failures (if any) + +<!-- Populate only if Stage 4 exhausted its 3-retry budget. + List the failing test/lint rule and the blocker reason. --> +<CHECK_FAILURES or "None -- all checks green"> + +## Guideline compliance + +- [ ] No secrets committed (secure-coding-base s.1) +- [ ] All new handlers have auth (secure-coding-base s.3) +- [ ] All new DB queries parameterized (secure-coding-base s.2) +- [ ] CI/CD changes follow golden paths (ci-cd-golden-paths) +- [ ] Docs updated where code changed meaning (docs-style-guide) +``` diff --git a/apm.lock.yaml b/apm.lock.yaml index afca4e5..3f8eaba 100644 --- a/apm.lock.yaml +++ b/apm.lock.yaml @@ -1,6 +1,6 @@ lockfile_version: '1' -generated_at: '2026-05-07T23:57:26.502703+00:00' -apm_version: 0.12.2 +generated_at: '2026-06-22T13:03:46.726584+00:00' +apm_version: 0.21.0 dependencies: - repo_url: DevExpGbb/zava-agent-config host: github.com @@ -12,8 +12,9 @@ dependencies: deployed_files: - .github/agents/architect.agent.md deployed_file_hashes: - .github/agents/architect.agent.md: sha256:de74154b914769a13b4dc594f998db0e745280f8890af1ca526f10b10d0ecc5a - content_hash: sha256:bb505597414ca6787bb4cee8075c1d2f0ebc63c358792c2f4a7a5aa4dfe108a9 + .github/agents/architect.agent.md: sha256:5afb7ce3a027e25b37237e246cc5165fa91999e22ae1c91cf1f38b6f2b0bb403 + content_hash: sha256:238eface848c75d6b6d263f34d14e426b05af4e1ac14c890353709864803a3f1 + declared_license: MIT - repo_url: DevExpGbb/zava-agent-config host: github.com resolved_commit: 7a12679a9e1f55e06d16616ed0244b5fdeaad3da @@ -23,7 +24,11 @@ dependencies: package_type: marketplace_plugin deployed_files: - .agents/skills/incident-to-pr - content_hash: sha256:0f69245549bb931ddd009f09c736d8f63998f64fb6dc1a0a9ce133eb86e76335 + - .agents/skills/incident-to-pr/SKILL.md + deployed_file_hashes: + .agents/skills/incident-to-pr/SKILL.md: sha256:c68ec37dd01f5ffffa807b8b7d42e00f9b0c95d0c679749e8c43bb21d0248d9e + content_hash: sha256:dd6a7af443875312f9d2793068d3d4aa965c22bc101f53fd19b8e3c9a8b56acc + declared_license: MIT - repo_url: DevExpGbb/zava-agent-config host: github.com resolved_commit: 7a12679a9e1f55e06d16616ed0244b5fdeaad3da @@ -34,8 +39,9 @@ dependencies: deployed_files: - .github/instructions/ci-cd-golden-paths.instructions.md deployed_file_hashes: - .github/instructions/ci-cd-golden-paths.instructions.md: sha256:2fbe12eb300b75f8bc09ddb461c252a48c5b355c4aaf44f689d3b56b2086bb20 - content_hash: sha256:68d410244792d9650337f5ba9debf2233a3830fc0c4e5004ef4db90acb8c8be3 + .github/instructions/ci-cd-golden-paths.instructions.md: sha256:ae3aebedc313182f683142bf9dd3eeb8c50997445d747e090feee80c71781947 + content_hash: sha256:3e9670d78d71fd805ec0092f6b4c0d25c12419f603734cd3b4907678d2d94e73 + declared_license: MIT - repo_url: DevExpGbb/zava-agent-config host: github.com resolved_commit: 7a12679a9e1f55e06d16616ed0244b5fdeaad3da @@ -45,7 +51,11 @@ dependencies: package_type: marketplace_plugin deployed_files: - .agents/skills/panel-review - content_hash: sha256:6c34beac889dab8124b1c8f08783390264d976764d9b9012af3d0ea069ec5b22 + - .agents/skills/panel-review/SKILL.md + deployed_file_hashes: + .agents/skills/panel-review/SKILL.md: sha256:8e33b93b22e9f7eaa589d22012e220310f7897aa846936c9d5ea5eec04640b15 + content_hash: sha256:5bd2bddd200fc078d30e7b4156f76b60ca7613465fe6b45c3729f7fe43827c9e + declared_license: MIT - repo_url: DevExpGbb/zava-agent-config host: github.com resolved_commit: 7a12679a9e1f55e06d16616ed0244b5fdeaad3da @@ -58,7 +68,38 @@ dependencies: - .github/instructions/docs-style-guide.instructions.md - .github/instructions/secure-coding-base.instructions.md deployed_file_hashes: - .github/agents/security.agent.md: sha256:8092bef9ff98c66251754d33e9b241f4f25293578b31e258cee88124c80590c1 - .github/instructions/docs-style-guide.instructions.md: sha256:cad1b1161ef8d53e08038302b6c8cd14dbc03f75fa7ec267edf3d1f07824a739 - .github/instructions/secure-coding-base.instructions.md: sha256:f0a32226b87082a4e4cad08a070e55c177e23e0ef728329973634c84c3a81d14 - content_hash: sha256:b415e1715cf537747ea8312137fa129a154e46abfe50f4a97594fd40b0f1befe + .github/agents/security.agent.md: sha256:d4e21f8f4c542433407dd3fb379aca3b1b0a61f9dca438ed2296cd46e246b092 + .github/instructions/docs-style-guide.instructions.md: sha256:fddb50410971943302549ca50310df3b03147091cf13186ad81d55dbe6388ba7 + .github/instructions/secure-coding-base.instructions.md: sha256:5e8f6a09fac3aa837313ae0af5f17f4a050ac9be53f1ac81ad6ed31d75df60ee + content_hash: sha256:2bc0b645982e933356d19eb82ce36013491515ef4abf38943ed0b9763d65c320 + declared_license: MIT +local_deployed_files: +- .agents/skills/framework-modernizer +- .agents/skills/framework-modernizer/SKILL.md +- .agents/skills/framework-modernizer/evals/README.md +- .agents/skills/framework-modernizer/evals/evals.json +- .agents/skills/framework-modernizer/evals/expected/findings.txt +- .agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json +- .agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js +- .agents/skills/framework-modernizer/evals/run.js +- .agents/skills/framework-modernizer/evals/triggers.json +- .agents/skills/framework-modernizer/references/DESIGN.md +- .agents/skills/framework-modernizer/references/classifier-rubric.md +- .agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md +- .agents/skills/framework-modernizer/references/phased-plan-template.md +- .agents/skills/my-skill +- .agents/skills/my-skill/SKILL.md +local_deployed_file_hashes: + .agents/skills/framework-modernizer/SKILL.md: sha256:60df2736d5758566232f001406c5ea7b9d6260606ee789469823c8087de5b800 + .agents/skills/framework-modernizer/evals/README.md: sha256:4d64c1d9969b2526fb290396ab873be7bf2ca3635c16914390cf20fb8475021d + .agents/skills/framework-modernizer/evals/evals.json: sha256:487fe9bea22abc49fe0eef8d235b7571d6e5d21be2b9818ee372799d27f164d8 + .agents/skills/framework-modernizer/evals/expected/findings.txt: sha256:a77dc7769816479af420e015fc256cbec6004da9542f087b89dbe79c2d90214f + .agents/skills/framework-modernizer/evals/fixtures/express4-app/package.json: sha256:b5412f218cecf320a02cede5b783b48c23907ffa231c1549c38fa924345ebb9a + .agents/skills/framework-modernizer/evals/fixtures/express4-app/server.js: sha256:8236a6baf2a1d0387649685f6340fff39539240283110d794839530eeb928033 + .agents/skills/framework-modernizer/evals/run.js: sha256:b5db51664f8a292d88c23cee02a3c8a037c28360eae0252c905ec28c85e25f22 + .agents/skills/framework-modernizer/evals/triggers.json: sha256:5eb5ff56416221209579281adcdd6ae37be7d8d1a913e0c5be83a411b9372971 + .agents/skills/framework-modernizer/references/DESIGN.md: sha256:cfc85e90812240ce9f93918260f14a960857e67ef5160ab4d5f48fa8ee48580b + .agents/skills/framework-modernizer/references/classifier-rubric.md: sha256:0538096d985627699da3416537ecaecfbbb9c5b93d2196e332277a0e0bf215b3 + .agents/skills/framework-modernizer/references/express-4-to-5-breaking-changes.md: sha256:c46cc1529b0fd09a3ac9ca8c2cf5ad8e52b9225ad8e60812648e125dc7acd565 + .agents/skills/framework-modernizer/references/phased-plan-template.md: sha256:f4e6df41c8dde82002abe3c86c3844571e6b57dc0289944243c4e4a1cf08b04a + .agents/skills/my-skill/SKILL.md: sha256:c85123112cc9ac4d5b050c42dcdd894addd156b9c2e56d3bd614a1f546146e16