Skip to content

feat(skills): add forest skills:init β€” install Forest skills into a repo - #804

Closed
Gawtier wants to merge 3 commits into
mainfrom
feat/skills-init
Closed

Gawtier wants to merge 3 commits into
mainfrom
feat/skills-init

Conversation

@Gawtier

@Gawtier Gawtier commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What

forest skills:init β€” install Forest's agent skills into the current repo, project-scoped (.claude/skills/ + .agents/skills/), so a client's coding agent (Claude Code or Codex) knows Forest with no marketplace add.

Fetches from ForestAdmin/ai-marketplace (tarball at --ref, default main), copies the curated dev-facing skills for both agents, merges a delimited Forest block into CLAUDE.md / AGENTS.md (preserving user content), wires the Forest docs MCP into .mcp.json, and records the ref + file list in .forest/skills-manifest.json.

Flags: --ref (git ref, default main), --force (overwrite existing skill files). Both agents are always set up β€” the unused one's files are inert.

The distributed set is a curated list in SKILL_SOURCES (explicit control over what ships; internal/test-only skills stay out).

Tested

  • Unit tests (service): context-file resolution, block merge preserving user content, install into both agent dirs, docs-MCP merge (incl. malformed/non-object .mcp.json), manifest round-trip, missing-source fail-loud, and symlink-safe writes on both the destination and the source bundle.
  • Network path hardened: fetchMarketplace has a timeout and maps a bad --ref (404) to a clear message.
  • Live e2e against ai-marketplace@main: skills installed across .claude/ + .agents/.

Note

Add forest skills:init command to install Forest skills into agent repos

  • Adds a new forest skills:init CLI command in init.ts that downloads a tar.gz bundle from ForestAdmin/ai-marketplace at a given git ref and installs curated skills for Claude Code (.claude/skills) and Codex (.agents/skills).
  • Merges a Forest context block into CLAUDE.md/AGENTS.md and wires the forest-docs MCP server into .mcp.json, preserving existing user-defined servers.
  • Tracks installed files in .forest/skills-manifest.json to prune stale managed files on re-runs without deleting user-added content.
  • skills-manager.ts implements all underlying utilities including marketplace fetch, recursive directory copy, symlink-safe writes, stale file pruning, and preflight validation of local .mcp.json and the bundle.
  • Risk: adds tar as a new runtime dependency; command aborts early if the local .mcp.json is malformed or the bundle is missing forest-docs/.mcp.json.

Macroscope summarized aea7046.

Comment thread src/services/skills/skills-manager.ts Outdated
Comment thread src/services/skills/skills-manager.ts
Comment thread src/services/skills/skills-manager.ts Outdated
Comment thread src/commands/skills/init.ts Outdated
Comment thread src/services/skills/skills-manager.ts Outdated
@Gawtier
Gawtier force-pushed the feat/skills-init branch from 071cd15 to d181de6 Compare July 24, 2026 08:56
Comment thread src/services/skills/skills-manager.ts Outdated
@Gawtier
Gawtier force-pushed the feat/skills-init branch from d181de6 to 6206b47 Compare July 24, 2026 09:10
Comment thread src/services/skills/skills-manager.ts Outdated
@Gawtier
Gawtier force-pushed the feat/skills-init branch from 6206b47 to 0206986 Compare July 24, 2026 09:45
Comment thread src/services/skills/skills-manager.ts
Comment thread src/services/skills/skills-manager.ts Outdated
@Gawtier
Gawtier force-pushed the feat/skills-init branch 2 times, most recently from 0604388 to 2db53d2 Compare July 24, 2026 10:14
Comment thread src/services/skills/skills-manager.ts
@Gawtier
Gawtier force-pushed the feat/skills-init branch from 2db53d2 to d76ec2f Compare July 24, 2026 11:31
Comment thread src/services/skills/skills-manager.ts Outdated
Comment thread src/services/skills/skills-manager.ts
Comment thread src/services/skills/skills-manager.ts
@Gawtier
Gawtier force-pushed the feat/skills-init branch from d76ec2f to 31a06b0 Compare July 24, 2026 12:25
Comment thread src/services/skills/skills-manager.ts
@Gawtier
Gawtier force-pushed the feat/skills-init branch 2 times, most recently from f40b132 to a133503 Compare July 27, 2026 06:54
Comment thread src/services/skills/skills-manager.ts
… repo

Project-scoped, marketplace-independent onboarding for the client's coding agent
(Claude Code + Codex). Fetches the public ForestAdmin/ai-marketplace, drops all 8
skill bundles (forest/* + forest-code + forest-legacy) into .claude/skills/ and
.agents/skills/, merges a Forest block into CLAUDE.md/AGENTS.md, and wires the
forest-docs MCP (https://docs.forest.app/mcp, no secret) from the marketplace.

Includes 11 unit tests (skills-manager) and conforms to repo conventions
(array methods, declared `tar` dep, no jest hooks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Gawtier
Gawtier force-pushed the feat/skills-init branch from a133503 to 6c0e6f3 Compare July 27, 2026 07:27
Comment thread src/services/skills/skills-manager.ts
PMerlet and others added 2 commits July 31, 2026 11:54
On the no-force skip path, installSkills reported the bundle-derived file
list as managed even though nothing was written. A user-authored skill dir
that pre-existed the first skills:init (e.g. their own
.claude/skills/layout/SKILL.md) was thus recorded in the manifest, and a
later refresh β€” once the skill left the upstream bundle β€” would prune it,
deleting user data.

installSkills now takes the previous manifest's file list (null on a first
run) and, on the skip path, only carries over the intersection of the
bundle-derived list and that previous manifest: files a past run provably
wrote. First run over a pre-existing dir claims nothing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… write

installDocsMcp runs last (after skill installs, CLAUDE.md/AGENTS.md merges
and stale pruning), so its deliberate throw on an unparseable local
.mcp.json β€” or a raw ENOENT on a bundle missing forest-docs/.mcp.json β€”
left a half-applied install with no manifest rewrite.

skills:init now fails fast: validateLocalMcp() runs before any disk
mutation, and validateMarketplaceBundle() checks the fetched bundle right
after the fetch, before the first write. Both are exported from
skills-manager (and unit-tested) so skills:update can reuse them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* write. The docs MCP config is read last by `installDocsMcp`; a bundle missing it would otherwise
* die on a raw ENOENT after everything else was already applied.
*/
export function validateMarketplaceBundle(srcRoot: string, ref: string): void {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High skills/skills-manager.ts:287

validateMarketplaceBundle only checks that forest-docs/.mcp.json exists, so a custom ref with malformed MCP JSON (or a later curated skill missing from the bundle) passes the preflight. installSkills and mergeBlock then write skills and context files before installDocsMcp parses the JSON or the missing skill throws, aborting the command without writing a manifest β€” leaving the repo in a half-applied state. Parse the MCP JSON and verify every SKILL_SOURCES source path inside validateMarketplaceBundle so both failures surface before any writes begin.

πŸš€ Reply "fix it for me" or copy this AI Prompt for your agent:
In file @src/services/skills/skills-manager.ts around line 287:

`validateMarketplaceBundle` only checks that `forest-docs/.mcp.json` exists, so a custom ref with malformed MCP JSON (or a later curated skill missing from the bundle) passes the preflight. `installSkills` and `mergeBlock` then write skills and context files before `installDocsMcp` parses the JSON or the missing skill throws, aborting the command without writing a manifest β€” leaving the repo in a half-applied state. Parse the MCP JSON and verify every `SKILL_SOURCES` source path inside `validateMarketplaceBundle` so both failures surface before any writes begin.

@Gawtier

Gawtier commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #814, which carries these commits rebased on main β€” your two fixes included and still load-bearing, @PMerlet.

The design changed rather than the code: Claude Code and Codex both expose a non-interactive plugin CLI, and both read the same .claude-plugin/marketplace.json, so we drive their CLI instead of copying files for them. That removes the .mcp.json merge and its validators (.mcp.json is a Claude Code file, and Claude Code now gets the docs MCP from the forest-docs plugin) and the duplicate .claude/skills/ copy. File copy stays for Cursor, OpenCode and the rest, which have no plugin mechanism.

Closing here rather than force-pushing, so the new diff is readable on its own instead of mutating under the old description. Review continues in #814.

@Gawtier Gawtier closed this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants