From 87749b6259c03de1304e8cc4fe3dc1a543417481 Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Thu, 16 Apr 2026 22:30:25 +0900 Subject: [PATCH 1/2] docs(skills): add ask skill with references for progressive disclosure Adds skills/ask/SKILL.md teaching agents the $(ask docs ...) / $(ask src ...) subshell idiom for resolving accurate doc paths at runtime. Three reference files under skills/ask/references/ provide on-demand depth: cache.md (store layout, ASK_HOME, gc/quarantine), declarative-workflow.md (ask.json schema, strict ref validation, install pipeline, intent-format), and skills-vendoring.md (ask skills install/remove, --agent flag, vendored dir layout). Modeled on vercel-labs/opensrc's skill; command surface verified against packages/cli/src/. --- skills/ask/SKILL.md | 106 +++++++++++++ skills/ask/references/cache.md | 93 +++++++++++ skills/ask/references/declarative-workflow.md | 145 ++++++++++++++++++ skills/ask/references/skills-vendoring.md | 109 +++++++++++++ 4 files changed, 453 insertions(+) create mode 100644 skills/ask/SKILL.md create mode 100644 skills/ask/references/cache.md create mode 100644 skills/ask/references/declarative-workflow.md create mode 100644 skills/ask/references/skills-vendoring.md diff --git a/skills/ask/SKILL.md b/skills/ask/SKILL.md new file mode 100644 index 0000000..6413196 --- /dev/null +++ b/skills/ask/SKILL.md @@ -0,0 +1,106 @@ +--- +name: ask +description: Fetch version-accurate library documentation, source trees, and producer-shipped skills so the agent works against the exact version installed in the project, not training-data guesses. Use this skill whenever the user needs docs for a dependency, wants to read a library's real source, asks "how does X work internally", needs to pin reading to a specific version or ref, mentions ask docs / ask src / ask skills, or any task that would benefit from a library's actual README / source / skill files over recalled knowledge — even when they don't explicitly name the "ask" CLI. Preferred over inferring API shape from memory whenever accuracy matters. +allowed-tools: Bash(ask:*) +--- + +# Version-Accurate Docs & Source with `ask` + +`ask` resolves the version from the project's lockfile +(`bun.lock → package-lock.json → pnpm-lock.yaml → yarn.lock → package.json` +range fallback), fetches docs or source once, caches them globally at +`~/.ask/` (override via `ASK_HOME`), and prints absolute paths to stdout so +the commands compose naturally in shell substitutions. Progress / errors go +to stderr, paths go to stdout — safe for `$(ask …)`. + +## Core Pattern + +```bash +# Docs — one candidate path per line +cat "$(ask docs zod | head -n1)"/README.md +rg "parseAsync" $(ask docs zod) + +# Source — single absolute path to the checkout root +rg "ZodError" $(ask src zod) +fd -e test.ts . $(ask src zod) + +# Producer-shipped skills — one /skills/ dir per line +ls $(ask skills vercel/ai) +``` + +`ask docs` emits candidate documentation directories (publish-time +`dist/docs` first, then any subdirectory whose basename matches `/doc/i` +up to depth 4, falling back to the checkout root when nothing matches). +`ask src` emits exactly one path: the checkout root. Both auto-fetch on +cache miss; pass `--no-fetch` to fail fast (exit 1) on miss instead. + +## Spec Grammar + +``` +zod # bare → npm ecosystem (resolved via lockfile) +npm:next # explicit ecosystem +npm:@mastra/client-js # scoped package +facebook/react # owner/repo → github:facebook/react@main +github:vercel/next.js@v14.2.3 # pinned tag +github:owner/repo@main # pinned branch +``` + +- For `npm:` specs (and bare names), the version comes from the project's + lockfile. Append `@version` to pin explicitly: `zod@3.22.0`, + `npm:next@14.2.3`. +- For `github:` specs, `@` pins a tag or branch. Bare `owner/repo` + defaults to `main`. +- The one-shot reading commands (`docs` / `src` / `skills list`) skip the + strict ref validation used by the declarative workflow, so `main` / + `master` / mutable refs are allowed here. + +## One-Shot Reading Commands + +| Command | Output | Use when | +|---------|--------|----------| +| `ask docs [--no-fetch]` | Candidate doc dirs, one per line | You want README / guides / handwritten docs at the installed version | +| `ask src [--no-fetch]` | Checkout root, single line | You need to read real source, search all files, follow implementations | +| `ask skills ` (= `ask skills list`) | `/skills/` dirs, one per line | The library ships its own Claude / Cursor / OpenCode skills | + +All three share `ensureCheckout`, so the cached path is reused across +commands — calling `ask docs`, then `ask src`, then `ask skills list` on +the same spec fetches once. + +## When You Need More + +Lazy-load these references only when the situation calls for them: + +- **Managing the cache** — disk pressure, stale entries, `--kind` / + `--older-than` filters, legacy v1 layout cleanup → + [`references/cache.md`](references/cache.md). +- **Project-level declarative workflow** — `ask.json`, `ask install`, + `ask add`, `ask remove`, `ask list`, auto-regenerated `AGENTS.md`, + `.ask/resolved.json` cache → + [`references/declarative-workflow.md`](references/declarative-workflow.md). +- **Vendoring producer skills into this project** — `ask skills install`, + `--force`, `--agent claude,cursor,opencode,codex`, `ask skills remove + --ignore-missing` → + [`references/skills-vendoring.md`](references/skills-vendoring.md). + +## When to Reach for `ask` + +Reach for it when: + +- The installed version matters — otherwise the agent risks fabricating + API shape from an outdated training snapshot. +- The answer lives in source, not types — edge cases, error paths, + internal helpers, behavior that isn't documented anywhere else. +- A library may ship its own skills — `ask skills list ` discovers + producer-side `skills/` directories without touching the project. + +Skip it when TypeScript / LSP / intellisense can answer the question, or +when the user has already pointed at a specific file path. + +## Why This Exists + +Training data ages; lockfiles don't. `ask` bridges the two by pinning +every read to the version the project actually runs, so generated code +reflects reality instead of last year's docs. The `$(ask …)` idiom is +the main ergonomic: it turns a cached path into a first-class argument +to `rg`, `cat`, `fd`, or any tool that accepts a path — no extra API to +learn. diff --git a/skills/ask/references/cache.md b/skills/ask/references/cache.md new file mode 100644 index 0000000..cf82e81 --- /dev/null +++ b/skills/ask/references/cache.md @@ -0,0 +1,93 @@ +# Cache Management (`ask cache`) + +Read this when disk is filling up, a cached entry is stale, or you need +to inspect what `ask` has fetched. The global store lives at `~/.ask/` +by default; set `ASK_HOME` to relocate it. Every `ask install` writes +`/STORE_VERSION` (currently `"2"`) as a layout marker. + +## Store Layout (v2) + +``` +/ +├── npm/@/ # npm tarball extractions +├── github///// # github shallow clones (PM-unified) +├── web// # crawled doc sites +├── llms-txt/@/ # llms.txt imports +├── STORE_VERSION # "2" +└── .quarantine/-/ # corrupt entries, preserved for inspection +``` + +`host` is currently always `github.com` — the segment exists so +`gitlab.com` / `bitbucket.org` can slot in without a migration. + +## `ask cache ls [--kind ]` + +List every entry in the store with its size. + +```bash +ask cache ls # everything +ask cache ls --kind npm # filter: npm | github | web | llms-txt +``` + +Entries print as `/ `. Total size is summarized at +the end. If a legacy (pre-v2) `github/db` or `github/checkouts` +directory exists, its entries are tagged with a `(legacy) ` prefix in +the key — that's the signal to run `ask cache clean --legacy`. + +## `ask cache gc [--dry-run] [--older-than ]` + +Remove entries that no project references anymore. `gc` discovers +references by walking `$HOME` (or each path in `ASK_GC_SCAN_ROOTS`, +colon-separated) for `.ask/resolved.json` files and treating the +entries listed there as roots. + +```bash +ask cache gc # remove all unreferenced +ask cache gc --dry-run # preview only, no deletion +ask cache gc --older-than 30d # also require age > 30 days +ask cache gc --older-than 12h # supports d / h / m / s +ASK_GC_SCAN_ROOTS=/repos:/work ask cache gc # restrict scan +``` + +Always try `--dry-run` first when `ASK_GC_SCAN_ROOTS` is unset — the +default `$HOME` scan may miss project directories stored elsewhere, +which would mark still-used entries as unreferenced. + +## `ask cache clean --legacy` + +Remove the pre-v2 github store layout. The flag is required; calling +`ask cache clean` without it exits with a reminder. + +```bash +ask cache clean --legacy +``` + +Deletes: +- `/github/db/` (bare-clone DB from the old shared layout) +- `/github/checkouts/` (per-ref worktrees from the old layout) + +The v2 layout (`/github/////`) is +untouched. Safe to run anytime; it's a no-op when no legacy dirs exist. + +## Environment Variables + +| Variable | Purpose | Default | +|-----------------------|--------------------------------------------|----------------| +| `ASK_HOME` | Override the global store root | `~/.ask` | +| `ASK_GC_SCAN_ROOTS` | Colon-separated scan roots for `cache gc` | `$HOME` | + +Both are read at invocation time — set them inline or export per shell. + +## Troubleshooting Tips + +- "Corrupted store entry … quarantined to …" — `ask install` moved a + tamper/missing-stamp entry into `.quarantine/`. Re-run the install; + the fresh fetch replaces it. Delete the quarantine dir manually once + you've inspected it. +- `ask docs ` prints nothing — the checkout exists but has no + `/doc/i` subdirs AND the walker can't see the root. Try + `ask src ` and `ls $(ask src )` to confirm the checkout + is populated. +- `gc` removed an entry you still needed — either the project wasn't + inside `ASK_GC_SCAN_ROOTS`, or `.ask/resolved.json` was missing / + out of date. Re-run `ask install` in the project to rebuild both. diff --git a/skills/ask/references/declarative-workflow.md b/skills/ask/references/declarative-workflow.md new file mode 100644 index 0000000..4400db7 --- /dev/null +++ b/skills/ask/references/declarative-workflow.md @@ -0,0 +1,145 @@ +# Declarative Workflow (`ask.json` + `ask install`) + +Read this when the user wants docs checked into the repo, wants +`AGENTS.md` auto-regenerated on every `bun install`, or wants a +versioned list of libraries the project's agents should reference. The +one-shot `ask docs` / `ask src` / `ask skills list` commands don't +persist anything; this flow does. + +## Core Idea + +- `ask.json` — the single declarative input. Lists the library specs + the project wants docs / skills for. Checked into git. +- `.ask/resolved.json` — a pure cache recording how each entry + resolved (version, commit SHA, format). Gitignored; safe to delete + and rebuild with `ask install`. +- `.ask/docs/@/` — generated doc copies / symlinks. +- `.claude/skills/-docs/SKILL.md` — generated Claude Code skill + per library. +- `AGENTS.md` — has a block between + `` and `` that + `ask` maintains. + +## `ask install` + +Reads `ask.json`, resolves each entry, fetches to `/`, +materializes files in the project, and rewrites `AGENTS.md`. + +```bash +ask install +``` + +Pipeline per entry: + +1. Lockfile read (npm ecosystem only) — priority `bun.lock → + package-lock.json → pnpm-lock.yaml → yarn.lock → package.json`. +2. Source dispatch — `npm` / `github` / `web` / `llms-txt` adapter. +3. Convention-based discovery (npm only, no explicit `source`): tries + `local-ask` (`package.json.ask.docsPath`), `local-intent` + (TanStack-intent packages), `local-conventions` (`dist/docs`, `docs`, + `README.md`) — first hit wins, registry is the fallback. +4. Write `.ask/docs/@/` and `INDEX.md`. +5. Generate `.claude/skills/-docs/SKILL.md`. +6. Upsert `.ask/resolved.json`. +7. Regenerate the `AGENTS.md` auto block. + +`postinstall`-friendly: per-entry failures emit a warning and the +overall exit code is always 0, so a missing registry entry doesn't +break `bun install` (FR-10). + +## `ask add ` / `ask add` (interactive) + +Appends to `ask.json` and runs install for just that entry. + +```bash +ask add npm:next +ask add npm:@mastra/client-js +ask add github:vercel/next.js@v14.2.3 +ask add facebook/react # bare owner/repo → github:facebook/react +ask add # interactive picker +``` + +Bare names without `:` or `/` (e.g. `ask add zod`) are rejected with a +hint showing the two valid forms. (The one-shot `ask docs zod` DOES +accept bare names — only `add` is strict.) + +## `ask remove ` + +Removes from `ask.json`, deletes the generated skill, re-runs install +to regenerate `AGENTS.md` cleanly. + +```bash +ask remove next +ask remove @mastra/client-js +ask remove npm:next # also accepts full spec +``` + +## `ask list [--json]` + +Joins `ask.json` with `.ask/resolved.json`. Declared-but-not-installed +entries show `version: unresolved` so drift is visible. + +```bash +ask list +ask list --json | jq '.libraries[] | select(.version == "unresolved")' +``` + +## `ask.json` Shape + +```json +{ + "libraries": [ + "npm:next", + "npm:@mastra/client-js", + "github:vercel/next.js@v14.2.3", + { "spec": "github:vercel/ai", "ref": "v5.0.0" }, + { "spec": "npm:zod", "docsPath": "docs" } + ] +} +``` + +Either strings or objects. Object form keys: + +| Key | Purpose | +|-------------|-----------------------------------------------------------| +| `spec` | Required. Same grammar as the CLI positional. | +| `ref` | Required for standalone `github:` entries. No default. | +| `source` | Optional. Force `npm` / `github` / `web` / `llms-txt`. | +| `docsPath` | Optional. Subpath within the source to treat as the doc root. | + +## Ref Validation + +Strict-by-default for `ask.json` — mutable refs are rejected to keep +generated docs reproducible: + +- Rejected: `main`, `master`, `develop`, `trunk`, `HEAD`, `latest`, + any single-word ref without `.` or a digit. +- Accepted: `v1.2.3`, `1.2.3`, `next-14.2.3-canary.1`, full SHAs. + +To bypass (e.g. in CI that docs-against-HEAD): + +```bash +ask install --allow-mutable-ref +ask add github:owner/repo@main --allow-mutable-ref +``` + +The one-shot reading commands (`docs` / `src` / `skills list`) skip +this check entirely — they're meant for exploration, not persistence. + +## `.ask/resolved.json` Cache + +Records the last successful resolution for each spec: resolved +version, commit SHA (for github entries), and `format` (`docs` or +`intent-skills`). Safe to delete; `ask install` rebuilds it. The file +is gitignored by default — `ask` auto-manages the relevant ignore +files via a marker block (`# ask:start … # ask:end`). Don't hand-edit +inside that block; `install` / `remove` will overwrite it. + +## Intent-Format Packages + +Packages shipping TanStack-intent skills (`keywords: +["tanstack-intent"]`) are materialized differently. They live in a +separate `AGENTS.md` block (``), disjoint from the regular docs block. `ask +remove` dispatches on the recorded `format` and tears down the right +block. diff --git a/skills/ask/references/skills-vendoring.md b/skills/ask/references/skills-vendoring.md new file mode 100644 index 0000000..502d286 --- /dev/null +++ b/skills/ask/references/skills-vendoring.md @@ -0,0 +1,109 @@ +# Vendoring Producer Skills (`ask skills install` / `remove`) + +Read this when the user wants a library's own skills (the ones the +library *publishes* for consumers) copied into this project and linked +into the agent's skills directory. The one-shot `ask skills list +` only prints paths; the commands here actually vendor and link. + +## The Core Flow + +`ask skills install ` does five things: + +1. Resolves and fetches the library the same way `ask src` does. +2. Collects each direct child of every `skills/` (or `skill/`) parent + directory shipped by the library — matched case-insensitively + against `^skills?$` exactly, so `my-skills/` / `skill-set/` are + ignored. +3. Vendors those children into `.ask/skills///` + in the project. `specKey` is derived from ecosystem + name + + resolved version (e.g. `npm__next__14.2.3`). +4. Symlinks each vendored skill into the detected agent skills + directories (`.claude/skills/`, `.cursor/skills/`, + `.opencode/skills/`, `.codex/skills/`). +5. Records the install in `.ask/skills-lock.json` and patches + `.gitignore` via the `ask` marker block. + +## `ask skills install ` + +```bash +ask skills install vercel/ai +ask skills install npm:@mastra/core +ask skills install github:owner/repo@v1.2.3 --no-fetch # cache hits only +ask skills install vercel/ai --force # overwrite conflicts +ask skills install vercel/ai --agent claude,cursor # explicit targets +``` + +| Flag | Effect | +|-------------------|------------------------------------------------------------------| +| `--no-fetch` | Return cache hit only — exit 1 on miss. | +| `--force` | Overwrite a conflicting `.claude/skills/` symlink. | +| `--agent ` | Explicit targets: `claude,cursor,opencode,codex`. Skips detection. | + +### Agent selection + +Without `--agent`: + +- 0 agents detected → error asking the user to pass `--agent `. +- 1 agent detected → auto-use it. +- 2+ detected → interactive multiselect prompt. + +Detection looks for the agent's directory (`.claude/`, `.cursor/`, +`.opencode/`, `.codex/`). If none exist, pass `--agent` to create the +target directory and force-install. + +### Exit conditions + +- No `skills/` (or `skill/`) dir found in the source → exit 1 with the + message `no skills/ directories found for `. +- No agent selected → exit 1. +- Symlink collision without `--force` → exit from `linkSkill` with a + descriptive error. + +## `ask skills remove ` + +Reverses a prior install using `.ask/skills-lock.json` as the source +of truth. Only removes symlinks that point at the vendored copy — a +user-edited skill in `.claude/skills/` that no longer points at +`.ask/skills///` is left alone. + +```bash +ask skills remove vercel/ai +ask skills remove npm__vercel-ai__5.0.0 # spec-key also accepted +ask skills remove vercel/ai --ignore-missing # exit 0 even if no lock entry +``` + +| Flag | Effect | +|---------------------|-------------------------------------------------------| +| `--ignore-missing` | Exit 0 when the spec has no lock entry. | + +On success, the vendor dir `.ask/skills//` is deleted and the +lock entry removed. + +## Directory Layout After Install + +``` +.ask/ +├── skills/ +│ └── / +│ ├── / +│ │ └── SKILL.md +│ └── / +│ └── … +└── skills-lock.json + +.claude/skills/ +├── -> ../../.ask/skills// +└── -> ../../.ask/skills// +``` + +Each skill is symlinked individually — no wrapper directory — so the +agent discovers them like any other project skill. + +## Docs vs. Intent-Format Distinction + +This command only handles the "skills" channel. Libraries that +distribute docs (not skills) go through `ask install` instead. If the +library is a TanStack-intent package (keywords include +`tanstack-intent`), `ask install` takes the intent path and writes an +`` block in `AGENTS.md`, independent of +the `ask skills install` flow described here. From 877367e592acaf394768558364f1c92fb3b03f31 Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Thu, 16 Apr 2026 23:14:05 +0900 Subject: [PATCH 2/2] docs(skills): align declarative workflow doc with lazy-first install Fix five inaccuracies caught by the documentation-analyzer review of PR #94 against packages/cli/src/: - ask install pipeline rewritten for the lazy-first orchestrator: only version resolution + SKILL.md + AGENTS.md, no doc fetching. - ask.json shape restricted to the strict spec-string array that AskJsonSchema actually accepts; object-form entries removed. - ask list --json example uses .entries[] (ListModelSchema), not the non-existent .libraries[]. - --allow-mutable-ref removed; the flag is not implemented. - .ask/resolved.json cache section replaced with an honest note that install no longer writes it in the lazy-first architecture. SKILL.md: drop the stale strict-ref-validation framing and the reference-list link to the resolved.json cache. --- skills/ask/SKILL.md | 12 +- skills/ask/references/declarative-workflow.md | 174 +++++++++--------- 2 files changed, 89 insertions(+), 97 deletions(-) diff --git a/skills/ask/SKILL.md b/skills/ask/SKILL.md index 6413196..7dfb96e 100644 --- a/skills/ask/SKILL.md +++ b/skills/ask/SKILL.md @@ -49,10 +49,10 @@ github:owner/repo@main # pinned branch lockfile. Append `@version` to pin explicitly: `zod@3.22.0`, `npm:next@14.2.3`. - For `github:` specs, `@` pins a tag or branch. Bare `owner/repo` - defaults to `main`. -- The one-shot reading commands (`docs` / `src` / `skills list`) skip the - strict ref validation used by the declarative workflow, so `main` / - `master` / mutable refs are allowed here. + (no `@ref`) defaults to `main`. +- Any ref works with these one-shot reading commands — branches, tags, + or mutable refs like `main` / `master` are all accepted, since + nothing is persisted. ## One-Shot Reading Commands @@ -74,8 +74,8 @@ Lazy-load these references only when the situation calls for them: `--older-than` filters, legacy v1 layout cleanup → [`references/cache.md`](references/cache.md). - **Project-level declarative workflow** — `ask.json`, `ask install`, - `ask add`, `ask remove`, `ask list`, auto-regenerated `AGENTS.md`, - `.ask/resolved.json` cache → + `ask add`, `ask remove`, `ask list`, auto-regenerated `AGENTS.md` and + per-library `.claude/skills/-docs/SKILL.md` → [`references/declarative-workflow.md`](references/declarative-workflow.md). - **Vendoring producer skills into this project** — `ask skills install`, `--force`, `--agent claude,cursor,opencode,codex`, `ask skills remove diff --git a/skills/ask/references/declarative-workflow.md b/skills/ask/references/declarative-workflow.md index 4400db7..bcd0ed8 100644 --- a/skills/ask/references/declarative-workflow.md +++ b/skills/ask/references/declarative-workflow.md @@ -1,55 +1,59 @@ # Declarative Workflow (`ask.json` + `ask install`) -Read this when the user wants docs checked into the repo, wants -`AGENTS.md` auto-regenerated on every `bun install`, or wants a -versioned list of libraries the project's agents should reference. The -one-shot `ask docs` / `ask src` / `ask skills list` commands don't -persist anything; this flow does. - -## Core Idea - -- `ask.json` — the single declarative input. Lists the library specs - the project wants docs / skills for. Checked into git. -- `.ask/resolved.json` — a pure cache recording how each entry - resolved (version, commit SHA, format). Gitignored; safe to delete - and rebuild with `ask install`. -- `.ask/docs/@/` — generated doc copies / symlinks. -- `.claude/skills/-docs/SKILL.md` — generated Claude Code skill - per library. -- `AGENTS.md` — has a block between - `` and `` that - `ask` maintains. +Read this when the user wants a checked-in list of libraries the +project's agents should reference, or wants `AGENTS.md` auto-regenerated +with lazy `ask src` / `ask docs` pointers. The one-shot reading +commands (`ask docs` / `ask src` / `ask skills list`) don't persist +anything; this flow does. + +`ask install` is **lazy-first**: it resolves versions and writes +`AGENTS.md` plus a per-library `.claude/skills/-docs/SKILL.md` +that tells agents to invoke `ask src ` / `ask docs ` on +demand. No documentation is downloaded during `install` — fetching +happens the first time an agent calls `ask docs` / `ask src`. + +## What Gets Written + +- `ask.json` — the declarative input. Array of spec strings. Checked + into git; edited by `ask add` / `ask remove` (or by hand). +- `AGENTS.md` — block between `` + and `` maintained by `ask`. +- `.claude/skills/-docs/SKILL.md` — one skill per declared + library, each delegating to `ask docs ` / `ask src `. +- Ignore files (`.gitignore`, `.prettierignore`, etc.) — patched via + `# ask:start … # ask:end` marker blocks. Don't hand-edit inside + those markers; `install` / `remove` overwrite them. ## `ask install` -Reads `ask.json`, resolves each entry, fetches to `/`, -materializes files in the project, and rewrites `AGENTS.md`. +Reads `ask.json`, resolves each spec to a concrete version, regenerates +per-library skills, and rewrites the `AGENTS.md` block. ```bash ask install ``` -Pipeline per entry: +Per-entry behavior: -1. Lockfile read (npm ecosystem only) — priority `bun.lock → - package-lock.json → pnpm-lock.yaml → yarn.lock → package.json`. -2. Source dispatch — `npm` / `github` / `web` / `llms-txt` adapter. -3. Convention-based discovery (npm only, no explicit `source`): tries - `local-ask` (`package.json.ask.docsPath`), `local-intent` - (TanStack-intent packages), `local-conventions` (`dist/docs`, `docs`, - `README.md`) — first hit wins, registry is the fallback. -4. Write `.ask/docs/@/` and `INDEX.md`. -5. Generate `.claude/skills/-docs/SKILL.md`. -6. Upsert `.ask/resolved.json`. -7. Regenerate the `AGENTS.md` auto block. +1. **Version resolution** + - `npm:` specs → read lockfile chain + `bun.lock → package-lock.json → pnpm-lock.yaml → yarn.lock → + package.json` (range fallback). Missing from every lockfile ⇒ + entry skipped with a warning. + - `github:` specs → the ref encoded in the spec + (`github:vercel/next.js@v14.2.3`). No lockfile lookup. + - Explicit `@version` on any spec wins over lockfile resolution. +2. **Skill generation** — writes `.claude/skills/-docs/SKILL.md`. +3. **AGENTS.md regeneration** — rewrites the auto-generated block + with every resolved library. -`postinstall`-friendly: per-entry failures emit a warning and the -overall exit code is always 0, so a missing registry entry doesn't -break `bun install` (FR-10). +`postinstall`-friendly: per-entry failures emit a warning; the overall +exit code is always 0, so an unresolvable entry doesn't break +`bun install`. ## `ask add ` / `ask add` (interactive) -Appends to `ask.json` and runs install for just that entry. +Appends to `ask.json`, then runs install for just that entry. ```bash ask add npm:next @@ -60,86 +64,74 @@ ask add # interactive picker ``` Bare names without `:` or `/` (e.g. `ask add zod`) are rejected with a -hint showing the two valid forms. (The one-shot `ask docs zod` DOES -accept bare names — only `add` is strict.) +hint listing the two valid forms. Note the asymmetry: the one-shot +`ask docs zod` DOES accept bare names — only `ask add` is strict +because the spec is persisted. ## `ask remove ` Removes from `ask.json`, deletes the generated skill, re-runs install -to regenerate `AGENTS.md` cleanly. +to regenerate `AGENTS.md`. ```bash ask remove next ask remove @mastra/client-js -ask remove npm:next # also accepts full spec +ask remove npm:next # also accepts the full spec ``` +The `name` match is tried against the full spec, the spec body, and +the derived library slug — so the unscoped name, the full spec, or the +scoped package name all work. + ## `ask list [--json]` -Joins `ask.json` with `.ask/resolved.json`. Declared-but-not-installed -entries show `version: unresolved` so drift is visible. +Prints declared libraries with their resolved versions. Unresolved +entries (declared in `ask.json` but missing from every lockfile) show +`version: unresolved` so drift is visible. ```bash ask list -ask list --json | jq '.libraries[] | select(.version == "unresolved")' +ask list --json | jq '.entries[] | select(.version == "unresolved")' ``` +JSON shape (`ListModelSchema`): `{ "entries": [...], "conflicts": +[...], "warnings": [...] }`. Each entry has `name`, `version`, +`format`, `source`, `location`, plus optional `itemCount` / `skills`. + ## `ask.json` Shape +Strict array of ecosystem-prefixed spec strings — schema is +`z.array(z.string().regex(/^[a-z][a-z0-9+-]*:.+$/))` with `.strict()`. +Object entries are rejected. + ```json { "libraries": [ "npm:next", "npm:@mastra/client-js", + "npm:zod@3.22.0", "github:vercel/next.js@v14.2.3", - { "spec": "github:vercel/ai", "ref": "v5.0.0" }, - { "spec": "npm:zod", "docsPath": "docs" } + "github:vercel/ai@v5.0.0" ] } ``` -Either strings or objects. Object form keys: - -| Key | Purpose | -|-------------|-----------------------------------------------------------| -| `spec` | Required. Same grammar as the CLI positional. | -| `ref` | Required for standalone `github:` entries. No default. | -| `source` | Optional. Force `npm` / `github` / `web` / `llms-txt`. | -| `docsPath` | Optional. Subpath within the source to treat as the doc root. | - -## Ref Validation - -Strict-by-default for `ask.json` — mutable refs are rejected to keep -generated docs reproducible: - -- Rejected: `main`, `master`, `develop`, `trunk`, `HEAD`, `latest`, - any single-word ref without `.` or a digit. -- Accepted: `v1.2.3`, `1.2.3`, `next-14.2.3-canary.1`, full SHAs. - -To bypass (e.g. in CI that docs-against-HEAD): - -```bash -ask install --allow-mutable-ref -ask add github:owner/repo@main --allow-mutable-ref -``` - -The one-shot reading commands (`docs` / `src` / `skills list`) skip -this check entirely — they're meant for exploration, not persistence. - -## `.ask/resolved.json` Cache - -Records the last successful resolution for each spec: resolved -version, commit SHA (for github entries), and `format` (`docs` or -`intent-skills`). Safe to delete; `ask install` rebuilds it. The file -is gitignored by default — `ask` auto-manages the relevant ignore -files via a marker block (`# ask:start … # ask:end`). Don't hand-edit -inside that block; `install` / `remove` will overwrite it. - -## Intent-Format Packages - -Packages shipping TanStack-intent skills (`keywords: -["tanstack-intent"]`) are materialized differently. They live in a -separate `AGENTS.md` block (``), disjoint from the regular docs block. `ask -remove` dispatches on the recorded `format` and tears down the right -block. +- `npm:` specs: append `@` to pin, otherwise the lockfile + decides. +- `github:` specs: append `@` to encode the version inside the + spec string. `github:` without a ref defaults to `latest` in the + generated output. +- No separate `ref`, `source`, or `docsPath` keys. Per-library + `docsPath` comes from the ASK Registry when `ask docs` / `ask src` + actually fetch, not from `ask.json`. + +## Intent-Format and Resolved-Cache Notes + +Prior versions of `ask` had a `.ask/resolved.json` lockfile, a +`` block in `AGENTS.md` for +TanStack-intent packages, and a `--allow-mutable-ref` flag. In the +current lazy-first architecture `ask install` does not write +`.ask/resolved.json` and does not populate intent blocks; the +underlying code modules still exist but are not wired into the +install path. Treat the docs, source, and skills commands as the +supported surface, and `ask.json` as a strict spec-string array.