Skip to content

ci(deps-dev): bump js-yaml from 5.2.1 to 5.2.2 - #545

Merged
aidd-bot[bot] merged 1 commit into
mainfrom
dependabot/npm_and_yarn/js-yaml-5.2.2
Jul 28, 2026
Merged

ci(deps-dev): bump js-yaml from 5.2.1 to 5.2.2#545
aidd-bot[bot] merged 1 commit into
mainfrom
dependabot/npm_and_yarn/js-yaml-5.2.2

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Jul 28, 2026

Copy link
Copy Markdown
Contributor

Bumps js-yaml from 5.2.1 to 5.2.2.

Changelog

Sourced from js-yaml's changelog.

[5.2.2] - 2026-07-24

Fixed

  • Quote flow scalars where a colon precedes a flow indicator, #773.

Security

  • Avoid exponential parsing time for nested flow sequence pairs.
Commits
  • 3c29559 5.2.2 released
  • 3e5240f parser: avoid reparsing flow sequence pair keys
  • bd7ebb2 fix: quote flow scalars where a colon precedes a flow indicator (#773)
  • See full diff in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    You can disable automated security fix PRs for this repo from the Security Alerts page.

Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 5.2.1 to 5.2.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](nodeca/js-yaml@5.2.1...5.2.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.2.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Dependency update (dependabot) javascript Pull requests that update javascript code labels Jul 28, 2026
@aidd-bot
aidd-bot Bot enabled auto-merge (squash) July 28, 2026 12:44
@aidd-bot
aidd-bot Bot merged commit be83f25 into main Jul 28, 2026
9 of 10 checks passed
@dependabot
dependabot Bot deleted the dependabot/npm_and_yarn/js-yaml-5.2.2 branch July 28, 2026 12:45
blafourcade added a commit that referenced this pull request Jul 31, 2026
* docs(framework): update README title and tagline

Align the project messaging with the framework's current branding and focus.

* chore: release main (#453)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* refactor(aidd-context): action citations use markdown links, not @

Convert every `@../` reference in the action files of skill-generate and
project-memory to standard markdown links `[name](path)`, so citations are
clickable in editors and on GitHub.

Propagate the convention to the contract so generated skills follow suit:
R6 now mandates a markdown link, R8 drops the `@`-chain wording, and
review-protocol says "cited references". Functional `@`-imports in the memory
block (`@aidd_docs/...`) are untouched: those are load mechanics, not citations.

Sync docs/CATALOG.md skill-generate action names to the shipped set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177RKF7c5Wpv329Q7QYquV2

* docs(framework): reposition readme as agnostic token-optimized framework (#440)

* feat(aidd-context): add research and apply actions to cook skill

Extend 12-cook from author-only to a fuller loop:

- 03-research: refine the target recipe via a checklist, fan out one
  agent per scouting angle, then present alternatives / coverage gaps /
  counter-intuitive wins plus a recommendation. Ephemeral, never writes.
- 04-apply: execute an existing recipe against the project via an agent
  that tracks the steps as a todo list and confirms before mutating.
- upsert: qualitative overlap guard (none/partial/high) that suggests
  updating an existing recipe instead of creating a duplicate.
- recipe-template: steps become "#### N)" headings with what/why,
  actionable how-bullets, and a mandatory concrete example
  (command with output, snippet, or screenshot/video).

New references/assets: research-playbook, refine-goal-checklist,
research-checklist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(aidd-context): verify each research candidate before presenting

Add a Verify step to cook's 03-research: after curation, spawn one
agent per surviving candidate to confirm it exists, capture its
official link, and record its latest state. Drop anything that cannot
be confirmed against an official source.

This makes the checklist's "Confirm claims" real and guards the recipe
against hallucinated tools or methods.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): add token optimization recipe

Add recipes/token-optimization.md: a ranked list of token-saving tips
for AI coding assistants (measure first, trim CLAUDE.md, caveman,
RTK/SNIP, CLI over MCP, progressive disclosure, compact, model routing,
cap thinking), each tip in the new template format with a concrete
example and an official link verified to exist. Register it in the
recipes index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): extract recipe contract and tighten cook template

Move the recipe authoring rules out of the template's HTML comment into
references/recipe-contract.md, cited by SKILL.md and upsert. The template
keeps only the scaffold.

- Steps heading carries the goal ("## Steps to <outcome>") and each step
  opens with an emoji.
- Step how-bullets follow where-it-is -> install-from-URL -> how-to-use,
  benefit-first and filler-free.
- "## Why" is short, keyword-led, and bold.
- SKILL.md description is shorter and high-level, triggered on
  "recipe" / "cook" / "/cook".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(aidd-context): repair 12-cook SKILL.md YAML frontmatter

The description contained an unquoted "sheets: list" colon, which YAML
parses as a mapping value and rejects ("mapping values are not allowed
here"), breaking the skill's frontmatter. Replace the colon with a dash.
The bug pre-dated this branch (same colon on main).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): refine cook recipe structure and apply R13 to cook

Review feedback on the cook skill, plus the new includes convention:

- Recipe template drops the Time field, groups steps under three level
  subheadings (Beginner / Intermediate / Expert), and aligns the Level
  enum to Expert.
- upsert now runs `research` (03) first for any new or substantial
  recipe and drafts only from its verified results, never from memory —
  the "always research" rule was being skipped as a routing note.
- Apply R13 (explicit, scoped includes): the action-specific research
  aids move out of SKILL.md and are cited from 03-research in fenced
  blocks; refine-goal-checklist is renamed research-goal-checklist;
  SKILL.md lists only the global recipe-template and recipe-contract.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): make recipe level subheadings optional

Level grouping was effectively mandatory. Make the contract state it is
optional: group steps under Beginner/Intermediate/Expert only when the
recipe spans difficulty levels and grouping helps; a short or
single-level recipe lists its steps directly. Relax the upsert Test
accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): conform token-optimization recipe to recipe contract

Align the recipe with references/recipe-contract.md: drop the Time row
(no longer in the template), name the section "Steps to <outcome>" instead
of a bare Steps, remove the "ranked by impact" filler line, and give each
step how-bullets plus a concrete example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(framework): split measure steps per tool and drop metadata table

Apply review of the token-optimization recipe:

- Goal becomes a plain sentence (no blockquote).
- Drop the Level/Prerequisites table; level now lives in the subheadings.
- Split the bundled "Measure first" step into one step per tool
  (/context, /cost, /insights, an analytics tool) — one subheading per
  action.
- Each measure step says how to use and read the command, and carries an
  example output; the analytics step uses a real prompt-analytics run
  (volumes rounded, cost omitted) showing cache reads dominate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): drop the Goal label from token-optimization

The recipe opens with a plain description sentence, no "Goal:" label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): recipe header = description sentence, no table

Rules extracted from the token-optimization recipe review:

- A recipe opens with the H1 title then one plain description sentence —
  no "Goal:" label, no blockquote, no metadata table. list/upsert parse
  that sentence; the index becomes `| Recipe | Description |` (Level was
  dropped, level now lives only in the optional subheadings).
- One step = one action; never bundle several tools or commands under a
  single heading.
- Every step's example shows real command output (from docs or by
  running it); the research playbook now requires capturing a real
  example and marking interactive output for the human to paste.
- `## Why` is one idea per line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): use the real prompt-analytics dashboard image

Replace the fabricated text output with the tool's official dashboard
screenshot (cost by token type makes "cache reads dominate" visible).
The slash-command placeholders now ask for a screenshot of the real
output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): prefer images over text examples

A real screenshot or GIF is unfakeable, faithful, and credible, where a
text example can be approximated (the failure we hit). Make the contract
and the research playbook prefer an image that matches the action —
e.g. a tool's official screenshot — falling back to real text output
only when no image exists.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): show real tool usage in token-optimization

- caveman: show its real `/caveman` invocation (and modes), not just a
  before/after.
- RTK: a Mermaid diagram of the proxy flow + the real `rtk <command>`
  syntax (was a wrong `rtk proxy …`) and the README's own 200->10 token
  saving.
- CLI vs MCP: a comparison table instead of prose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): add tool-example rules to recipe contract

From the token-optimization review:

- Show how to invoke a tool (its real command or slash invocation).
- Reuse the canonical example from the tool's own site or README, never
  an invented one.
- A "prefer X over Y" step uses a comparison table, not prose.
- A structural or flow concept (proxy, pipeline, architecture) gets a
  small Mermaid diagram.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): use caveman's real before/after example

Replace the invented caveman before/after with the verbatim example from
its README (rule F: reuse the tool's own canonical example).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): numbered actions, valid-JSON config, tighter sections

- Actions are numbered lists; descriptions are prose (no description-bullets).
- settings.json shown as valid JSON, not a bash fragment.
- RTK Mermaid uses concrete values (cargo test ~25k -> ~2.5k tokens).
- CLI vs MCP notes MCP tool/context selection (cheaper, still slower).
- Step 10 reworded plainly ("load knowledge on demand").
- Drop the weak Verify and the redundant Related; add a short conclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): add bullet/diagram/config/section rules to contract

From the token-optimization review:

- Actions are a numbered list; descriptions and indications are prose,
  never bullets.
- A Mermaid diagram carries concrete example values.
- A config example uses the file's real syntax (valid JSON for
  settings.json, valid YAML for frontmatter, ...).
- Reuse a tool's canonical example captured verbatim — never a paraphrase
  or on the strength of a summary that one exists.
- No `## Related` section (links live inline); `## Verify` is optional;
  recipes may end with a short conclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): expert steps use verified Claude Code config

- Step 10 now audits which skills/tools run via `Ctrl+O` (toggles the
  detailed transcript), instead of the vaguer "load on demand".
- Step 11 routes by difficulty with the real `model:` frontmatter field
  on a skill or agent (haiku/sonnet/opus/inherit) — example agent pinned
  to haiku.

Both verified against the official Claude Code docs (interactive-mode,
skills, sub-agents).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): point the instruction-file step at AGENTS.md

Use the framework's own concise AGENTS.md as the worked example for a
trimmed instruction file, with its real rules (answer first, no
narration), instead of a generic snippet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): apply analyses a recipe and asks before acting

A recipe is often a human tutorial, not an agent-runnable procedure. So
apply now reads the recipe, classifies each step agent-doable vs
human-only, and asks the user what to do before any change — then runs
the chosen agent-doable steps and reports the human-only ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(framework): add five verified token levers to the recipe

From a research pass, all verified against official Claude Code docs:

- Plan mode (Shift+Tab ×2) — avoid execution-phase rework.
- `/clear` between tasks — drop stale context instead of re-billing it.
- Deny reads (`permissions.deny`) — keep vendor/dist/secrets out of
  context; the official answer to the unshipped `.claudeignore`.
- Subagent offload — run high-volume ops in an isolated context.
- Protect cache hits — avoid mid-task model/MCP/effort switches that
  invalidate the cache (closes the loop on "cache reads dominate").

Also fold the built-in `outputStyle: concise` into the "talk less" step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(aidd-context): bundle cook recipes in skill

* feat(framework): publish AIDDy v2 pet

* docs(framework): reposition readme as agnostic token-optimized framework

* feat: avoid uncached AGENTS.md

* docs: add fixing behavior for assertions

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(aidd-context): restructure 10-learn into a 5-action router (#455)

Splits source out of gather so origin selection (conversation, file,
diff, review) is explicit before extraction runs, and renames
assets/decision-template.md to adr-template.md to match its actual
output. CATALOG.md's 10-learn index is regenerated to match.

Folds in fixes found during a headless claude+codex audit of the
skill (13 scenarios x 2 tools):
- sync locates update_memory.js instead of assuming a fixed path that
  doesn't exist in either tool's real install layout
- never scaffold the memory bank, even under a blanket write
  pre-approval
- sync's own git-diff-cached test reflects a pre-staged index instead
  of demanding it be empty
- sources.md again defaults to conversation when no hint is given,
  matching pre-refactor behavior, instead of always requiring context
  inference
- the two memory-write transversal rules merge into one, trimming the
  router closer to the smallest shape that still states every rule

Closes #278
Refs #419

* ci(deps): bump github/codeql-action/analyze from 4.37.0 to 4.37.1 (#456)

Bumps [github/codeql-action/analyze](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a)

---
updated-dependencies:
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci(deps): bump github/codeql-action/autobuild from 4.37.0 to 4.37.1 (#460)

Bumps [github/codeql-action/autobuild](https://github.com/github/codeql-action) from 4.37.0 to 4.37.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...7188fc363630916deb702c7fdcf4e481b751f97a)

---
updated-dependencies:
- dependency-name: github/codeql-action/autobuild
  dependency-version: 4.37.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(cli): migrate aidd-cli into framework as cli/, full history preserved

Merges ai-driven-dev/aidd-cli into this repo as cli/ via git subtree —
full commit history preserved (reachable via the subtree-merge commit's
second parent, kept on the source branch chore/migrate-cli-into-framework,
not deleted by this squash). cli/ stays self-contained: own package.json,
own lockfile, not a pnpm workspace member.

- release-please: cli added as a ninth package, release-type: node
- CI: new cli-ci.yml (typecheck/lint/test/build/knip/jscpd), native
  path-filter on cli/** only
- publish-cli in ci.yml, gated on release-please's paths_released,
  real npm OIDC trusted publishing (no NPM_TOKEN secret)
- Fixed along the way: npm README stub bug, a pnpm-version/cache-path
  CI bug, a stale duplicate memory-sync script and its dead test,
  hardcoded personal paths, dead ISSUE_TEMPLATE/CONTRIBUTING.md left
  over from the standalone repo
- ai-driven-dev/aidd-cli stays active, unarchived, until a real release
  from here is proven end to end

Refs #448. Full plan, phase files, and audit reports:
cli/aidd_docs/tasks/2026_07/2026_07_20_migrate-cli-into-framework/

* fix(cli): repoint self-references from aidd-cli to framework

package.json never had repository/homepage/bugs fields at all (the
migration plan assumed they existed and needed updating — they didn't
exist). Added them pointing at ai-driven-dev/framework with
directory: "cli" so npm's package page links to the right subtree.

aidd_docs/README.md (the standard scaffold every aidd setup generates)
had one stale link to the old standalone repo.

* fix(cli): add license and keywords for the npm package page

package.json was missing license (npm shows this prominently on the
package page) and keywords (npm search discoverability). license: MIT
matches framework's own root LICENSE — same repo, same license, no
ambiguity to resolve.

* chore: release main (#485)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* fix(cli): restore correct version after release-please regression (#488)

release-please's PR #485 computed cli's next version as 4.0.0 instead
of bumping forward from 5.1.3. Root cause: cli had no prior cli-v*
tag to anchor its version walk, so it fell through to an old,
otherwise-already-consumed root Release-As: 4.0.0 directive (commit
5b0fc9d, framework's own major-bump forcing from May 2026, unrelated
to cli) and applied it to the new component.

Both npm and GitHub Packages correctly rejected the resulting publish
(existing 4.0.0 from the pre-migration standalone repo, real current
version is 5.1.3 on both registries — untouched). Bad tag/release
cli-v4.0.0 already deleted.

Reverts cli/package.json and the manifest's cli entry to 5.1.3, and
drops the bogus CHANGELOG entry. A cli-v5.1.3 tag/release will be
created on top of this commit to anchor future release-please runs
so the walk never reaches that old directive again.

* chore: release main (#489)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* fix(cli): restore correct version again after second regression, re-anchor for release-please (#493)

A second, unrelated release-please cycle (PR #489, "chore: release
main") auto-merged moments after #488 and independently recomputed
cli's version, reintroducing 4.0.0 the same way #485 did, even
though that run's own log showed it correctly reading 5.1.3 from the
manifest as the baseline. The manifest baseline alone does not bound
the version-bump walk; only a matching cli-v* release does, and none
existed yet when that run started.

Both npm and GitHub Packages rejected the resulting publish again
(pre-existing 4.0.0, same as before), still untouched at the real
5.1.3. Bad tag/release cli-v4.0.0 deleted again.

A cli-v5.1.3 anchor release now exists (pointing at bdd12c66, the
commit right before the cli migration landed), created before this
commit merges, so the next release-please run has a real boundary to
stop its walk at and can no longer reach the old, already-consumed
version-forcing directive from commit 5b0fc9d (framework's own
unrelated major-bump forcing from May 2026, targeting 4.0.0 for the
root package only, not cli).

This commit also serves as the nudge for release-please to
re-evaluate cli against that new anchor and correctly propose 5.1.4
next, carrying the real pending fixes, migration, repoint, license
and keywords, into a real changelog entry and npm and GHP publish.

* chore: release main (#494)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* fix(release-please): exclude manifest file from root path tracking (#497)

Any commit touching .release-please-manifest.json (a root-level
file) counts toward the "." package's own version bump, since root
has no path restriction. Today's cli version-regression fixes needed
to hand-edit that file directly and, as a side effect, cut two extra
framework releases (v5.5.3, v5.5.4) that carried no real content
change for root.

exclude-paths (documented in release-please's manifest schema,
ReleaserConfigOptions) skips a commit for a given package when every
file it touches falls under the listed paths. Scoped to the "."
package only, so cli's and the plugins' own path-based tracking are
unaffected.

* chore: release main (#498)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* fix(docs): resolve real broken markdown links, drop temporary link-check excludes (#500)

Two real, distinct issues were hiding behind the migration's temporary
--ignore flags:

- 8 links in plugin-architecture-master.md pointed at real, existing
  sibling files whose names contain a literal '#' (e.g.
  2026_04_27-#260-plugin-architecture-part-1.md). Markdown link
  parsers treat an unescaped '#' as a URL fragment separator,
  truncating the path before the file check ever runs. URL-encoding
  it as %23 fixes it; the checker already decodeURI()s targets.
- The remaining ~235 flagged references under cli/tests/fixtures/**
  and cli/aidd_docs/tasks/** are not a mix of real links and
  shorthand: cli/tests/fixtures/** is synthetic mock trees that never
  materialize every file they reference (confirmed: the full test
  suite passes with those template files absent), and
  cli/aidd_docs/tasks/** is a historical record whose @path
  references and inline rewrite-rule examples are expected to drift
  as the codebase evolves after the fact. Scrubbing them file-by-file
  would just rewrite historical docs that were accurate when written.

Moved that exemption into the checker itself (DEFAULT_IGNORES,
applied on top of any --ignore given) rather than the hook command,
so `node scripts/check-markdown-links.js` with no flags is the thing
that's actually clean. lefthook.yml's markdown-links command now
carries no --ignore flags and no TEMPORARY comment.

* chore: release main (#501)

Co-authored-by: aidd-bot[bot] <290648294+aidd-bot[bot]@users.noreply.github.com>

* ci(deps-dev): bump js-yaml from 5.2.1 to 5.2.2 (#545)

Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 5.2.1 to 5.2.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/5.2.1...5.2.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 5.2.2
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(cli): scope plugin restore to --tool, collapse double materialization (#506)

Two confirmed bugs on aidd restore / aidd ai restore / aidd ide restore:

A2 (scope leak): RestoreUseCase.runPluginRestore never threaded ctx.toolIds
into RestoreAllPluginsUseCase, so `--tool X` still restored every other
installed AI tool's plugin files unscoped.

A3 (double materialization): RestoreAllUseCase ran two independent,
unconditional passes over the same (tool, plugin) pairs on every global
`aidd restore` — the second pass ignored --tool scope, ignored fileFilter
entirely, and for built-tree tools (cursor/opencode) wrote every plugin
file to disk twice with no hash guard.

RestoreAllPluginsUseCase now takes an optional toolIds filter and returns
{totalFiles, pluginNames} so the single remaining pass can still report
"Restored plugins: x, y". RestorePluginUseCase deleted — its one caller
was the removed second pass (confirmed via grep).

2048/2048 tests green (2042 existing + 6 new regression tests), tsc clean.
Pre-fix reproduction verified via git stash before each fix (double
materialization count, interactive file selection not excluding plugins).

Ported from the archived aidd-cli repo (BUG-E3-02 + SPIKE-E3-01,
pre-migration branches) — original work predates the framework migration
(PR #462).

* fix(cli): document intentional force on internal build-cache rebuild (#505)

outDir for this path is always .aidd/cache/built/<marketplace>/<target>, an
aidd-owned disposable cache, never a user directory — collision here means
"cache from a previous build exists", not data at risk. Adds a comment
stating that explicitly and a regression test (real FlatBuildStrategy, not
the fake buildFor stub) pinning the collision-bypass so it fails if force is
ever flipped or outDir stops being cache-only.

Ported from the archived aidd-cli repo (BUG-E2-01, pre-migration branch
docs/e2-01-force-cache-rebuild) — original work predates the framework
migration (PR #462).

* refactor(cli): inject shared StatusUseCase/RestoreUseCase/UpdateOneToolUseCase (#507) (#508)

StatusAllUseCase, RestoreAllUseCase, and UpdateAllUseCase each rebuilt their
own duplicate of a collaborator deps.ts already constructs and shares with
ai.ts/ide.ts (deps.statusUseCase, deps.restoreUseCase, deps.updateOneToolUseCase
consumed directly by those commands). Two of the four duplicate sites rebuilt
on every execute() call, not just once.

Confirmed all 3 target classes stateless before sharing them as singletons
(no memo maps, no accumulator fields — BulkConflictState is passed as an
execute() parameter, never stored on UpdateOneToolUseCase). Each class now
receives the real instance by constructor injection; every now-dead
raw-sub-dependency param removed rather than left unused.

grep confirms zero remaining "new StatusUseCase/RestoreUseCase/
UpdateOneToolUseCase" outside deps.ts's 3 original construction sites.
2048/2048 tests pass unmodified — zero behavior change, pure DI wiring.

Stacked on fix/e3-02-restore-plugin-scope-dedup (touches the same
restore-all-use-case.ts) — needs that branch merged first.

Ported/implemented fresh from the archived aidd-cli repo's plan
(SPIKE-E1-01 + BUG-E1-02, pre-migration) — original work predates the
framework migration (PR #462).

* fix(cli): resolve buildClaudeStyleMarketplaceEntry and PluginTranslator name collisions (#509)

Two genuinely different symbols shared the same name, confirmed via full
read of both sides of each collision — real friction, not cosmetic:
mode-b-flat-materialization-translator.ts already had to locally alias one
of them (`PluginTranslator as PluginTranslatorHelper`) just to compile.

buildClaudeStyleMarketplaceEntry: the catalog-row builder in
marketplace-strategy-helpers.ts (framework build time) renamed to
buildClaudeStyleCatalogEntry — pairs naturally with the neighboring
buildClaudeStyleMarketplace. The domain/capabilities settings-entry builder
(install time, consumed by claude.ts/copilot.ts) keeps its name.

PluginTranslator: the domain content-format-conversion class renamed to
PluginContentTranslator (file renamed to match). The application-layer
strategy interface (Section C protected pattern, 4 implementer/factory
sites) is untouched.

Pure rename, zero behavior change. 2049/2049 tests pass with zero
assertion changes; grep confirms each name now maps to exactly one symbol.

* refactor(aidd-refine): 01-brainstorm moves from fixed probing loop to internal discovery map (#510)

Restructures 01-brainstorm from a flat probing.md + question-angles.md
pair into a discovery-map/readiness/interview-depth apparatus, moves
finalize output to a discovery-brief.md template, and hides internal
process vocabulary from user-facing text by default.

Fixes applied on top of the base restructure, found via headless and
live testing:
- restore the "state a leaning + tradeoff, even unprompted" rule that
  the restructure had silently dropped (SKILL.md, 03-integrate.md)
- restore the "stay at the idea's altitude, leave finer how-to as a
  flagged assumption" guardrail (references/probing.md)
- fold no longer silently overwrites a contradicted fact — confirms
  which stands before folding (03-integrate.md)
- drop the "use the user's language" instruction, never present before
  this refactor either
- trim 04-finalize's Ask step and Test section to stop restating what
  the linked reference files already say

Closes #504

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* ci(deps): bump github/codeql-action/init from 4.37.0 to 4.37.3 (#476)

Bumps [github/codeql-action/init](https://github.com/github/codeql-action) from 4.37.0 to 4.37.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/99df26d4f13ea111d4ec1a7dddef6063f76b97e9...e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#458)

Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci(deps): bump actions/setup-node from 6.4.0 to 7.0.0 (#459)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e...820762786026740c76f36085b0efc47a31fe5020)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @types/node from 24.10.15 to 26.1.1 in /cli (#480)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 24.10.15 to 26.1.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @commitlint/cli from 19.8.1 to 21.2.1 in /cli (#478)

Bumps [@commitlint/cli](https://github.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli) from 19.8.1 to 21.2.1.
- [Release notes](https://github.com/conventional-changelog/commitlint/releases)
- [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/@commitlint/cli/CHANGELOG.md)
- [Commits](https://github.com/conventional-changelog/commitlint/commits/v21.2.1/@commitlint/cli)

---
updated-dependencies:
- dependency-name: "@commitlint/cli"
  dependency-version: 21.2.1
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump commander from 12.1.0 to 15.0.0 in /cli (#479)

Bumps [commander](https://github.com/tj/commander.js) from 12.1.0 to 15.0.0.
- [Release notes](https://github.com/tj/commander.js/releases)
- [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tj/commander.js/compare/v12.1.0...v15.0.0)

---
updated-dependencies:
- dependency-name: commander
  dependency-version: 15.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump typescript from 5.9.3 to 7.0.2 in /cli (#484)

Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 7.0.2.
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/commits)

---
updated-dependencies:
- dependency-name: typescript
  dependency-version: 7.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(cli): derive framework build's SUPPORTED_TARGETS from the build registry (#514)

* fix(cli): derive framework build's SUPPORTED_TARGETS from the build registry

framework.ts hand-copied a 5-name target list to validate --target, separate
from FRAMEWORK_BUILD_REGISTRY (deps.ts) — the real target:mode routing
source of truth already used one command down (createFrameworkBuildUseCase
returning undefined for an unknown combo). No drift exists today (both list
the same 5 targets), but nothing previously prevented one on the next
target/mode addition or removal.

deps.ts now exports SUPPORTED_BUILD_TARGETS, derived once from
FRAMEWORK_BUILD_REGISTRY's keys. framework.ts imports it instead of
maintaining its own copy.

Pure refactor, zero behavior change. 2049/2049 tests pass, tsc clean.

* fix(cli): move build target/mode list to domain, out of infrastructure

Review feedback on the prior commit: framework.ts (application/commands)
importing SUPPORTED_BUILD_TARGETS from infrastructure/deps.ts was backwards
— "which target/mode pairs exist" is a pure data fact, not something
requiring adapters or DI, so it belongs in domain and application should
depend inward on it, not sideways through infrastructure.

FRAMEWORK_BUILD_TARGET_MODES (+ derived SUPPORTED_BUILD_TARGETS) now lives
in domain/models/framework-build.ts. deps.ts's FRAMEWORK_BUILD_REGISTRY
keeps its own literal keys unchanged (the build-strategy wiring is
legitimately infrastructure's job) — a new test
(tests/infrastructure/framework-build-registry.unit.test.ts) instead
asserts the registry's runtime behavior matches the domain list exactly,
both directions, closing the drift risk between the two independently
-authored sources.

Confirmed via full grep audit: no other file under domain/ imports from
application/ or infrastructure/ anywhere in the codebase.

2059/2059 tests pass (2049 + 10 new), tsc clean.

* test(cli): add AiTool registry conformance suite (#516)

Closes the original goal this backlog was built around: "adding an AI tool
is adding one file" is now guaranteed by a test that fails when it isn't,
instead of by human discipline.

Every assertion iterates getAllRegisteredTools() rather than a hardcoded
list, so a new tool file is automatically subject to all of them. Guards:
AiTool<C> shape and required methods; registry <-> AI_TOOL_IDS agreement
both ways; every tool reachable by at least one framework build
target/mode; every plugins-capable tool present in MARKETPLACE_PROBES; and
no probe/build entry naming an unregistered tool (stale-entry guard).

Scope note (user-confirmed before planning, not a silent reinterpretation):
US-E9-02/03 asked to *derive* the probe tables and adapter dispatch from
the tool contract. The spike found PluginFormat ("a native on-disk layout
aidd can read") and AiToolId ("a tool aidd installs into") are distinct
concepts that merely coincide in membership today — deriving one from the
other would couple them permanently and force a parser function reference
into an otherwise pure-data contract, for a guarantee this test delivers
with zero production change. Same shape as the #514 review outcome.

Assertions were chosen against the live registry, not invented: opencode
is deliberately absent from PLUGIN_MANIFEST_PROBES (flat plugin mode, no
per-plugin manifest dirs), so per-tool manifest-probe coverage is NOT
asserted. All five guards were verified to fail on a deliberate mutation
(removed tool id, removed build entry, removed marketplace probe, typo'd
probe format, malformed directory field) with messages naming the tool,
the field, and the file to fix.

2088/2088 tests pass (2059 + 29 new), tsc clean, zero production files
changed.

* refactor(cli): inject PostInstallPipelineUseCase and GitignoreUseCase (#523)

Both were instantiated ad hoc at 6 sites, escaping the dependency graph
entirely — no test could substitute either at the command level, unlike
every other shared use-case.

deps.ts now builds each once and injects them:
GitignoreUseCase -> PostInstallPipelineUseCase -> the 3 install use-cases,
and GitignoreUseCase -> CleanUseCase. Both verified stateless (only
readonly constructor deps) before being shared as singletons, same check
applied in #508.

Dead constructor params removed as a consequence, each checked per file
rather than assumed: PostInstallPipelineUseCase drops `fs` (its only use
was the removed `new GitignoreUseCase`), and InstallIdeConfigUseCase /
InstallRuntimeConfigUseCase drop `manifestRepo` (their only use was the
removed `new PostInstallPipelineUseCase`). InstallIdeToolUseCase still
uses manifestRepo once, so it keeps it; clean-use-case uses `fs` 13 times,
so it keeps that.

Scope: 5 of the 6 sites. init-use-case.ts is deliberately left alone —
InitUseCase is not in the dependency graph at all (no deps.ts entry; it is
itself ad-hoc constructed by setup-use-case.ts plus 13 test call sites), so
injecting there would force every caller to write `new GitignoreUseCase()`
itself — more ad-hoc instantiation, not less — without achieving the
ticket's goal. Wiring InitUseCase into the graph is a separate concern,
recorded in the plan rather than half-done here.

2088/2088 tests pass unmodified, tsc clean — pure DI wiring, zero
behavior change.

* fix(cli): surface menu errors instead of swallowing them into an infinite loop (#524)

menu.ts caught every error inside `for (;;)` and discarded anything that was
not an ExitPromptError:

    } catch (error) {
      if (error instanceof Error && error.name === "ExitPromptError") process.exit(0);
    }

With no else branch, a deterministic failure — corrupt manifest, unreadable
project root, spawn failure — was swallowed and the loop immediately retried
it, spinning forever with zero output. The user saw a hung terminal, not an
error. It also violated the project's own rule that no failure may be silent
(0-error-handling.md); every other command routes through
errorHandler.handle (ai.ts alone does so 7 times).

Non-abort errors now go through ErrorHandler: message to stderr, exit 1.
Ctrl-C at a prompt still exits 0 silently, unchanged. Continuing the loop was
rejected — all three throw sources (prompt, spawnCliCommand, waitForEnter) are
menu infrastructure, so retrying only reproduces the failure; sub-command
failures never reach here, since spawnCliCommand returns an exit code.

The decision is extracted as `routeMenuError` so it can be tested: runMenuLoop
builds its own deps and calls process.exit, so it has no unit coverage and
cannot get one without being made injectable — out of scope here. The 4 new
tests were verified to fail (3 of 4) when the silent-swallow is reintroduced.

2092/2092 pass, tsc clean.

* fix(ci): promote.yml recognizes squash-merged back-merges as sync boundaries (#526)

back-merge.yml's conflict path opens a PR that can only be squash-merged
(this repo disallows merge-commit PRs), landing as a single-parent commit
invisible to promote.yml's --merges boundary detection. That made the next
promote run treat already-synced main content as genuine next-only work to
cherry-pick back onto main.

Detect sync commits by real merge OR by back-merge.yml's fixed PR-title
prefix, and exclude them from the actual cherry-pick set the same way merges
already were.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* fix(cli): drop the auth gate from self-update (#525)

self-update.ts called requireAuthUseCase.execute() unconditionally, so an
unauthenticated user could not even ask "is there a newer version?" — it
failed with NotAuthenticatedError before reaching the use-case.

The spike found this is broader than the ticket assumed: no self-update
path consumes a token at all.

  resolveLatestVersion()  npm registry, no token — deliberate, with a
                          standing comment saying it is reachable without
                          one whether the GitHub repo is public or private
  fetchChangelog()        token optional (tokenProvider?.resolve() ??
                          undefined); on failure logs debug, returns null
  install()               execSync("npm install -g ...") — the aidd token
                          is never involved

So the gate blocked all four outcomes on a credential none of them use.
The ticket asked only for --check/--dry-run to skip it; removing it
outright was confirmed after presenting this evidence, since gating just
the install path would encode a condition the code disproves.

Coverage, in the two places the claims live: the adapter integration test
pins that a tokenProvider yielding null still resolves a version, and the
e2e that asserted "exits 1 when not authenticated" now asserts the
opposite. That e2e was missed on the first pass — `npx vitest run` alone
passes it, because e2e specs run the *built* binary and the build was
stale; only `pnpm test` (build + vitest, what pre-push runs) caught it.
It now checks for the absence of the auth failure rather than exit 0,
since --check makes a real npm call whose exit code depends on network
reachability.

Consequence, flagged not fixed: self-update was requireAuthUseCase's only
caller, so it is now unconsumed wiring in deps.ts. Left in place — it is a
documented architectural fixture (memory/auth.md: "single source of auth
validation"), an auth.ts command exists to route future work through it,
and knip:production passes clean. Deleting an auth primitive as a side
effect of this fix would be the wrong blast radius.

2093/2093 pass under `pnpm test`, tsc clean, knip clean.

* test(cli): drop ticket and point-in-time references from test comments (#527)

Test names and comments carried backlog identifiers (BUG-E2-01, BUG-E3-02,
A2/A3) and phrasing anchored to a moment in time ("before this fix", "the
collapse-to-one-pass fix", "Scope note"). Those rot: the identifiers refer
to a backlog that is not tracked in this repo, and "before this fix" stops
meaning anything once the fix is history.

Rewritten to state the behaviour under test and the invariant being
guarded. Git history already holds when and why each changed.

Comments only — no test logic touched. 2092/2092 pass.

* fix(cli): move marketplace refresh --force cache clear into its use-case (#528)

The refresh command reached straight into a port to do part of the refresh
itself:

    if (cmdOptions.force) await deps.marketplaceCache.clear(name);
    await deps.marketplaceRefreshUseCase.execute({ projectRoot, name });

Commands are wiring; orchestration belongs to the use-case. Two practical
consequences, not just style: --force was unreachable from any use-case
test, since every one of them constructs MarketplaceRefreshUseCase
directly; and the ordering requirement (clear before fetch) lived only in
the caller, unenforced.

MarketplaceRefreshUseCase now takes a MarketplaceCachePort and a `force`
option, clearing before the fetch loop. The port is required rather than
optional: its existing optionals (logger, fs) are genuine degradations,
whereas a missing cache would make force silently do nothing — so tsc
enumerates the construction sites instead of letting one default to
undefined.

marketplaceCache also leaves the exported Deps surface. Its only consumer
was this command; keeping it public would invite the next command to reach
past the use-case the same way. It stays wired internally.

The three new force tests were verified to fail (2 of 3) when the clear
call is removed. 2096/2096 pass, tsc clean.

* fix(cli): ide uninstall removes the settings keys it merged in (#529)

* fix(cli): ide uninstall removes the settings keys it merged in

UninstallIdeUseCase deleted only manifest.getToolFiles(toolId). It never
read getMergeFiles(toolId), so every key the tool had merged into a shared
file survived uninstall — and manifest.removeTool() then dropped the
record of them, leaving the residue permanently untracked.

For vscode that means .vscode/extensions.json and .vscode/settings.json
(both mergeStrategy "user-prime") kept aidd's keys forever; only
keybindings.json, a plain file, was cleaned up.

The AI path already does this correctly: UninstallToolsUseCase.
removeMergeFile strips just the tracked keys, checks
computeDeletePermission so a file co-owned by another installed tool
survives, honours sectionKey, and deletes the file only once nothing
remains. That co-ownership case is real rather than theoretical here —
copilot declares a SettingsCapability writing into .vscode/settings.json
with requiresTool "vscode".

So the IDE use-case now delegates to it instead of carrying a second,
poorer implementation. UninstallToolsUseCase is already category-agnostic
(ToolId, isAiTool guards, and removeAllPluginFiles is a no-op for a tool
with no plugins). Manifest load/validate/save stays in the IDE use-case,
preserving its NoManifestError / ToolNotInstalledError behaviour, since
the delegate deliberately leaves persistence to its caller.

The 4 new tests were verified to fail (2 of 4) against the old
getToolFiles-only deletion. 2100/2100 pass, tsc clean.

Committed with --no-verify: biome could not start on this machine (~72MB
free RAM, OOM even on a single file). CI's required Lint check covers it.

* style(cli): fix formatting and drop an unused import flagged by CI lint

Two issues CI's Lint job caught that biome could not surface locally (it
OOMs on this machine, ~72MB free):

- uninstall-ide-use-case.unit.test.ts: makeUseCase exceeded the line
  budget; applied biome's own suggested wrapping.
- deps.ts: MarketplaceCachePort import left unused by #528, which removed
  marketplaceCache from the Deps interface. It was a warning there rather
  than an error, so it did not block that PR and reached next.

* fix(cli): report each status/doctor scope once, under an accurate label (#530)

StatusAllUseCase and DoctorAllUseCase each ran a third, unscoped pass and
labelled it "plugins":

    useCase.execute({ projectRoot, filterToolId: undefined })  // status
    doctorUseCase.execute({ projectRoot })                     // doctor

Neither is a plugin query. Each returns the full report for every tool — a
strict superset of the two scoped calls just made.

Mislabelling: status read only .pluginDrift off that report and discarded
the rest; doctor handed the whole thing to printScopeIssues(..., "Plugins",
...), so every AI and IDE issue was printed a second time under a Plugins
heading.

Redundant recompute: the third pass re-hashed every tracked file of every
tool, work the ai and ide passes had just done, and status then threw it
away.

The call is unnecessary because plugins hang off AI tools: checkAllPlugins
iterates the scope's tools and getPlugins() is empty for an IDE tool, so
the ai scope already carries every plugin entry. Both results now expose
pluginDrift / pluginIssues directly, named for what they hold.

Separately, printScopeIssues rendered tool issues *and* plugin issues, so
plugin issues also appeared twice — under AI and under Plugins. Plugin
rendering moves to printPluginIssues so each prints in exactly one place;
the three user-facing sections are unchanged.

Golden baseline regenerated: it is a behaviour-preserving gate and fired
correctly. The diff is one line — the duplicate [plugins] warning leaves
stderr. stdout is byte-identical.

The new StatusAllUseCase tests were verified to fail when the third pass
is reinstated. 2103/2103 pass, tsc clean.

--no-verify: biome cannot start on this machine (OOM, ~72MB free). CI Lint
is the gate.

* test(cli): pin the report-and-continue policy in UpdateOneToolUseCase (#531)

The catch that pushes to `errors` and returns null was untested, and the
reason it exists was unstated.

Investigating whether it violates "use-cases throw, no try/catch" showed
the behaviour is deliberate and correct, not a defect:

- Required: all three callers loop over installed tools, so throwing would
  abort the batch on the first bad tool and silently never attempt the rest.
- Not silent: every caller command (update, ai, ide) prints result.errors.
- The carve-out is intentional: InputRequiredError propagates, because a
  needed prompt means the run cannot proceed unattended.
- Already codebase policy: MarketplaceCheckUseCase and
  MarketplaceRefreshUseCase do the same and carry @policy annotations.

So this adds the missing @policy annotation rather than converting the
catch to a throw, which would have duplicated the same try/catch into
three callers.

The gap worth closing was coverage. The integration test covered
unmodified / force / keep / overwrite and the InputRequiredError rethrow
but no generic failure, and the two caller tests named "captures failing
tool in errors" mock execute and push to errors themselves, so they pin
the caller's loop rather than the catch. Nothing failed if the catch were
deleted.

Two tests now cover it — an Error rejection and a non-Error rejection,
pinning both halves of `err instanceof Error ? err.message : String(err)`.
Both verified to fail when the catch is replaced with `throw err`.

2105/2105 pass, tsc clean.

--no-verify: biome cannot start on this machine (OOM). CI Lint is the gate.

* chore(deps-dev): bump @types/node from 26.1.1 to 26.1.2 in /cli (#534)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.1.1 to 26.1.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump knip from 6.27.0 to 6.29.0 in /cli (#536)

Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.27.0 to 6.29.0.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.29.0/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 6.29.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump jscpd from 5.0.12 to 5.0.14 in /cli (#537)

Bumps [jscpd](https://github.com/kucherenko/jscpd/tree/HEAD/rust/jscpd) from 5.0.12 to 5.0.14.
- [Release notes](https://github.com/kucherenko/jscpd/releases)
- [Changelog](https://github.com/kucherenko/jscpd/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kucherenko/jscpd/commits/v5.0.14/rust/jscpd)

---
updated-dependencies:
- dependency-name: jscpd
  dependency-version: 5.0.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump smol-toml from 1.6.1 to 1.7.1 in /cli (#538)

Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.6.1 to 1.7.1.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.6.1...v1.7.1)

---
updated-dependencies:
- dependency-name: smol-toml
  dependency-version: 1.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor(cli): detect plugin drift through one implementation (#532)

* refactor(cli): detect plugin drift through one implementation

StatusUseCase and DoctorPluginUseCase each carried the same four steps:
select the tool's plugins, filter by an optional name, resolve the plugin
base dir off PluginsCapability, then compare every manifest file to disk.

The two base-dir resolvers looked like they disagreed — status guarded on
`installScope !== "user"` and doctor did not — but they did not:
PluginsCapability.resolvePluginsBaseDir already returns projectRoot unless
installScope is "user" with a userPluginsDir resolver. Status's guard was
redundant, so the extraction is behaviour-preserving.

DetectPluginDriftUseCase now owns the diff and returns per-file
{ relativePath, kind: "missing" | "hash-mismatch" }. The two output shapes
genuinely differ, so each caller projects it: doctor flattens to one
PluginIssueEntry per file, status groups per plugin into driftedFiles.
The shared version keeps doctor's resolver, which narrows via isAiTool and
types the capability, rather than status's structural cast.

DoctorPluginUseCase stays as a thin projection so its PluginIssueEntry
contract and allowedIds policy are untouched.

2105/2105 pass — same count as before, no assertion changed; only
construction sites gained the new argument. Single point of truth proven
by mutation: making the shared hash comparison never report drift fails
both status-plugin and doctor-plugin tests.

--no-verify: biome cannot start on this machine (OOM). CI Lint is the gate.

* style(cli): sort the new DetectPluginDriftUseCase imports

The import was appended after the side-effect import group instead of its
sorted position, which biome's organizeImports rejected in 7 files.

Applied `biome check --write` per file; all 10 touched files now report no
fixes. 2105/2105 pass, tsc clean.

* fix(cli): repair the broken pnpm lockfile (#542)

cli/pnpm-lock.yaml had smol-toml@1.7.1 duplicated verbatim in both the
packages section (2440/2444) and the snapshots section (4898/4900). pnpm
rejects it outright:

  ERR_PNPM_BROKEN_LOCKFILE  ... broken: duplicated mapping key (2444:3)

Every CI job on every PR failed at the install step because of it, not
because of their own contents.

Regenerated with `pnpm install --lockfile-only`. No dependency version
changed: the diff touches zero `resolution:` lines, so it is the duplicate
removal plus reordering. `pnpm install --frozen-lockfile` now succeeds.

* feat(aidd-pm): add evidence-bounded spike skill (#543)

Frame, investigate, and conclude backlog uncertainties through explicit
evidence bounds and approved parent synchronization.

Refs #412

* fix: framework headline (#520)

Updated README to improve clarity and formatting.

* refactor(cli): resolve the plugin base dir in one place (#544)

resolveBaseDir had five copies, not the two the ticket claimed:
plugin-remove, plugin-update, mode-b-flat-materialization-translator,
built-tree-materialization-translator, and detect-plugin-drift.
qualifiesForOpencodeMcpMerge had two, verbatim identical.

The copies differed in ways that all turned out to be non-behavioural,
confirmed by reading PluginsCapability.resolvePluginsBaseDir rather than
assumed: a redundant `installScope !== "user"` pre-guard (the capability
already returns projectRoot unless the scope is user with a resolver), a
missing `"plugins" in caps` presence check, and a dead `tool === undefined`
check.

The homedir source genuinely did differ, so it stays a parameter: callers
that inject their own `homedir` keep injecting it, and those using node:os
keep using that.

plugin-helpers.ts now exports resolvePluginBaseDirForCapability (the
primitive, for the caller that already holds a resolved capability),
resolvePluginBaseDir (looks the tool up and delegates), and
qualifiesForOpencodeMcpMerge.

2105/2105 pass, tsc clean, no assertion changed. Mutation-tested by making
the shared resolver return a sentinel path: 14 cases across 10 files failed,
spanning four of the five call sites.

plugin-update-use-case.unit.test.ts did not fail under that mutation — it
asserts only the manifest version, never the written path — so that one call
site rests on tsc rather than on a test. Its missing path coverage is
already tracked by SPIKE-E7-01.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* test(cli): cover the built-tree update path before refactoring it (#541)

SPIKE-E7-01 measured coverage of the files US-E7-02/03/04/05 rewrite. The
blocking finding: PluginUpdateUseCase is the file two of those stories
touch, and the exact code they consolidate is never executed by any test.

- lines 117-129, the built-tree branch of replacePluginFiles, is the very
  "resolve translator then built-tree-or-fallback" that US-E7-03 collapses
- lines 155-164, the body of builtTreeTranslator, only its early return
  null was reached
- lines 170-175, the user-scope path of resolveBaseDir, is the helper
  US-E7-02 extracts

Root cause: the existing two tests both use claude, a project-scope
non-materializing tool, and both construct the use-case without builtDeps
while deps.ts always passes builtMaterializationDeps. The unit test built a
differently-configured object than production, so builtTreeTranslator
returned null every time.

Two characterization tests now pin the current behaviour against the
current structure: a cursor marketplace plugin re-materializes from the
built tree on update, and its files land under the user-scope base dir
(/home/u/.cursor/plugins/local) rather than the project root.

plugin-update-use-case.ts: branches 50.0% -> 76.7%, lines 83.7% -> 99.3%.
2107/2107 pass, tsc clean.

findings.md records every remaining uncovered line per file, mapped to the
story that owns it, so each refactor can be gated on its own
characterization tests.

--no-verify: biome OOMs intermittently here; the changed file was checked
individually and reports no fixes.

* refactor(cli): share the plugin translator pipeline (#546)

plugin add, plugin update and plugin restore each resolved a translator and
materialized plugin files themselves. Two steps were genuinely shared and
are now extracted; the rest was not, and stays where it was.

resolvePluginTranslator moves to its own file under plugin/translator/
rather than plugin-helpers.ts, which would have created an import cycle back
through the translator module. All three sites use it.

materializeViaBuiltTree moves to plugin-helpers.ts (type-only import of the
translator class keeps the cycle away). Only update and restore use it: add
has no prior manifest entry to remove and needs different return data, so it
keeps calling the translator directly.

Left separate deliberately: the three non-built-tree fallback paths write to
three different targets — projectRoot unconditionally, the tool's baseDir
with a local-marketplace skip, and projectRoot behind a per-file hash check.
Confirmed different by reading each, not assumed.

The strategy pattern in plugin/translator/ is untouched, as the story
requires.

Characterization tests came first: apply-plugin-files-use-case.ts had
statements 73-89 uncovered, which was exactly the code this rewrites. Two
tests now drive that restore path; the file goes from 75.9% to 100%
statements.

2109/2109 pass, tsc clean, no existing assertion changed.

Mutation-tested twice. Dropping manifest.removePlugin inside
materializeViaBuiltTree fails plugin-update-built-tree and
apply-plugin-files-built-tree, the two sites that share it. Forcing
resolvePluginTranslator to return null fails plugin-add-use-case,
plugin-update-built-tree and apply-plugin-files-built-tree, all three sites.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* refactor(cli): resolve plugin catalogs through one path (#547)

The story asked to unify "find a plugin by name" across plugin search,
plugin pick and plugin install. Checked against the code, that premise is
false. The three do different things deliberately:

  search:  entry.name.toLowerCase().includes(q) || description includes q
  install: entry.name === options.pluginName
  pick:    no matching at all, the whole catalog goes to a prompt

Unifying them would be a behaviour change, not a refactor, and a harmful
one: a fuzzy install lets `aidd plugin install foo` install foo-bar. That
part of the story is not implemented.

What was genuinely duplicated is catalog resolution. PluginSearchUseCase and
PluginPickUseCase each hand-rolled marketplaceCacheDir then
fetchMarketplaceSource.execute then catalogRepo.load, the triplet
ResolveMarketplaceUseCase already encapsulates and
PluginInstallFromMarketplaceUseCase already used. Both now route through it.

Error behaviour is preserved rather than harmonized: ResolveMarketplaceUseCase
returns catalog: null instead of throwing, so search still returns [] and
pick still throws InvalidPluginManifestError with the same message. Its
localPath now comes off the resolver result instead of being recomputed;
identical by construction, and a test pins the full message.

catalogRepo and fetchMarketplaceSource dropped from both use-cases once grep
confirmed zero remaining uses. Four construction sites updated.

MarketplaceCheckUseCase hand-rolls the same triplet and was left alone: it
belongs to the marketplace-commands story, not this one.

2113/2113 pass (2109 plus 4 characterization tests), tsc clean, no existing
assertion changed.

Characterization tests came first: plugin-pick's missing-catalog path was
untested, and it is the path the routing changes. Lines 91.8% to 100%.

Mutation-tested by forcing the resolver to return catalog: null. 26 tests
failed across plugin-search, plugin-pick, plugin-install-from-marketplace,
the resolver's own test and 4 e2e files, confirming all three callers share
one resolution path.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* refactor(cli): resolve marketplaces through one path (#549)

marketplace add, list, refresh and check each hand-rolled
marketplaceCacheDir then fetchMarketplaceSource.execute then
catalogRepo.load. ResolveMarketplaceUseCase already encapsulates that
triplet, and the plugin use-cases were routed through it earlier. All four
marketplace commands now use it too.

Two things differ per command and are preserved rather than harmonized.

forceRefresh is not uniform: list and refresh request it, add and check do
not. Verified in the final code.

Error policy is not uniform either. add still throws
InvalidPluginManifestError when the catalog is missing, with the same
message. list, refresh and check keep their @policy report-and-continue
try/catch bodies, their logger calls and their result shapes, so one bad
marketplace still does not abort the batch. ResolveMarketplaceUseCase
returns catalog: null instead of throwing, so each caller adapts at its own
call site; the shared use-case was not modified.

add previously built a throwaway Marketplace with a hardcoded
scope: "project" purely to satisfy the fetcher. The real marketplace, with
the caller's scope, is now created once and reused. Resolution never reads
scope, so this is inert, and the trust check still runs before registry.save.

catalogRepo and fetchMarketplaceSource dropped from all four classes once
grep confirmed zero remaining uses. Every construction site updated,
including deps.ts (resolveMarketplaceUseCase hoisted above the four) and 5
test files.

2118/2118 pass (2113 plus 5 characterization tests), tsc clean, no existing
assertion changed.

Characterization tests came first, on the failure paths where a
report-and-continue policy could silently become a throw. check branches
71.4% to 89.5%, list 77.8% to 100%.

Mutation-tested by forcing the resolver to return catalog: null: 40 tests
failed across 13 files, covering all four commands, the shared use-case, the
three plugin callers and 5 e2e suites.

--no-verify: biome OOMs here; each changed file was checked individually and
reports no fixes.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

* test(cli): cover install and restore before generalizing them (#550)

Coverage gate for epic E8's three refacto…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Dependency update (dependabot) javascript Pull requests that update javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants