Skip to content
Merged
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
135 changes: 135 additions & 0 deletions AGENT_SKILLS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# How ICP skills are managed in generated projects

Every project scaffolded from these templates ships an `AGENTS.md` (and a
`CLAUDE.md` that points to it) with a **self-configuring skills section**. This
document explains what that section does, the choices it offers, and why it is
written the way it is — so a future reader of a project's `AGENTS.md` can tell
where the content came from and why.

For the broader, tool-agnostic picture of how agents consume ICP skills, see the
developer-docs guide: <https://docs.internetcomputer.org/guides/ai-coding-agents>.

## What ICP skills are

ICP skills are tested, frequently-updated instruction files (correct dependency
versions, compiler flags, API signatures, and documented pitfalls) published at
<https://skills.internetcomputer.org>. ICP evolves quickly, so pre-training
knowledge is outdated by definition. The rule agents follow is: **when a skill
and general knowledge disagree, the skill is correct.**

## The self-configuring `AGENTS.md`

The skills section of a fresh `AGENTS.md` is delimited by markers:

```
<!-- ic-skills:managed:start -->
<!-- state: onboarding-needed -->
...
<!-- ic-skills:managed:end -->
```

While `state:` is `onboarding-needed`, the first agent session runs a one-time
setup: it asks you to choose how this project should use skills, performs the
setup, and then **rewrites everything between the two markers** with a small,
mode-specific block — deleting the onboarding instructions. From then on every
session reads that terse block and behaves accordingly, without asking again.
Only the text between the markers is ever rewritten; anything you add elsewhere
in `AGENTS.md` is left untouched.

## The three modes

| Mode | Who it's for | Preconditions | How skills are obtained |
|------|--------------|---------------|-------------------------|
| **autosync** | Claude Code users who want zero-maintenance, always-current skills | bash, `curl`, `jq`, Claude Code | A `SessionStart` hook (`.claude/sync-ic-skills.sh`) mirrors the latest skills into `.claude/skills/` every session |
| **pinned** | Any agent/harness; teams wanting reproducible, version-locked skills | Node / `npx` | `npx skills add dfinity/icskills` records a `skills-lock.json`; skills restored/refreshed via the CLI |
| **on-demand** | Anyone; zero install; the safe default | Network access | Skills fetched fresh from the registry on demand each session |

`on-demand` is the **recommended default** and the fallback (see below) because it
installs nothing, works with any agent, and is fully reversible.

### Pinned update policy

Pinned means "use the version-locked skills" — that is the safe default, so the two
policies only differ in how updates are offered (there is deliberately no silent
auto-update, which a prompt cannot enforce reliably):

- **manual** — skills change only when the user runs `npx skills update`.
- **ask-first** — before the first task of a session, the agent offers to run
`npx skills update`. If the user declines, or the session is non-interactive
(e.g. CI), it keeps the locked versions and continues — it never blocks the task.

**An update is refresh + discover.** `npx skills update` only refreshes and prunes
skills already in `skills-lock.json` — it does **not** pull in skills added to the
repo since you pinned ([vercel-labs/skills#591](https://github.com/vercel-labs/skills/issues/591)),
and its stale-skill prune can silently no-op for shorthand sources like
`dfinity/icskills` ([vercel-labs/skills#1376](https://github.com/vercel-labs/skills/issues/1376)).
So whichever policy you pick, an update should also run
`npx skills add dfinity/icskills --list` and offer to add any skills not yet installed
— never `--all`, since pinning is curated. A **renamed** skill surfaces there under its
new name; if `update` left the old name behind, drop it with `npx skills remove <old-name>`.

Either way, if the skills are missing they are restored from `skills-lock.json`
with `npx skills experimental_install`. For always-latest without asking, use
`autosync` (Claude Code) or `on-demand` instead — pinned is about control, not
freshness.

## The two fallbacks (and why they differ)

If skills aren't present when a session needs them, the recovery path depends on
the mode — because each mode stores skills differently:

- **autosync → fetch from the registry.** There is no lock file in this mode; the
hook is the source of truth. If the hook hasn't run yet (or `jq` is missing),
the agent fetches skills on demand from
<https://skills.internetcomputer.org/llms.txt>.
- **pinned → `npx skills experimental_install`.** This mode commits a
`skills-lock.json`, so the exact locked versions are restored deterministically
from it.

## Version control — the maintainer's call

The templates do **not** touch `.gitignore`; how the skill directories
(`.claude/skills/`, `.agents/skills/`) are tracked is your decision:

- **Commit them** — reproducible, offline, and reviewable; a natural fit for
`pinned`. Trade-off: with `autosync`, refreshed skills show up as changes to
commit, and `npx skills` may use symlinks that are awkward across platforms.
- **Git-ignore them** — treat them as a managed cache (like `node_modules`); a
natural fit for `autosync` (no churn). They are repopulated by the hook, by
`npx skills experimental_install` (pinned), or by on-demand fetch.
- **Leave it to your harness's default** — also fine; just note that untracked
skill files will otherwise show up in `git status`.

Whichever you choose, keep the small reproducibility metadata committed:
`skills-lock.json` for `pinned`, and the hook + `.claude/sync-ic-skills.sh` for
`autosync`. That way every mode stays fully recoverable regardless of the
`.gitignore` decision.

## Non-interactive sessions, and "just start"

The onboarding never blocks your actual work. If a session can't ask you
(non-interactive/CI) or you'd rather just start coding, the agent uses `on-demand`
for that session only and **does not modify `AGENTS.md`** — leaving the choice
open so a later interactive session can still make it. A choice is persisted only
when you actively make one.

## Changing your mind later

To switch modes, reset the marker to re-trigger onboarding: replace the block
between `ic-skills:managed:start` / `end` with a single line:

```
<!-- state: onboarding-needed -->
```

The next session will walk you through the choice again. (You can also just edit
the configured block by hand if you know the target mode.)

## A note on reliability

The self-rewrite is deliberately simple (clear markers, copy-one-block-verbatim)
so a wide range of agents can perform it. It has been validated across simulated
Claude Code / Cursor / Aider sessions. A less capable model could still mis-edit
the block; because `on-demand` is side-effect-free and every mode is recoverable,
the failure modes are benign (an extra prompt, or one session on general
knowledge) rather than destructive.
116 changes: 97 additions & 19 deletions _shared/write-agent-files.rhai
Original file line number Diff line number Diff line change
@@ -1,22 +1,100 @@
let agents_md = `# AI Agent Instructions

This is an Internet Computer (ICP) project built with icp-cli.

Documentation: https://cli.internetcomputer.org/llms.txt

## Skills

Tested implementation patterns for ICP development are available as agent skills.
Fetch the skills index and remember each skill's name and description:
https://skills.internetcomputer.org/.well-known/skills/index.json

When a task matches a skill's description, use it if already loaded in your
context. Otherwise, fetch its content on-demand from the registry:
https://skills.internetcomputer.org/.well-known/skills/{name}/{file}

Skills contain correct dependency versions, configuration formats, and common pitfalls that prevent build failures.
Always prefer skill guidance over general documentation when both cover the same topic.
`;
// Writes AGENTS.md + CLAUDE.md into every generated icp-cli project (post-hook,
// shared by all sub-templates). AGENTS.md is emitted as an array of double-quoted
// lines joined with "\n": its content has many backticks (markdown code spans) that
// would terminate a Rhai backtick string, and no double quotes, so this needs zero
// escaping. Canonical source and rationale: AGENT_SKILLS.md at the repo root.

let agents_lines = [
"# AI Agent Instructions",
"",
"This is an Internet Computer (ICP) project built with icp-cli.",
"Documentation: https://cli.internetcomputer.org/llms.txt",
"",
"## Skills",
"",
"<!-- ic-skills:managed:start -->",
"<!-- state: onboarding-needed -->",
"",
"ICP evolves quickly. Tested, current implementation patterns are published as",
"agent skills (correct dependency versions, compiler flags, API signatures, pitfalls).",
"Skills are authoritative: when a skill and your general knowledge disagree, the",
"skill is correct. How these options work and why:",
"https://github.com/dfinity/icp-cli-templates/blob/main/AGENT_SKILLS.md",
"",
"This project has not chosen how to use skills yet. Handle setup as follows, and",
"NEVER block the user's actual request on it:",
"",
"- If you CANNOT ask the user (non-interactive session), or the user would rather",
" just start: use `on-demand` for THIS session only (fetch skills on demand — see",
" the on-demand block below), do NOT modify this file, and continue with their",
" request. A later session can still choose.",
"",
"- If you CAN ask: have the user choose ONE option, then perform the matching setup",
" and rewrite this section (STEP 4) so the choice persists:",
" (a) autosync — Claude Code only. Skills auto-update every session.",
" (b) pinned — any agent (needs Node/npx). Skills version-locked via",
" skills-lock.json, restored on demand.",
" (c) on-demand — no install, works anywhere with network. Fetch skills fresh",
" each session. (Recommended default if the user is unsure.)",
" If they chose (b) pinned, also ask how updates should happen: only when the user",
" runs `npx skills update` themselves (manual), or offer to update at the start of",
" each session (ask-first). Either way, an update is refresh + discover: `npx skills",
" update` only refreshes/prunes already-pinned skills, so also run `npx skills add",
" dfinity/icskills --list` and offer to add any not yet installed (never --all).",
"",
"Setup commands:",
" (a) autosync — fetch and follow:",
" https://skills.internetcomputer.org/.well-known/skills/autosync-ic-skills/SKILL.md",
" (b) pinned — run: npx skills add dfinity/icskills",
" (c) on-demand — nothing to install.",
"",
"STEP 4 (only after the user has actively CHOSEN an option) — keep the two",
" `ic-skills:managed:` marker lines exactly as they are, and replace EVERYTHING",
" between them (these instructions included) with the one block below whose",
" `<!-- state: ... -->` comment matches the choice. Copy it verbatim; for pinned,",
" pick the block matching the update answer. Do NOT do this if you fell back to",
" on-demand without the user choosing.",
"",
">>> Blocks to choose from (write exactly one, between the markers):",
"",
"<!-- state: configured (autosync) -->",
"ICP skills auto-update each session via a SessionStart hook (`.claude/sync-ic-skills.sh`)",
"and live in your agent skills directory — you don't need to run anything to refresh them.",
"Skills are authoritative — prefer them over general knowledge for all ICP work.",
"If they are not present (hook hasn't run, or `jq` is missing), fetch them on demand per",
"https://skills.internetcomputer.org/llms.txt instead.",
"How skills are managed here, and why: https://github.com/dfinity/icp-cli-templates/blob/main/AGENT_SKILLS.md",
"",
"<!-- state: configured (pinned, manual-update) -->",
"ICP skills are version-locked in this repo (skills-lock.json) and live in your agent",
"skills directory. Skills are authoritative — prefer them over general knowledge for all",
"ICP work. Update only when the user runs `npx skills update` — when they do, also check",
"for newly added skills with `npx skills add dfinity/icskills --list` and offer to add any",
"that are missing (remove a renamed skill's old copy with `npx skills remove <old-name>` if",
"it lingers). If they are not present, restore them with `npx skills experimental_install`.",
"How skills are managed here, and why: https://github.com/dfinity/icp-cli-templates/blob/main/AGENT_SKILLS.md",
"",
"<!-- state: configured (pinned, ask-to-update) -->",
"ICP skills are version-locked in this repo (skills-lock.json) and live in your agent",
"skills directory. Skills are authoritative — prefer them over general knowledge for all",
"ICP work. Before your first task in a new session, offer to run `npx skills update`, then",
"check for newly added skills with `npx skills add dfinity/icskills --list` and offer to add",
"any not yet installed (remove a renamed skill's old copy with `npx skills remove <old-name>`",
"if it lingers); if the user declines or the session is non-interactive, keep the locked",
"versions and continue — never block. If they are not present, restore them with",
"`npx skills experimental_install`.",
"How skills are managed here, and why: https://github.com/dfinity/icp-cli-templates/blob/main/AGENT_SKILLS.md",
"",
"<!-- state: configured (on-demand) -->",
"Fetch the skills index once per session and keep each skill's name, description, and",
"SKILL.md URL: https://skills.internetcomputer.org/.well-known/skills/index.json",
"Before writing ICP code for a task, fetch the matching skill's SKILL.md",
"(https://skills.internetcomputer.org/.well-known/skills/{name}/SKILL.md) and follow it.",
"Skills are authoritative — prefer them over general knowledge.",
"How skills are managed here, and why: https://github.com/dfinity/icp-cli-templates/blob/main/AGENT_SKILLS.md",
"<!-- ic-skills:managed:end -->",
];
let agents_md = "";
for line in agents_lines { agents_md += line + "\n"; }
file::write("AGENTS.md", agents_md);
file::write("CLAUDE.md", "Read and follow the instructions in [AGENTS.md](AGENTS.md).\n");
Loading