$ ls docs/
rosie is also a typed JavaScript library. install rosie-skills from npm and call it from node directly — no spawning, no parsing stdout. backed by the same C internals as the CLI, compiled to wasm and inlined in the package.
$ npm install rosie-skills
the package is esm-only. use a namespace import:
// every function returns a Promise. failures throw Error with a descriptive message.
import * as rosie from 'rosie-skills';
await rosie.install('anthropics/skills');
const skills = await rosie.list();
const agents = await rosie.agents();
install
every cli flag has a corresponding option. pass nothing extra and rosie does the right thing: install the whole repo, auto-detect agents, write to .agents/skills/.
// one skill, specific agents
await rosie.install('anthropics/skills', {
skill: 'pdf',
agent: ['claude', 'cursor'],
});
// install as a reference (.md doc indexed in AGENTS.md / CLAUDE.md / …)
await rosie.install('vercel/next.js', { ref: true });
// reference with a custom name
await rosie.install('anthropics/skills', {
ref: true,
skill: 'pdf',
name: 'pdf-handling',
});
// from an npm package — symlinks .md files out of node_modules/
await rosie.install('react', {
ref: true,
npm: true,
include: ['README.md'],
});
// global install (~/.agent/skills/ instead of ./.agents/skills/)
await rosie.install('anthropics/skills', { global: true });
// ad-hoc install, don't record in rosie.lock
await rosie.install('anthropics/skills', { lockfile: false });
// install into a different project directory
await rosie.install('anthropics/skills', { cwd: '/path/to/project' });
// reinstall everything in .agents/rosie.lock — no args needed
await rosie.installFromLockfile();
install result
install, installFromLockfile, and update return an InstallResult. per-skill detail in skills, deduped unions in installedAgents / failedAgents, and installedInstruction names the agent-instructions file that was written (or null when none was touched).
const result = await rosie.install('anthropics/skills');
// {
// skills: [
// { name: 'pdf', kind: 'skill',
// installedAgents: ['claude', 'cursor'],
// failedAgents: [] },
// ...
// ],
// installedAgents: ['claude', 'cursor'],
// failedAgents: [],
// installedInstruction: null,
// }
failedAgents is non-fatal — rosie tries every agent and reports the misses. Common cause: an existing non-symlink file at ~/.<agent>/skills/<name> blocking the create, or restrictive permissions on the agent's skills/ dir. The canonical copy under .agents/skills/ still lands and the lockfile still records the entry; rerunning rosie.install after fixing permissions re-attempts the failed agents.
const result = await rosie.install('anthropics/skills');
if (result.failedAgents.length > 0) {
console.warn(`couldn't symlink into: ${result.failedAgents.join(', ')}`);
}
reference installs land under .agents/references/ instead of agent dirs, so kind === 'reference' and the agent arrays are empty. installedInstruction is the path of the markdown file (AGENTS.md / CLAUDE.md / GEMINI.md / .github/copilot-instructions.md) whose references block was rewritten.
const result = await rosie.install('vercel/next.js', { ref: true });
// {
// skills: [{ name: 'vercel-next.js', kind: 'reference',
// installedAgents: [], failedAgents: [] }],
// installedAgents: [],
// failedAgents: [],
// installedInstruction: 'AGENTS.md',
// }
references
pass ref: true to install a markdown doc into .agents/references/<name>/REFERENCE.md and append it to the project's agent-instructions file (AGENTS.md · CLAUDE.md · GEMINI.md · .github/copilot-instructions.md, first one found — else AGENTS.md is created).
// the repo's README.md becomes the reference
await rosie.install('vercel/next.js', { ref: true });
// pick a specific SKILL.md (frontmatter stripped) — source is recorded
// as owner/repo#skill so rosie.update() round-trips correctly
await rosie.install('anthropics/skills', {
ref: true,
skill: 'pdf',
});
// override the default install name (default: owner-repo[-skill])
await rosie.install('anthropics/skills', {
ref: true,
skill: 'docx',
name: 'word-docs',
});
// from an npm package — symlinks .md files out of node_modules/<pkg>/
// (implies ref: true; tracks the installed version)
await rosie.install('react', { ref: true, npm: true });
// scope the npm walk — replaces the default README+docs/**.md set
await rosie.install('zod', {
ref: true,
npm: true,
include: ['README.md'],
});
title in the index is re-extracted from each file's first H1 on every rebuild; falls back to the install name. references show up in rosie.list() with isReference: true:
const all = await rosie.list();
const refs = all.filter(s => s.isReference);
// [
// { name: 'vercel-next.js', source: 'vercel/next.js',
// ref: 'main', sha: '...', isReference: true },
// ...
// ]
list, agents
read-only commands; structured results, no parsing.
const skills = await rosie.list();
// [
// { name: 'pdf', source: 'anthropics/skills', ref: 'main',
// sha: 'f458cee31...', isReference: false },
// ...
// ]
const agents = await rosie.agents();
// [
// { name: 'claude', display: 'Claude Code',
// detected: true, installPath: '/home/me/.claude/skills' },
// { name: 'cursor', display: 'Cursor',
// detected: false, installPath: null },
// ...
// ]
remove, update
// remove a skill from all agents (or pass agent: ['claude'] to scope)
await rosie.remove('pdf');
// update everything in rosie.lock
await rosie.update();
// update just one entry
await rosie.update('pdf');
logging
silent by default. pass onLog to observe progress. failures still throw — onLog is purely for visibility.
await rosie.install('anthropics/skills', {
onLog: ({ level, message }) => {
if (level === 'error') console.error(message);
else if (level === 'info') console.log(message);
// 'warn' and 'debug' levels also available
},
});
error handling
try {
await rosie.install('owner/nonexistent-repo');
} catch (err) {
// err.message has the underlying log_error from the C side
console.error('install failed:', err.message);
}
cwd and lockfile
every command accepts a cwd option, equivalent to cd'ing into that directory before running. process.cwd() is restored on exit. mirrors the cli's --cwd flag.
await rosie.install('owner/repo', { cwd: '/path/to/project' });
// cli equivalent
// $ rosie --cwd /path/to/project install owner/repo
pass lockfile: false on install, remove, or update to skip reads and writes to .agents/rosie.lock. files still land on disk; nothing is recorded. mirrors the cli's --no-lockfile.
await rosie.install('anthropics/skills', { lockfile: false });
// cli equivalent
// $ rosie install anthropics/skills --no-lockfile
how it runs
- ▸ in-process wasm the api always uses the wasm build · no subprocess spawning · synchronous from the c side via asyncify
-
▸
node 18+
uses built-in
fetchfor http · nodefsfor file i/o via NODERAWFS -
▸
esm-only
"type": "module"· useimport· norequire -
▸
typescript types
ships
.d.tsalongside.js· works in any editor that resolves npm types -
▸
cli still works the same
npx rosie-skills install …uses the native binary when available, falls back to wasm otherwise