Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

- Hardened per-registry skill updates with direct cache-filter coverage, available-registry hints, and unique repository-name shorthand.

## [0.52.0] - 2026-08-18

- [4690e3e](https://github.com/codeaholicguy/ai-devkit/pull/172) Simplified obsolete test cleanup guidance in the simplify-implementation skill.
Expand Down
62 changes: 62 additions & 0 deletions docs/ai/design/2026-08-18-feature-skill-update-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
phase: design
title: System Design & Architecture
description: Resolve and test cached registry update selection
---

# System Design & Architecture

## Architecture Overview

```mermaid
flowchart LR
CLI[skill update optional-id] --> SR[SkillRegistry.updateSkills]
Cache[(owner/repo cache)] --> Discover[discover cached registry IDs]
SR --> Discover
Discover --> Select{input form}
Select -->|none| All[all cached registries]
Select -->|owner/repo| Exact[exact match]
Select -->|repo| Unique[unique repo-name match]
Select -->|zero or ambiguous| Missing[NotFoundError + available IDs]
All --> Update[updateRegistry]
Exact --> Update
Unique --> Info[ui.info resolution] --> Update
Update --> Summary[UpdateSummary]
```

The existing command and public method signatures stay unchanged. `SkillRegistry.updateSkills()` first discovers all cache candidates, then resolves the optional selector, then updates only selected candidates and derives the existing summary.

## Data Models and API

- Cached candidate: `{ path: string; id: string }`, where `id` is `owner/repo`.
- Public API remains `updateSkills(registryId?: string): Promise<UpdateSummary>`.
- Exact selectors match a full ID. Owner-less selectors compare against the substring after `/` and resolve only for one candidate.
- Failure details retain the requested `registryId`; the message appends `Available: <sorted IDs>.`.

## Component Breakdown

- `packages/cli/src/lib/SkillRegistry.ts`: candidate discovery, selector resolution, update loop, summary.
- `packages/cli/src/__tests__/lib/SkillRegistry.test.ts`: direct contract tests with a temporary home/cache and mocked Git boundary.
- `packages/cli/README.md`: all, exact, and shorthand usage.
- `CHANGELOG.md`: unreleased hardening entry following the current top-of-file convention.

## Design Decisions

- Discover before filtering so errors can enumerate all available candidates and shorthand can detect ambiguity.
- Sort available IDs for deterministic UX and assertions; update ordering may follow the same sorted list.
- Keep shorthand resolution local to cached candidates because the update operation cannot update an uncached registry.
- Inform only on successful shorthand resolution; exact IDs preserve existing output.
- Prefer real temporary directories with mocked `ensureGitInstalled`, `isGitRepository`, and `pullRepository` boundaries.

### Alternatives considered

- Merging configured registries was rejected because configuration does not guarantee a cache exists.
- Picking the first shorthand match was rejected because filesystem order is not a safe disambiguation rule.
- Moving resolution into the command was rejected because it would leave direct `SkillRegistry` callers with a different contract.

## Non-Functional Requirements

- Selection is linear in cached registry count and performs no additional network work.
- No cache contents are created, deleted, or rewritten by resolution.
- Errors occur before any registry pull for invalid or ambiguous selectors.
- Existing callers and `UpdateSummary` consumers remain source-compatible.
53 changes: 53 additions & 0 deletions docs/ai/implementation/2026-08-18-feature-skill-update-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
phase: implementation
title: Implementation Guide
description: Implementation record for registry update hardening
---

# Implementation Guide

## Development Setup

- Worktree: `feature-skill-update-registry`.
- Task tracing unavailable: `npx ai-devkit@latest task list --name skill-update-registry --json` returns `unknown command 'task'`.
- Project-local built-in skill installation failed twice at `.agents/skills`; global skill instructions were used as authorized by the brief.

## Code Structure

- `packages/cli/src/lib/SkillRegistry.ts`: cached-candidate discovery and optional selector resolution.
- `packages/cli/src/__tests__/lib/SkillRegistry.test.ts`: direct public-contract coverage with an isolated temporary cache.
- `packages/cli/README.md` and `CHANGELOG.md`: usage and release documentation.

## Implementation Notes

- Candidate discovery now completes before filtering. IDs are sorted, enabling deterministic available-ID errors.
- Full `owner/repo` input still requires an exact match.
- Input without `/` compares with each cached repository directory name. Exactly one match resolves and emits `ui.info`; zero or multiple matches use the improved `NotFoundError`.
- Invalid selection fails before `updateRegistry()` and therefore before any pull.
- Update execution and `UpdateSummary` derivation remain unchanged.

## TDD Evidence

- Red: focused run produced 4 expected failures for missing available IDs and shorthand resolution; 4 baseline-contract tests passed.
- Green: focused run passed 8/8 tests.
- Regression proof: removing the production change reproduced the same 4 failures; restoring it returned 8/8 green.
- Focused coverage passed; whole-file coverage is 64.42% statements / 62% branches because unrelated fetch/clone paths are outside this hardening scope. All added selection branches are exercised.

## Error Handling and Safety

- `NotFoundError` retains code `NOT_FOUND` and requested `registryId` details.
- Available IDs reflect cached directories only and are sorted for stable output.
- No persistent cache data is mutated by the selector; tests clean their process-isolated temporary home.

## Design Alignment

Implementation matches the design without public API, schema, cache-layout, or command-parser changes. No security-sensitive inputs, credentials, migrations, or irreversible operations were introduced.

## Final Validation

- `npm ci`: exit 0; 1001 packages installed (npm reported 4 pre-existing high-severity audit findings).
- `npm run build`: exit 0; all 6 workspace project builds passed.
- `npx ai-devkit@latest lint` and `lint --feature skill-update-registry`: exit 0.
- `npm run lint`: exit 0; 6 project lints passed with 6 pre-existing unused-catch warnings and no errors.
- `npm test`: exit 0; 1,923 tests passed across 137 files and 6 projects.
- `node packages/cli/dist/cli.js skill update --help`: exit 0; optional `[registry-id]` documented.
64 changes: 64 additions & 0 deletions docs/ai/planning/2026-08-18-feature-skill-update-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
phase: planning
title: Project Planning & Task Breakdown
description: TDD plan for registry update hardening
---

# Project Planning & Task Breakdown

## Milestones

- [x] Milestone 1: Baseline update contract has direct `SkillRegistry` coverage.
- [x] Milestone 2: Error and shorthand UX is implemented through red-green-refactor cycles.
- [x] Milestone 3: Docs, coverage, full gates, and final review are complete.

## Task Breakdown

### Task 1: Dedicated baseline contract tests

- [x] Create an isolated `SkillRegistry.test.ts` fixture using a temporary cache.
- [x] Prove no-argument updates all candidates, exact IDs update one, non-Git candidates skip, and summaries count statuses correctly.
- Validation: focused Vitest run and assertions on Git/UI boundary calls.
- Dependencies: existing `SkillRegistry.updateSkills()` behavior.

### Task 2: Helpful not-found errors

- [x] **Red:** Assert unknown full IDs throw `NotFoundError` with sorted available IDs and perform no pulls.
- [x] **Green:** Discover candidates before selection and build the improved message.
- [x] **Refactor:** Remove duplicated selection/error formatting and rerun focused tests.
- Validation: focused test and changed-file coverage.

### Task 3: Owner-less shorthand

- [x] **Red:** Add unique, ambiguous, and zero-match repository-name scenarios.
- [x] **Green:** Resolve only a single repo-name match and emit `ui.info`; reuse the not-found path otherwise.
- [x] **Refactor:** Keep exact/full-ID and no-ID paths explicit and minimal.
- Validation: focused tests prove only the resolved registry pulls and invalid selectors pull none.

### Task 4: Documentation and release note

- [x] Document no-argument, exact-ID, and unique shorthand examples in CLI docs.
- [x] Add an unreleased changelog entry without altering released history.
- Validation: docs review and CLI help smoke test.

### Task 5: Lifecycle verification and review

- [x] Update implementation/testing docs with changed files and fresh evidence.
- [x] Run `npm ci` and `npm run build` before full gates.
- [x] Run focused coverage, lifecycle lint, full workspace gates, and final code review.
- [ ] Commit and push each completed phase, rebase on latest `origin/main`, revalidate, and open the requested PR.

## Dependencies and Sequencing

Task 1 establishes the harness. Tasks 2 and 3 follow strict red-green-refactor cycles. Task 4 follows green behavior. Task 5 is last and may send implementation back to Tasks 2-4 if review identifies a blocking gap.

## Risks & Mitigation

- Module-level `SKILL_CACHE_DIR` can leak the real home directory: mock `os.homedir()` before dynamic import and reset modules.
- Filesystem enumeration order can make messages flaky: sort IDs before formatting and asserting.
- Existing `SkillManager` tests may duplicate behavior through mocks: keep new tests on the `SkillRegistry` public API and use real temporary directories.
- Workspace hooks depend on built artifacts: run deterministic install and build before full validation or commits containing implementation.

## Progress Summary

All planned hardening, documentation, focused coverage, workspace gates, and final review are complete. Task tracing was unavailable because `npx ai-devkit@latest task ...` reports `unknown command 'task'`. No scope changes or blocking risks were discovered.
53 changes: 53 additions & 0 deletions docs/ai/requirements/2026-08-18-feature-skill-update-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
phase: requirements
title: Requirements & Problem Understanding
description: Harden the existing per-registry skill update contract
---

# Requirements & Problem Understanding

## Problem Statement

`ai-devkit skill update [registry-id]` and exact `owner/repo` filtering already work. The cache-level contract is not directly tested, unknown-registry errors do not identify valid choices, and users must always type the owner even when a repository name uniquely identifies one cached registry.

## Goals & Objectives

- Add dedicated `SkillRegistry` tests backed by an isolated temporary cache.
- Preserve no-argument updates of every cached registry and exact-ID updates of only the requested registry.
- Include sorted available registry IDs in unknown-registry errors.
- Resolve an owner-less repository name only when it matches exactly one cached registry and report the resolved ID through `ui.info`.
- Keep zero-match and ambiguous shorthand inputs as `NotFoundError` cases.
- Document exact and shorthand per-registry update forms and add a changelog entry.

### Non-goals

- Reimplementing the existing optional CLI argument or exact registry filter.
- Changing cache layout, cloning, pull behavior, summary structure, or command parsing.
- Choosing among ambiguous shorthand matches.

## User Stories & Use Cases

- As a user, I can update all cached registries with `ai-devkit skill update`.
- As a user, I can update one registry with its exact `owner/repo` ID.
- As a user, I can use a unique repository-name shorthand and see which full ID was selected.
- As a user who mistypes or supplies an ambiguous name, I see the available full registry IDs and no registry is updated.
- As a maintainer, I have direct regression tests for filtering, skipped non-Git directories, and summary counts.

## Success Criteria

- Dedicated tests prove all-cache, exact-ID, unique shorthand, ambiguous shorthand, zero-match, unknown full ID, non-Git skip, and summary-count behavior.
- Exact matching remains case-sensitive and uses the existing `owner/repo` cache layout.
- Available IDs are deterministic and comma-separated in `NotFoundError` messages.
- Unique shorthand emits an informational resolution message before updating only the resolved registry.
- Targeted tests, coverage, lifecycle lint, workspace lint/tests, and build pass.

## Constraints & Assumptions

- The cache is the source of truth for update candidates; configured but uncached registries are not listed.
- A shorthand is any supplied ID without `/`; its candidate key is the repository directory name.
- Cache entries are owner/repository directories. Non-directory entries are ignored and repository directories that are not Git repositories are counted as skipped.
- No material product questions remain; the brief fixes scope and behavior.

## Questions & Open Items

None. Per-registry updating is an established capability; this feature hardens its contract and UX only.
60 changes: 60 additions & 0 deletions docs/ai/testing/2026-08-18-feature-skill-update-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
phase: testing
title: Testing Strategy
description: Direct coverage for cached registry update selection and summaries
---

# Testing Strategy

## Test Coverage Goals

- Cover every new selector branch and preserve the existing full-ID/no-ID contract.
- Exercise real temporary cache directory discovery while mocking only Git and terminal boundaries.
- Target 100% coverage of changed `SkillRegistry` selection logic.

## Unit and Integration Tests

### `SkillRegistry.updateSkills`

- [x] No argument updates every cached registry.
- [x] Exact `owner/repo` updates only that registry.
- [x] Unknown full ID throws `NotFoundError` listing sorted available IDs before pulls.
- [x] Unique owner-less repo name resolves, emits `ui.info`, and updates only its full ID.
- [x] Ambiguous owner-less name throws and lists available IDs before pulls.
- [x] Zero-match owner-less name throws and lists available IDs before pulls.
- [x] Non-Git cache directories are skipped without a pull.
- [x] Mixed success, skip, and failure results produce correct totals and per-status counts.
- [x] Missing cache returns an empty summary without updates.

## Test Data and Isolation

- Override `os.homedir()` before importing `SkillRegistry` so its exported cache constant points into a per-test temporary directory.
- Seed `~/.ai-devkit/skills/<owner>/<repo>` directories with `fs-extra`.
- Mock `ensureGitInstalled`, `isGitRepository`, `pullRepository`, and terminal UI methods; do not run Git or network operations.
- Clean the temporary directory and restore mocks/modules after each test.

## Validation

- Focused red/green: `npx vitest run src/__tests__/lib/SkillRegistry.test.ts` from `packages/cli`.
- Focused coverage: `npx vitest run src/__tests__/lib/SkillRegistry.test.ts --coverage --coverage.include=src/lib/SkillRegistry.ts`.
- Required bootstrap before full gates: `npm ci`, then `npm run build` from repository root.
- Full gates: lifecycle lint, workspace lint, tests, and build using repository scripts.
- CLI help smoke test confirms the documented optional registry argument.

## Manual Testing

No live registry pull is required; the orchestrator already verified the baseline exact-ID behavior. Automated tests cover the hardening changes without mutating the user cache.

## Bug Tracking

Any failure that permits an invalid/ambiguous selector to pull a registry is blocking. Cosmetic output differences are blocking when they omit the resolved ID or available IDs required by the contract.

## Results

- Focused test: 9/9 passed after the final missing-cache scenario was added.
- Focused coverage: exit 0; all selector behaviors are covered (whole `SkillRegistry.ts`: 64.42% statements, 62% branches, including unrelated fetch/clone paths).
- Lifecycle lint: base and feature checks passed.
- Workspace lint: all 6 projects passed with warnings only.
- Workspace tests: 1,923 tests passed across 137 files and 6 projects.
- Workspace build: all 6 projects passed after a clean `npm ci`.
- CLI help smoke test: optional `[registry-id]` displayed.
7 changes: 7 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ ai-devkit lint --feature lint-command --json
# Install a skill
ai-devkit skill add <skill-registry> [skill-name]

# Update every cached skill registry
ai-devkit skill update

# Update one cached registry by its full ID or unique repository name
ai-devkit skill update codeaholicguy/ai-devkit
ai-devkit skill update ai-devkit

# List skills installed across known global environment paths
ai-devkit skill list --global

Expand Down
Loading
Loading