@nyuchi/mzizi-cli
v0.3.0
Published
Mzizi SDK — the AI agent (Fundi) that reads the Mzizi skills + guidelines and helps consumers explore and set up what's missing in their project.
Maintainers
Readme
@nyuchi/mzizi-cli
fundi — the Mzizi architecture agent. Reads the mzizi design system (skills + live registry MCP) and helps you explore, plan, and scaffold any project against it.
fundi is the agent identity. @nyuchi/mzizi-cli is the npm package. When a host application
calls createFundi(), the SDK spawns fundi to:
- Read mzizi's skills + guidelines (bundled
@nyuchi/mzizi-skills+ the live@nyuchi/mzizi-mcpregistry server). - Explore the consumer's project to discover what is already wired —
globals.css,tsconfig.json,components.json,tailwind.config.*, token layers, harness wiring. - Plan and apply the missing pieces — tokens, primitives, brand components, MDX styling, harness, resilience, and so on.
It reasons about each project's state and acts within the mzizi / bundu doctrine.
The package is part of the mzizi-tools monorepo (nyuchi/mzizi-tools) — an
open-architecture project of the Bundu Foundation, operated by Nyuchi.
Install
npm install @nyuchi/mzizi-cli
# or
pnpm add @nyuchi/mzizi-cliNode >= 20 is required.
createFundi(opts) — SDK API
import { createFundi } from "@nyuchi/mzizi-cli"
const fundi = await createFundi({
projectRoot: process.cwd(),
anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
mcpUrl: process.env.MZIZI_MCP_URL, // optional; defaults to https://mcp.mzizi.dev/mcp
model: process.env.MZIZI_MODEL, // optional; defaults to claude-sonnet-4-6
})createFundi(opts) returns a FundiClient. The SDK is intentionally env-agnostic — it
does not read process.env for the Anthropic key or MCP URL. Pass everything explicitly. (The
thin CLI layer is the only part that reads env, so library consumers stay in full control.)
FundiOptions
| Field | Type | Required | Default |
| ----------------- | ------------------ | -------- | ---------------------------- |
| projectRoot | string | no | process.cwd() |
| anthropicApiKey | string | yes | — |
| mcpUrl | string | no | https://mcp.mzizi.dev/mcp |
| model | string | no | claude-sonnet-4-6 |
| maxTokens | number | no | 8192 |
| anthropic | Anthropic | no | new instance from key |
| auth | FundiAuthOptions | no | resolved from env (see Auth) |
| requireAuth | boolean | no | false |
FundiClient methods
.explore() — pure disk read
const snapshot = await fundi.explore()
// {
// root: "/abs/path",
// packageJson: { ... },
// hasTsconfig: true,
// hasGlobalsCss: false,
// tailwindConfigPath: "tailwind.config.ts",
// hasComponentsJson: true,
// notes: []
// }Reads a small, well-known set of disk markers (package.json, tsconfig.json,
app/globals.css, tailwind.config.*, components.json) and returns a ProjectSnapshot. No
Claude call is made; no API key is needed.
.plan(goal, snapshot) — Claude tool-use loop, read-only
const plan = await fundi.plan("add the mzizi token layer", snapshot)
// preview without touching disk
const preview = await plan.apply({ dryRun: true })
for (const r of preview.results) {
console.log(`[${r.step.kind}] ${r.step.description}`)
}
// apply for real
await plan.apply({ dryRun: false })Runs a Claude tool-use loop in read-only mode — the side-effect tools (write_file,
run_shell) are blocked during planning. Returns a Plan whose .apply() method walks the
steps. Pass dryRun: true for a preview; dryRun: false writes files and shells out.
.chat(message) — single-turn chat
const reply = await fundi.chat("what is the nyuchi mineral palette?")
console.log(reply)One-shot chat scoped to the mzizi doctrine system prompt. Returns a plain string.
The 6 agent tools
| Tool | Phase | Notes |
| ----------------------- | ---------- | --------------------------------------------------------------------------------------------------- |
| read_file | read-only | UTF-8 file read; sandboxed to project root |
| list_dir | read-only | Directory listing; sandboxed to project root |
| fetch_mzizi_skill | read-only | Reads a named skill from the bundled @nyuchi/mzizi-skills package |
| fetch_mzizi_component | read-only | Calls get_component on the live mzizi-mcp over Streamable HTTP; returns the full component JSON |
| write_file | apply only | Sandboxed; blocked in read-only / plan mode |
| run_shell | apply only | Argv-style, no shell interpolation; blocked in read-only / plan mode |
Sandboxing
All file-system tools resolve paths through resolveWithin(root, rel). Any path that
escapes the configured projectRoot — via .., symlinks, or absolute paths — is rejected
with an error rather than silently permitted. Neither the planning loop nor the apply phase
can read or write outside the project root.
fetch_mzizi_component and mzizi-mcp
fetch_mzizi_component connects to the mzizi-mcp registry at https://mcp.mzizi.dev/mcp
(override via mcpUrl) using @modelcontextprotocol/sdk's Streamable HTTP transport. The
MCP client is cached per endpoint so repeated calls in the same agent loop reuse the
connection. The mzizi registry is gated by a free WorkOS signup — pass a WorkOS access token
via auth.accessToken (or WORKOS_ACCESS_TOKEN env) so the agent can reach it.
CLI — fundi
The package ships a thin fundi CLI that wraps the SDK. It is the only layer in this
package that reads env vars — the SDK itself stays env-agnostic.
fundi --help
fundi --version
# no API key needed — reads disk only
fundi explore
# needs ANTHROPIC_API_KEY; prints a dry-run plan
fundi plan "add the mzizi token layer and a button"
# one-shot chat; needs ANTHROPIC_API_KEY
fundi chat "what is the nyuchi mineral palette?"
# auth — save a WorkOS token so plan/chat can reach the mzizi-mcp registry
fundi login --token "$WORKOS_ACCESS_TOKEN"
fundi whoami
fundi logoutThe saved token is stored at $XDG_CONFIG_HOME/mzizi/auth.json (default
~/.config/mzizi/auth.json) with mode 0600.
Environment variables
| Variable | Used by | Notes |
| --------------------- | -------------- | ---------------------------------------------------------- |
| ANTHROPIC_API_KEY | plan, chat | Required for Claude calls |
| MZIZI_MCP_URL | plan, chat | Override MCP endpoint; default https://mcp.mzizi.dev/mcp |
| MZIZI_MODEL | plan, chat | Override model id; default claude-sonnet-4-6 |
| WORKOS_ACCESS_TOKEN | plan, chat | WorkOS token for the gated mzizi-mcp |
| MZIZI_AUTH_TOKEN | plan, chat | Ecosystem-wide alias for WORKOS_ACCESS_TOKEN |
explore runs offline and needs none of these.
Auth
@nyuchi/mzizi-cli resolves a WorkOS access token to authenticate outbound calls to the
mzizi-mcp registry. Resolution order:
options.auth.accessToken— explicit token wins.process.env.WORKOS_ACCESS_TOKEN— the canonical env var.process.env.MZIZI_AUTH_TOKEN— ecosystem-wide alias shared with the fundi worker.
With the default requireAuth: false, a missing token does not prevent construction — the
identity field is null and fetch_mzizi_component will receive a 401 from the registry.
Set requireAuth: true in production paths where auth-gated calls are required; the
constructor throws an AuthError immediately if no token can be resolved.
import { createFundi, getIdentity, type Identity } from "@nyuchi/mzizi-cli"
const fundi = await createFundi({
anthropicApiKey: process.env.ANTHROPIC_API_KEY!,
auth: { accessToken: myWorkOsAccessToken },
requireAuth: true,
})
console.log(fundi.identity?.email)
console.log(fundi.identity?.permissions)
// standalone helper — decode any WorkOS access token
const identity: Identity = getIdentity(myWorkOsAccessToken)Identity mirrors WorkOS AccessToken claims:
interface Identity {
sub: string // WorkOS user id
email?: string
organizationId?: string
permissions: string[] // WorkOS permission strings
exp: number // expiry, seconds since Unix epoch
}The SDK uses jose.decodeJwt to read claims — it does not verify the signature.
Signature verification is the responsibility of the resource server (mzizi-mcp, etc.). The SDK
does enforce exp; an expired token raises AuthError immediately.
Plan step types
.plan() returns a Plan with a steps: Step[] array. Each step has a kind:
| Kind | Description |
| ------------------- | --------------------------------------------------------- |
| install-component | Installs shadcn component(s) via npx shadcn add |
| create-file | Creates a new file at a relative path with given contents |
| modify-file | Replaces a file's contents (full replacement) |
| run-command | Runs an argv-style shell command in the project |
Type guards are exported: isInstallComponentStep, isCreateFileStep,
isModifyFileStep, isRunCommandStep.
Self-healing error emitter (planned)
The CLI is the designated richest error emitter in the mzizi self-healing system. It is
positioned to see what the edge cannot: failed plan / apply steps, run_shell failures,
and — when wired into a consumer runtime — uncaught errors in production. The emitter (stage 3
of the self-healing build) will call record_observability_event with structured diagnostics,
degrade gracefully offline, and feed fundi's watching-and-healing loop.
See docs/self-healing.md §5.2 for the emitter contract, §2 for
the full loop, and §10 for the build stages.
License
Apache-2.0. Governed by the Bundu Foundation, operated by Nyuchi.
