Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e4d814a
docs(aidd-context): refresh cook recipes
alexsoyes Jul 26, 2026
36f48da
docs(aidd-context): streamline token optimization recipe
alexsoyes Aug 7, 2026
6e45f17
feat(aidd-context): validate technical recipes
alexsoyes Aug 7, 2026
09f2d32
chore(cli): migrate Biome configuration
alexsoyes Aug 7, 2026
97b6abb
chore: merge next into codex/recipes-clean
alexsoyes Aug 27, 2026
4a9f80b
fix(aidd-context): align cook skill with generated format
alexsoyes Sep 17, 2026
ef53196
fix(aidd-context): preserve standalone cook workflows
alexsoyes Sep 30, 2026
ff28a45
chore(vcs): merge next and verify cook integration
alexsoyes Sep 30, 2026
74d1c18
fix(aidd-context): restore readable recipe authoring
alexsoyes Oct 1, 2026
ab2f64d
fix(aidd-context): make recipe checks portable
alexsoyes Oct 1, 2026
476678c
fix(aidd-context): align cook with skill templates
alexsoyes Oct 1, 2026
5a6cbea
chore(vcs): merge next into recipe cleanup
alexsoyes Oct 9, 2026
faebbf6
fix(cli): centralize recipe validation
alexsoyes Oct 9, 2026
98cd4db
fix(aidd-context): correct bundled recipe instructions
alexsoyes Oct 9, 2026
fa4af51
fix(cli): validate recipe links and strengthen framework assertions
blafourcade Oct 12, 2026
da795f5
fix(lefthook): preserve recipe validation on windows
blafourcade Oct 12, 2026
f6246be
fix(ci): install recipe validation dependencies before windows hooks
blafourcade Oct 12, 2026
c413202
chore(ci): integrate next and preserve windows recipe validation
blafourcade Oct 12, 2026
ec15617
docs(ci): describe windows recipe and installation builds
blafourcade Oct 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/cli-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,11 @@ jobs:
# devDependencies, so the install has to precede it — and the chain steps above stay
# ahead of any install on purpose.
- run: pnpm install --frozen-lockfile
- run: cd cli && pnpm install --frozen-lockfile
- name: Plugin suite
run: node scripts/check-tests-leave-git-alone.js -- node --test 'scripts/__tests__/**/*.test.js'

# A Windows commit's hooks, less the three cli jobs that need cli/'s dependencies.
# The same commit hooks on Windows; dedicated jobs cover the three CLI checks.
- name: Hooks - a commit's hooks pass on Windows
run: |
echo "# lefthook subject" > "$RUNNER_TEMP/msg"
Expand All @@ -535,10 +536,9 @@ jobs:
fi
exit $status

- run: cd cli && pnpm install --frozen-lockfile
# A real global install, not `node dist/cli.js` by path: nothing else proves `aidd`
# resolves on this platform's PATH. Built here, ahead of the suites below, so this is
# the only build the job needs.
# resolves on this platform's PATH. The recipe hook builds it first; this step
# rebuilds and packages it for the global install.
#
# Inlined rather than `pnpm run install:local`: a package.json script runs through
# pnpm's configured shell, `cmd.exe` on Windows, where that script's `$(node -p ...)`
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ jobs:
CI: "true"
# cli-ci.yml owns these three unconditionally, so running them again buys no coverage.
# cli-type-honesty is deliberately NOT excluded: no cli-ci.yml job runs it, making this
# its only CI coverage. The exclusion is also why no `cli/` install runs below — the
# three hooks needing cli/node_modules are the three dropped, and every hook that stays
# reads `cli/` as text from the repository root.
# its only CI coverage. Recipe validation builds the CLI and needs its separate install.
LEFTHOOK_EXCLUDE: "cli-biome,cli-architecture,cli-typecheck"
steps:
- name: Checkout
Expand All @@ -42,11 +40,13 @@ jobs:
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.local/share/pnpm/store
key: pnpm-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
key: pnpm-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml', 'cli/pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-

- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts
run: |
pnpm install --frozen-lockfile --ignore-scripts
pnpm --dir cli install --frozen-lockfile --ignore-scripts

- name: Run pre-commit hooks against the full tree
run: pnpm exec lefthook run pre-commit --all-files --force
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ Start with a functional need or User Story, then track it as an issue or ticket
before planning. Shipping follows the project's own delivery process. Capture a
learning only when it is durable enough to improve the next feature.

> 🍳 **More flows** → bundled recipes: [start a project](plugins/aidd-context/skills/12-cook/assets/recipes/start-a-project.md), [ship a feature](plugins/aidd-context/skills/12-cook/assets/recipes/ship-a-feature.md), and more.
> 🍳 **More flows** → bundled recipes: [MCP installations](plugins/aidd-context/skills/12-cook/assets/recipes/mcp-installation.md), [token optimization](plugins/aidd-context/skills/12-cook/assets/recipes/token-optimization.md), and [installing AIDDy](plugins/aidd-context/skills/12-cook/assets/recipes/install-aiddy-in-codex.md).

## 🧩 Plugins

Expand Down Expand Up @@ -338,7 +338,7 @@ Full catalog → [`CATALOG.md`](docs/CATALOG.md).

| | |
| --- | --- |
| 🍳 **Recipes** | Bundled how-to sheets: [start a project](plugins/aidd-context/skills/12-cook/assets/recipes/start-a-project.md), [ship a feature](plugins/aidd-context/skills/12-cook/assets/recipes/ship-a-feature.md), [MCP installations](plugins/aidd-context/skills/12-cook/assets/recipes/mcp-installation.md), [token optimization](plugins/aidd-context/skills/12-cook/assets/recipes/token-optimization.md). Project recipes created by cook live in `aidd_docs/recipes/`. |
| 🍳 **Recipes** | Bundled how-to sheets: [MCP installations](plugins/aidd-context/skills/12-cook/assets/recipes/mcp-installation.md), [token optimization](plugins/aidd-context/skills/12-cook/assets/recipes/token-optimization.md), [install AIDDy](plugins/aidd-context/skills/12-cook/assets/recipes/install-aiddy-in-codex.md). Project recipes created by cook live in `aidd_docs/recipes/`. |
| 🏛️ **[Architecture](docs/ARCHITECTURE.md)** | How the framework composes: plugins, skills, hooks, agents. |
| 🧩 **[Create a plugin](docs/CREATE_PLUGIN.md)** | Build and publish your own. |
| 🛒 **[Marketplace](docs/MARKETPLACE.md)** | Install scopes and versioning. |
Expand Down
60 changes: 60 additions & 0 deletions aidd_docs/tasks/2026_10/2026_10_01_cook-pr-cleanup/review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Review: Cook recipes, PR #521

- **Verdict**: approved locally; remote delivery pending
- **Diff**: `4919f04690350cb02d5f24e186137e04c8e6172e` to the working tree based on `476678cdbd6d932412e47470de0824cc656161d0`
- **Axes run**: code, functional, relevancy
- **Date**: 2026_10_05
- **Findings**: 0 critical, 0 warning, 0 minor in the reviewed scope

## Phases

### Phase 1: Recipe authoring

- [x] Preserve the readable English Markdown scaffold and optional sections; no template edits in this follow-up: `plugins/aidd-context/skills/12-cook/assets/recipe-template.md`.
- [x] Keep nine concise contract principles without extra scaffolding; no contract edits in this follow-up: `plugins/aidd-context/skills/12-cook/references/recipe-contract.md`.
- [x] Preserve standalone and repair routes, the generator's `Action | Does` table with bare slugs, and action test cases: `plugins/aidd-context/skills/12-cook/SKILL.md`, `plugins/aidd-context/skills/12-cook/actions/05-validate.md`.

### Phase 2: Reliable validation

- [x] Read and inspect the same file descriptor, close it, and report read failures: `cli/src/contexts/framework/infrastructure/recipe-files-adapter.ts`.
- [x] Expose validation through standard CLI wiring instead of a skill-owned script; stop when the CLI is absent or unsupported: `cli/src/presentation/commands/framework.ts`, `plugins/aidd-context/skills/12-cook/actions/05-validate.md`.
- [x] Accept useful descriptions without guessing sentence boundaries: `cli/src/contexts/framework/domain/recipes/recipe-validation.ts`.
- [x] Reject invalid published JSON examples without altering fenced bodies: recipe integration and built-command regression tests.
- [x] Validate visible links without rejecting hidden author notes: comment-aware link regression test.
- [x] Resolve Markdown anchors from visible linked heading text: linked-heading regression test.
- [x] Reject instructional placeholders while permitting real language syntax: every multiword placeholder from the unchanged template checked in eight languages; positive JSX, C include, and Java generic tests.

### Phase 3: Usable bundled guides

- [x] Qualify savings and interface recommendations instead of presenting guarantees: `plugins/aidd-context/skills/12-cook/assets/recipes/token-optimization.md`, `plugins/aidd-context/skills/12-cook/assets/recipes/mcp-installation.md`.
- [x] Remove published references to the deleted project/shipping guides: `README.md`, `docs/CATALOG.md`, `plugins/aidd-context/README.md`, `plugins/aidd-context/CATALOG.md`.
- [x] Distinguish Claude metrics from Codex logs and warn about sensitive tool snippets despite prompt redaction: `token-optimization.md`.
- [x] Include restart and explicit Claude output-style selection: `token-optimization.md`.
- [x] Keep installation examples usable; separate and label optional rollback and teardown: `mcp-installation.md`.

## Findings

| Sev | Kind | Phase | Location | Issue | Fix |
| --- | --- | --- | --- | --- | --- |
| - | - | - | - | No actionable findings remain in scope. | - |

## Verification

| Metric | Value |
| --- | --- |
| Verified | 15/15 acceptance criteria checked; criterion coverage, not an absolute quality guarantee |
| Scope | Full PR reviewed previously; unchanged material re-traced, and the complete follow-up implementation, tests, CLI wiring, skill delegation, recipes, and CI prerequisites reviewed |
| Root suite | `node scripts/check-tests-leave-git-alone.js -- node --test 'scripts/__tests__/**/*.test.js'`: 549 passed, 0 failed; Git-hook guard passed |
| CLI suite and coverage | `pnpm --dir cli exec vitest run --coverage` with a JSON reporter: 6,833 passed, 0 failed, 1 skipped; coverage thresholds passed, 99.15% statements and 96.98% branches |
| Regression evidence | Four original validator counterexamples failed before their fixes. The later template-vocabulary regression also failed before its fix. The six built-command tests failed with the command deliberately unwired, then passed after restoration |
| Guard evidence | Removed tracked files no longer break the text-source guard; an in-memory injected NUL still makes it fail. Sandbox tests execute Node by absolute path without weakening AI-tool filtering |
| CLI gates | Typecheck, lint, architecture tests, knip, help golden, type-honesty, and duplication checks passed. Lint retains one pre-existing unrelated unused-member warning in `uninstall-use-case.ts` |
| Built CLI smoke | `AIDD_SKIP_UPDATE_CHECK=1 bash cli/scripts/smoke-tools.sh`: 137 passed, 0 failed, remote-fetch case skipped; all 34/34 leaf commands exercised, including recipe validation. Temporary home and Codex profile isolated native host changes |
| Architecture | Pure domain, application port, filesystem adapter, composition-root wiring, no copied validator in the skill, no new runtime dependency |
| Fresh dependency/build check | Isolated CLI copy without `node_modules`: `pnpm install --frozen-lockfile --ignore-scripts`, then `pnpm build`, both exited 0. The Validate workflow now provisions these dependencies before hooks and caches both lockfiles |
| Bundled recipes | Built CLI validation with `--all --bundled plugins/aidd-context/skills/12-cook/assets/recipes`: `PASS: 3 recipe(s) validated.` |
| Bundle | 743.3 KB measured; deliberate budget 744 KB, documented in `cli/scripts/check-bundle-size.mjs`; size guard passed |
| Independent review | Recipe checker closed all three guide findings. Validator checker closed the original regressions and architecture finding, then rechecked and closed the CI-prerequisite and template-vocabulary findings |
| Local delivery | Uncommitted working-tree changes only; no commit, push, GitHub review/thread mutation, or merge |
| Remote boundary | Final `gh pr view 521` confirms remote head `476678cd`, `MERGEABLE` but `BLOCKED` and `REVIEW_REQUIRED`. Existing CI belongs to that head, not these local corrections. Commit/push, new required CI results, and human approval must precede merge |
| Limits | Linux CI was not executed locally. Native recipe installations, authentication/removal, and AI-client UI workflows were not exercised; non-JSON snippet validation and editorial quality still require the skill's semantic pass |
4 changes: 3 additions & 1 deletion cli/aidd_docs/memory/codebase-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ src/
│ │ │ │ └── content/
│ │ │ ├── ownership/ # machine plugin claims and safe project detach
│ │ │ ├── plugin/
│ │ │ ├── recipes/
│ │ │ ├── restore/
│ │ │ ├── setup/
│ │ │ ├── shared/
Expand All @@ -31,7 +32,8 @@ src/
│ │ │ ├── formats/
│ │ │ ├── manifest/
│ │ │ ├── plugins/
│ │ │ └── ports/
│ │ │ ├── ports/
│ │ │ └── recipes/
│ │ └── infrastructure/
│ ├── telemetry/ # what a session cost and who it was for
│ │ ├── application/
Expand Down
2 changes: 1 addition & 1 deletion cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"node": ">=22.12"
},
"packageManager": "pnpm@12.3.4",
"bundleBudgetKB": 734,
"bundleBudgetKB": 751,
"scripts": {
"build": "tsup && node scripts/check-bundle-size.mjs",
"build:check-size": "node scripts/check-bundle-size.mjs",
Expand Down
2 changes: 2 additions & 0 deletions cli/scripts/check-bundle-size.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"));
// guards for #829 (+10.4 KB over the preceding 703.8 KB); 1.1% measured headroom.
// 734 KB: 725.8 KB, Kilo Code's profile, generated bridge, and runtime smoke support (+9.6 KB
// over next's 716.2 KB); 1.1% measured headroom.
// 751 KB: 750.2 KB, recipe validation moved from Cook's script to the framework CLI after merging next;
// 0.1% measured headroom, with no added runtime dependency.
const budgetKB = pkg.bundleBudgetKB ?? 500;
const budgetBytes = budgetKB * 1024;

Expand Down
5 changes: 5 additions & 0 deletions cli/scripts/smoke-tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ IDE_TOOLS=(vscode)
ALL_COMMANDS=(
"setup" "doctor" "sync" "translate" "update" "clean"
"framework install" "framework update" "framework remove" "framework rules"
"framework validate-recipes"
"plugin remove" "plugin list" "plugin install" "plugin search" "plugin update"
"marketplace add" "marketplace list" "marketplace remove" "marketplace refresh" "marketplace check"
"auth login" "auth logout" "auth status"
Expand Down Expand Up @@ -175,6 +176,10 @@ run "--version" 0 "aidd/" "$ROOT" -- --version
run "unknown command exits non-zero" 1 "" "$ROOT" -- definitely-not-a-command
# (version/help are not counted leaves)

section "recipe validation (bundled)"
run "framework validate-recipes (bundled)" 0 "PASS: 3 recipe(s) validated." "$ROOT" -- \
framework validate-recipes --bundled "$ROOT/../plugins/aidd-context/skills/12-cook/assets/recipes"

section "translate (local fixture)"
FW_OUT="$TMPROOT/fw-out"
if run "translate --to claude" 0 "" "$ROOT" -- \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import path from "node:path";
import { InputRequiredError } from "../../../../kernel/errors.js";
import type { RecipeDirectory, RecipeFiles } from "../../domain/ports/recipe-files.js";
import {
headingAnchors,
inspectRecipe,
normalizedAnchor,
type RecipeFinding,
type RecipeInspection,
type RecipeValidationResult,
} from "../../domain/recipes/recipe-validation.js";

export interface ValidateRecipesInput {
readonly files: readonly string[];
readonly directories: readonly RecipeDirectory[];
}

type LinkTarget = { anchors: Set<string> | null } | { error: string };

export class ValidateRecipesUseCase {
constructor(private readonly files: RecipeFiles) {}

execute(input: ValidateRecipesInput): RecipeValidationResult {
const candidates = [
...input.files,
...input.directories.flatMap((directory) => this.files.list(directory)),
];
const files = [...new Set(candidates)].sort((left, right) => left.localeCompare(right));
if (files.length === 0) {
throw new InputRequiredError(
"Pass at least one recipe path or --all with a non-empty recipe directory."
);
}
const findings = files.flatMap((file) => this.validate(file));
findings.sort(
(left, right) =>
left.file.localeCompare(right.file) ||
left.line - right.line ||
left.rule.localeCompare(right.rule)
);
return { findings, fileCount: files.length };
}

private validate(file: string): RecipeFinding[] {
const source = this.files.read(file);
if (source.kind === "unreadable") {
const rule =
source.code === "ENOENT"
? "file-exists"
: source.code === "EISDIR"
? "file-type"
: "file-read";
return [
{ file, line: 1, rule, fix: `Pass a readable Markdown recipe file (${source.code}).` },
];
}
if (path.extname(file).toLowerCase() !== ".md") {
return [
{ file, line: 1, rule: "file-type", fix: "Pass a Markdown recipe file ending in .md." },
];
}
const inspected = inspectRecipe(file, source.content);
return [...inspected.findings, ...this.checkLinks(file, inspected)];
}

private checkLinks(file: string, inspected: RecipeInspection): RecipeFinding[] {
const findings: RecipeFinding[] = [];
const cache = new Map<string, LinkTarget>([[file, { anchors: inspected.anchors }]]);
for (const link of inspected.links) {
const hashIndex = link.target.indexOf("#");
const rawPath = hashIndex === -1 ? link.target : link.target.slice(0, hashIndex);
const rawAnchor = hashIndex === -1 ? "" : link.target.slice(hashIndex + 1);
let targetFile = file;
if (rawPath) {
let decodedPath: string;
try {
decodedPath = decodeURI(rawPath);
} catch {
findings.push({
file,
line: link.line,
rule: "link-encoding",
fix: `Encode the link target correctly: ${rawPath}`,
});
continue;
}
targetFile = path.resolve(path.dirname(file), decodedPath);
}
let target = cache.get(targetFile);
if (!target) {
const markdown = targetFile.toLowerCase().endsWith(".md");
const source = this.files.read(targetFile, markdown);
target =
source.kind === "unreadable"
? { error: source.code }
: { anchors: markdown ? headingAnchors(source.content) : null };
cache.set(targetFile, target);
}
if ("error" in target) {
findings.push({
file,
line: link.line,
rule: link.image ? "image-target" : "link-target",
fix: `Point the local target to a readable file: ${rawPath} (${target.error}).`,
});
} else if (rawAnchor && target.anchors === null) {
findings.push({
file,
line: link.line,
rule: "link-anchor",
fix: `Remove the anchor or target a Markdown heading: #${rawAnchor}`,
});
} else if (rawAnchor && !target.anchors?.has(normalizedAnchor(rawAnchor))) {
findings.push({
file,
line: link.line,
rule: "link-anchor",
fix: `Point the link to an existing heading: #${rawAnchor}`,
});
}
}
return findings;
}
}
13 changes: 13 additions & 0 deletions cli/src/contexts/framework/domain/ports/recipe-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type RecipeSource =
| { readonly kind: "readable"; readonly content: string }
| { readonly kind: "unreadable"; readonly code: string };

export interface RecipeDirectory {
readonly path: string;
readonly optional: boolean;
}

export interface RecipeFiles {
read(file: string, contentRequired?: boolean): RecipeSource;
list(directory: RecipeDirectory): string[];
}
Loading
Loading