Skip to content

feat(cli): add ask skills namespace (list/install/remove)#87

Merged
amondnet merged 11 commits into
mainfrom
amondnet/solar-boysenberry
Apr 14, 2026
Merged

feat(cli): add ask skills namespace (list/install/remove)#87
amondnet merged 11 commits into
mainfrom
amondnet/solar-boysenberry

Conversation

@amondnet

@amondnet amondnet commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces ask skills — a sibling namespace to ask docs that surfaces and installs producer-side skills (agent-facing instruction bundles) shipped by libraries under a top-level skills/ directory.

  • ask skills <spec> / ask skills list <spec> — prints candidate skill paths (one per line) from node_modules/<pkg>/ and the cached GitHub checkout.
  • ask skills install <spec> — vendors each discovered skill into .ask/skills/<spec-key>/<skill-name>/ and creates relative POSIX symlinks into every selected coding-agent directory (.claude/skills/, .cursor/skills/, .opencode/skills/, .codex/skills/). Detects agents automatically; prompts via consola.prompt multiselect when >1 is present. Supports --agent, --force, --no-fetch.
  • ask skills remove <spec> — reverses install deterministically via .ask/skills-lock.json; only unlinks symlinks whose target matches the vendored path.

The lock file is the single source of truth for removal so user-authored skills are never touched. Re-install is idempotent. Ignore markers in .gitignore, .prettierignore, sonar-project.properties, and .markdownlintignore now cover .ask/skills/ and .ask/skills-lock.json via the existing # ask:start ... # ask:end block.

Refs #86

Scope

  • New walker: packages/cli/src/commands/find-skill-paths.ts
  • New skills module: packages/cli/src/skills/{spec-key,lock,agent-detect,vendor,symlinks}.ts
  • New commands: packages/cli/src/commands/skills/{index,list,install,remove}.ts
  • Wired into packages/cli/src/index.ts
  • Extended packages/cli/src/ignore-files.ts
  • README.md section + product.md sync

Test plan

  • Unit tests for walker, spec-key, lock, agent-detect, vendor, symlinks, ignore-files
  • Command-level tests for list (mocked ensureCheckout)
  • Integration test: install → idempotent reinstall → remove on a synthetic checkout with .claude/skills/
  • Registration test: all subcommands + flags exposed via main.subCommands
  • ESLint clean (project-wide; 1 pre-existing unrelated error outside this PR)
  • 66/66 skills-related tests pass
  • Manual smoke on a real ask skills install github:...@v... in a project with .claude/ + .cursor/
  • Windows native junction fallback (deferred)

Design notes

  • Why symlinks, not copies: agents share one .ask/skills/<key>/ vendored copy; avoids byte duplication and keeps versions in sync.
  • Why lock file: scanning agent dirs at remove time would either over-delete user skills or under-delete (if the agent set changes). Lock is authoritative.
  • Why .ask/skills/<spec-key>/<name>/ layout: name collisions between libraries resolve via the spec-key namespace; remove is a single rm -rf.
  • <spec-key> encoding: / and :__, @ kept. Grep-friendly, reversible.

See the full plan at .please/docs/tracks/active/ask-skills-command-20260414/plan.md.


Summary by cubic

Adds a new ask skills CLI namespace to list, install, and remove producer-side skills from libraries. Skills are vendored into .ask/skills/, symlinked into detected agents, and tracked via a lock file; also fixes ignore patching for skills-only projects. Aligns with Linear #86.

  • New Features

    • ask skills [list] <spec> prints candidate skill paths from node_modules/<pkg>/ and the cached checkout.
    • ask skills install <spec> vendors into .ask/skills/<spec-key>/<skill-name>/ and symlinks into detected agents (.claude/skills/, .cursor/skills/, .opencode/skills/, .codex/skills/). Supports --agent, --force, --no-fetch.
    • ask skills remove <spec> reverses install via .ask/skills-lock.json; only unlinks symlinks that point to the vendored target.
    • Auto-detects agents and prompts if more than one is present; ask skills <spec> is shorthand for skills list.
    • Ignore markers cover .ask/skills/ and .ask/skills-lock.json. Uses relative POSIX symlinks; Windows junction fallback deferred.
  • Bug Fixes

    • Ignore file management now treats skills usage as opt-in. If .ask/skills/ or .ask/skills-lock.json exists, .gitignore and friends are patched even without an ask.json, preventing vendored skills from being committed.

Written for commit 038c926. Summary will update on new commits.

Verification Checklist

  • bun run --cwd packages/cli lint — zero errors
  • bun run --cwd packages/cli test — 67/67 skills tests pass
  • Integration test covers install → idempotent reinstall → remove on synthetic checkout
  • Integration regression test covers skills-only (no ask.json) ignore-file patching
  • AC-1 through AC-7 each covered by at least one automated test
  • Manual smoke on a real ask skills install github:...@v... against a project with both .claude/ and .cursor/ markers
  • Windows native junction fallback (deferred to follow-up track)

Parallels findDocLikePaths with a /skill/i regex so ask skills can
surface producer-side skill directories shipped by libraries.

Task: T001
encodeSpecKey rewrites / and : as __ so the spec triple (ecosystem,
name, version) becomes a flat, grep-friendly directory name. @ is
preserved to keep scoped npm packages human-readable.

Task: T002
lock.ts: .ask/skills-lock.json schema + atomic IO (readLock, upsertEntry,
removeEntry, writeLockAtomic) that backs remove/reinstall determinism.

agent-detect.ts: detectAgents + resolveAgentNames covering Claude Code,
Cursor, OpenCode, Codex. AGENTS.md alone is not an install target.

Tasks: T003, T004
- runSkillsList + skillsListCmd mirror ask docs using findSkillLikePaths.
- vendorSkills copies producer-side skill dirs into .ask/skills/<specKey>/
  via a staging dir + atomic rename. removeVendorDir wipes the tree.
- linkSkill creates relative POSIX symlinks, refuses to overwrite real
  dirs without --force, and is a no-op when the same link already exists.
  unlinkIfOwned only unlinks symlinks whose target matches the vendored
  path, protecting user-authored skills.
- ignore-files.ts now marks .ask/skills/ and .ask/skills-lock.json as
  vendored in .gitignore, .prettierignore, sonar-project.properties,
  and .markdownlintignore.

Tasks: T005, T006, T007, T008
Implements the install/remove orchestration and wires the new skills
namespace into the ask CLI.

- runSkillsInstall: resolve via ensureCheckout → walk via
  findSkillLikePaths → filter each /skill/i parent via ^skills?$/i →
  enumerate children as individual skills → vendor → detect agents →
  prompt if >1 → symlink → update .ask/skills-lock.json → patch ignore
  files. Supports --force, --agent CSV, --no-fetch.
- runSkillsRemove: reverse via the lock file. Only unlinks symlinks
  whose target matches the vendored path so user-authored skills are
  never touched.
- skillsCmd: citty parent that registers list/install/remove and
  short-circuits `ask skills <spec>` to the list behaviour.
- Wired into main subCommands alongside src/docs/cache.
- Integration test covers happy-path install, idempotent re-install,
  remove teardown, missing-entry error, and no-agent-detected error.
- Registration test asserts the four subcommand surfaces and their
  flag shapes.
- README gets an 'ask skills' section under CLI usage documenting the
  vendored + symlinked install model and the v1 POSIX-only caveat.

Tasks: T009, T010, T011, T012, T013
@codecov

codecov Bot commented Apr 14, 2026

Copy link
Copy Markdown

…nly projects

Review flagged an Important issue (PR #87 validation): manageIgnoreFiles
early-returned when ask.json was absent, so a user who ran only
'ask skills install' without any 'ask docs' state never had
.ask/skills/ or .ask/skills-lock.json added to .gitignore. That
violated FR-11 and AC-6 and would have quietly committed vendored
bytes.

- Replace the ask.json-only gate in manageIgnoreFiles with a new
  hasAskOptIn() that also accepts a .ask/skills-lock.json or
  .ask/skills/ presence signal. Either entry point (docs or skills)
  now qualifies as opt-in.
- Add integration regression test covering the skills-only path
  without ask.json. It asserts .gitignore picks up both .ask/skills/
  and .ask/skills-lock.json after the first runSkillsInstall — the
  exact shape that would have caught the original bug.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 14, 2026

Copy link
Copy Markdown

Deploying ask-registry with  Cloudflare Pages  Cloudflare Pages

Latest commit: 038c926
Status: ✅  Deploy successful!
Preview URL: https://02bcafc6.ask-registry.pages.dev
Branch Preview URL: https://amondnet-solar-boysenberry.ask-registry.pages.dev

View logs

@amondnet
amondnet marked this pull request as ready for review April 14, 2026 15:59
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Apr 14, 2026
@amondnet amondnet mentioned this pull request Apr 14, 2026
16 tasks
@dosubot dosubot Bot added status:in-progress Currently being worked on type/feature New feature type:test Testing improvements or additions labels Apr 14, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 4 files (changes from recent commits).

Requires human review: Significant new feature (ask skills) with complex filesystem logic (symlinks, lock files) and a large line count (1960), requiring human architectural review.

@amondnet amondnet self-assigned this Apr 14, 2026
@amondnet
amondnet merged commit f19e161 into main Apr 14, 2026
5 checks passed
@github-actions github-actions Bot mentioned this pull request Apr 14, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 29 files

Requires human review: Significant new feature (1900+ lines) adding filesystem mutations, symlinking logic, and lock file management; requires human validation of the architectural implementation.

Architecture diagram
sequenceDiagram
    participant User as CLI User
    participant CLI as Skills Command Logic
    participant Res as Resolver (ensureCheckout)
    participant FS as Local Filesystem (.ask/)
    participant Agents as Agent Dirs (.claude, .cursor, etc)

    Note over User,Agents: NEW: ask skills install [spec] Flow

    User->>CLI: ask skills install <spec>
    CLI->>Res: resolve and fetch spec
    alt Cache Miss & --no-fetch
        Res-->>CLI: NoCacheError
        CLI-->>User: Exit 1
    else Success
        Res-->>CLI: checkout directory path
    end

    CLI->>CLI: NEW: findSkillLikePaths(checkout)
    
    CLI->>CLI: NEW: detectAgents(projectDir)
    alt Multiple Agents Found
        CLI->>User: consola.prompt (multiselect)
        User-->>CLI: selected agents (e.g., claude, cursor)
    else Zero Agents Found
        CLI-->>User: Error: No agents detected
    end

    rect rgb(240, 240, 240)
        Note over CLI,FS: Atomic Vendoring
        CLI->>FS: NEW: vendorSkills()
        FS->>FS: Create .ask/skills/<spec-key>/.tmp
        FS->>FS: Copy source skills to .tmp
        FS->>FS: Rename .tmp to canonical path
    end

    loop For each Skill and Selected Agent
        CLI->>Agents: NEW: linkSkill()
        Agents->>Agents: Create relative POSIX symlink to .ask/skills/
    end

    CLI->>FS: NEW: writeLockAtomic(.ask/skills-lock.json)
    CLI->>FS: CHANGED: manageIgnoreFiles()
    Note right of FS: Patches .gitignore, .prettierignore, etc.
    
    CLI-->>User: Success Summary

    Note over User,Agents: NEW: ask skills remove [spec] Flow

    User->>CLI: ask skills remove <spec>
    CLI->>FS: readLock()
    FS-->>CLI: Lock entries (spec, skills, agents)
    
    loop For each recorded Symlink in Lock
        CLI->>Agents: NEW: unlinkIfOwned()
        Note right of Agents: Only unlinks if target matches vendor path
    end

    CLI->>FS: NEW: removeVendorDir()
    CLI->>FS: writeLockAtomic() (remove entry)
    CLI-->>User: Success message
Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files. status:in-progress Currently being worked on type/feature New feature type:test Testing improvements or additions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant