This file provides guidance to coding agents working in this repository (Claude Code, Cursor, Codex, and other tools that honor AGENTS.md).
A pnpm monorepo providing Ryzome canvas tools for AI agents via multiple integration surfaces:
packages/ryzome-core(@ryzome-ai/ryzome-core) — Shared logic: API client, 11 tools, graph builder, layout, canvas markdown formatterpackages/openclaw-ryzome(@ryzome-ai/openclaw-ryzome) — OpenClaw plugin adapter (thin wrapper over core)packages/hermes-ryzome(hermes-ryzome-pluginon PyPI) — Hermes plugin source package. Standard Hermes install repo:0xPlaygrounds/hermes-ryzome-plugin. This monorepo copy stays tied to the sharedryzome-coredevelopment flow.packages/ryzome-mcp(@ryzome-ai/ryzome-mcp) — MCP server with tools + resources for Claude Code / any MCP clientpackages/ryzome-claude-plugin(@ryzome-ai/ryzome-claude-plugin) — Claude Code plugin (skills, agent, hooks)
Code for the full Ryzome app is under a collocated monorepo at ../ryzome-monorepo of this repository.
pnpm -r build # Build all packages
pnpm -r test # Unit tests across all packages
pnpm -r --if-present typecheck # tsc --noEmit in each package
pnpm -r --if-present lint # biome lint + typecheck
pnpm format # biome format --write (root)
pnpm format:check # biome format check (CI mode)
pnpm changeset # Create a changeset for version bumps
pnpm version-packages # Apply changesets + sync adapter metadata
pnpm release # Build + publish all changed packages
# Per-package
pnpm --filter @ryzome-ai/ryzome-core test
pnpm --filter @ryzome-ai/ryzome-mcp test
pnpm --filter @ryzome-ai/ryzome-core test -- --testPathPattern=layoutIntegration tests hit the live Ryzome API and are gated by env vars: RYZOME_ENABLE_LIVE_SMOKE, RYZOME_LIVE_SMOKE_API_KEY, RYZOME_LIVE_SMOKE_API_URL.
ryzome-core — shared logic, all other packages depend on this:
packages/ryzome-core/src/config.ts— Resolves config from env vars (RYZOME_OPENCLAW_API_KEY,RYZOME_API_KEY,PLUGIN_USER_CONFIG_API_KEY). Supports${ENV_VAR}syntax.packages/ryzome-core/src/lib/ryzome-client.ts—openapi-fetch-based API client.RyzomeApiErrormarks 408/429/5xx as retryable.packages/ryzome-core/src/lib/graph-builder.ts— Converts tool steps intocreateNode/createEdge/setNodeColorpatch operations. Supports node coloring and group containers. DAG depths via BFS.packages/ryzome-core/src/lib/layout.ts— Positions nodes by depth level (320px wide, 80px H / 60px V gaps).packages/ryzome-core/src/lib/canvas-executor.ts— Orchestrates create → build graph → patch → return URL.packages/ryzome-core/src/lib/format-canvas-markdown.ts— ConvertsCanvasEditorViewto LLM-readable markdown. Used by MCP resources.packages/ryzome-core/src/lib/retry.ts— Max 2 retries with exponential backoff (250ms base).packages/ryzome-core/src/tools/— 11 tools spanning canvas creation, document CRUD, library promotion, and image upload. Zod schemas + execute functions.
ryzome-mcp — MCP server (packages/ryzome-mcp/src/server.ts):
- Registers all tools from
toolRegistry - Static resources
ryzome://canvasesandryzome://documents— JSON list of canvas / document summaries - Dynamic resources
ryzome://canvas/{id}andryzome://document/{id}— single canvas / document as markdown (viaResourceTemplatewithlistcallback) - Advertised MCP server version is read from
packages/ryzome-mcp/package.jsonat startup
openclaw-ryzome — OpenClaw plugin adapter (packages/openclaw-ryzome/src/index.ts): thin wrapper registering core tools + CLI commands.
Onboarding invariants (to avoid re-debugging the same phantom):
- Entry uses
definePluginEntryfromopenclaw/plugin-sdk/plugin-entry; no hand-rolledPluginApitype. - Tools register unconditionally regardless of whether the API key is set. The api-key check is lazy: each tool's
executeresolves config at call time and throws a setup-hint error if missing. Do not reintroduce an early return inregister()— that's what made the plugin appear "broken" with no tools visible. - The manifest declares
contracts.tools(all 11 tool names) andactivation.onCommands: ["ryzome"].contracts.toolsis what makes OpenClaw auto-allowlistopenclaw-ryzomeintoplugins.allowonce a config entry exists — agents should not "fix" missing tools by writing toplugins.allowfrom the plugin CLI. - Onboarding path is
openclaw ryzome setup --key <api-key>(andopenclaw ryzome statusto verify). The globalopenclaw setupwizard does not have a tool-plugin step today; upstream request tracked at openclaw/openclaw#68115.
hermes-ryzome — Hermes plugin source package (packages/hermes-ryzome). Standard user install path is the standalone repo 0xPlaygrounds/hermes-ryzome-plugin via hermes plugins install 0xPlaygrounds/hermes-ryzome-plugin --enable, which prompts for RYZOME_API_KEY from plugin.yaml and saves it to ~/.hermes/.env. Hermes general plugins should prefer requires_env + tools + optional slash commands (here: /ryzome-status), not ctx.register_cli_command(). Do not tell users to symlink packages/hermes-ryzome unless they are developing against this monorepo.
ryzome-claude-plugin — Claude Code plugin (no build step): .claude-plugin/plugin.json manifest, .mcp.json bundled server, skills (/plan, /research, /ryzome-status), ryzome-context agent. Install flow: /plugin marketplace add 0xPlaygrounds/ryzome-mcp-plugins then /plugin install claude-ryzome.
Onboarding invariants (to avoid re-debugging the same phantom):
.mcp.jsoninjectsuserConfigvalues via${user_config.<key>}. Not${PLUGIN_USER_CONFIG_<KEY>}— that syntax is treated as a literal parent-shell env var lookup and silently substitutes to empty string./doctorwill flag "Missing environment variables: PLUGIN_USER_CONFIG_" which is the tell.hooks/hooks.jsonmust use the nested shape{ hooks: { SessionStart: [{ hooks: [{ type: "command", command: "..." }] }] } }. The flat{ command, description }form that works in~/.claude/settings.jsonwill fail plugin validation with a Zod error..claude-plugin/plugin.jsonhas noscopesfield — don't reintroduce one. Hooks/skills/agents are auto-discovered fromhooks/,skills/,agents/subdirectories;.mcp.jsonis auto-loaded from the plugin root.userConfig.<key>entries require bothtypeandtitle.sensitive: trueroutes storage to the OS keychain./reload-pluginsre-reads manifests (you'll see hook count update) but does not restart running MCP subprocesses. Env changes require a full Claude Code restart.- The marketplace name (
.claude-plugin/marketplace.json→name: "ryzome") is a brand identifier; the plugin name (.claude-plugin/plugin.json→name: "claude-ryzome") is the install identifier used in/plugin install <name>. These are independent — thesupermemoryai/claude-supermemoryconvention is brand/marketplace-name +claude-<brand>plugin-name. - Version sync:
scripts/sync-claude-plugin-version.mjsruns duringpnpm version-packagesto keepplugin.jsonand the rootmarketplace.jsonentry in lockstep withpackage.json.
Data flow for canvas creation:
Tool params → Zod validation → canvas-executor
→ RyzomeClient.createCanvas()
→ graph-builder → layout → RyzomeClient.patchCanvas()
→ returns viewer URL + stats
Tool return format: All tools return { content: [{ type: "text", text: string }] }.
- Runtime: Node, ESM (
"type": "module") - Package manager: pnpm 10.31.0
- Monorepo: pnpm workspaces (
packages/*) - Linting/formatting: Biome (no ESLint or Prettier)
- Testing: Vitest with globals enabled
- Schema: Zod v4 (single source of truth);
z.toJSONSchema()for JSON Schema generation - API client: openapi-fetch with generated types in
packages/ryzome-core/src/lib/client/schema.d.ts - MCP SDK:
@modelcontextprotocol/sdkfor tools + resources inryzome-mcp
packages/ryzome-core/src/lib/client/index.ts defines PatchOperation as a narrowed Extract<> of the full Operation union from schema.d.ts. It only includes the operation types the plugin actually uses. When adding a new canvas capability (e.g. a new patch operation type), widen this type first — downstream code won't compile until you do.
schema.d.ts is auto-generated from the backend OpenAPI spec (cargo run -p canvas-routes --bin generate-openapi in the monorepo). When the backend has a route the spec hasn't been regenerated for, you can manually add paths/types with a NOTE: Manually added — regenerate later comment. Run pnpm codegen:all in the monorepo to regenerate all client specs.
GitHub Actions runs lint, typecheck, and tests on every push to main and on PRs. Live smoke tests run as a separate gated job.
- Any PR with release-relevant changes under
packages/should include a changeset file created withpnpm changeset. - Treat code, config, manifest, or shipped asset changes in published packages as release-relevant by default. Pure test-only changes and Markdown-only documentation edits do not need a changeset.
- Do not leave changeset creation for later in the flow. Add or update the changeset in the same PR as the package change so CI can enforce it.
Publishing uses Changesets for independent per-package versioning:
- Each package is versioned independently —
pnpm changesetto describe changes - On push to main,
changesets/actioneither creates a "Version Packages" PR (if pending changesets exist) or publishes stable releases (if a version PR was just merged) - Dev snapshots are published on every main push under the
devnpm tag when there are pending changesets scripts/sync-plugin-version.mjskeepsopenclaw.plugin.jsonversion in sync with theopenclaw-ryzomepackage versionscripts/sync-hermes-plugin-version.mjskeepspackages/hermes-ryzome/plugin.yaml,pyproject.toml, and__version__in sync with thehermes-ryzomepackage version