My Claw: Build Your AI System with Claude Code
The Substrate
Talk to Claude Code programmatically and get structured JSON back. Four files. The atom everything else builds on.
You have Claude Code installed. You've used it interactively. Typing prompts, watching it think, reading responses. But that's you sitting at a terminal. A personal AI system needs to call Claude Code from code, parse what comes back, and track conversations across invocations.
That's what this lesson builds. Four files. A script that sends a prompt, gets structured JSON, and maintains session continuity. The simplest possible claw.
What you're building
A Node.js script (claw.mjs) that:
After this lesson, you can run:
node claw.mjs "What's in this directory?"
node claw.mjs "What did I just ask you?" # it remembersThe atom
Create four files:
my-claw/
claw.mjs # the script
CLAUDE.md # identity
package.json # metadata
.gitignore # keep secrets out of gitpackage.json
{
"name": "my-claw",
"version": "0.1.0",
"type": "module"
}CLAUDE.md
# My Claw
You are a personal assistant. Be concise and helpful.
Check `skills/` for available capabilities and use them when relevant.This is your claw's identity. Claude Code reads it automatically on every invocation from the working directory. One file, immediate behavioral shift. The cheapest, highest-leverage change you can make. The skills/ line does nothing yet (the directory is empty), but it sets the claw up to discover skills as you add them.
claw.mjs
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
const SESSION_FILE = ".claw-session";
const prompt = process.argv.slice(2).join(" ");
if (!prompt) {
console.log("Usage: node claw.mjs <prompt>");
process.exit(0);
}
// Build command args. execFileSync passes args directly, no shell quoting issues
const args = ["-p", prompt, "--output-format", "json"];
// Resume session if one exists
const sessionId = existsSync(SESSION_FILE)
? readFileSync(SESSION_FILE, "utf-8").trim()
: null;
if (sessionId) {
args.push("--resume", sessionId);
}
try {
const raw = execFileSync("claude", args, {
cwd: import.meta.dirname,
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
});
const result = JSON.parse(raw);
if (result.is_error) {
console.error("Error:", result.result);
process.exit(1);
}
// Save session for next time
if (result.session_id) {
writeFileSync(SESSION_FILE, result.session_id);
}
console.log(result.result);
// Metadata when VERBOSE is set
if (process.env.VERBOSE) {
console.log("\n---");
console.log(`Session: ${result.session_id}`);
console.log(`Cost: $${result.total_cost_usd?.toFixed(4) ?? "?"}`);
console.log(`Turns: ${result.num_turns}`);
console.log(`Duration: ${(result.duration_ms / 1000).toFixed(1)}s`);
}
} catch (err) {
console.error("Failed:", err.message);
process.exit(1);
}.gitignore
node_modules/
.claw-session
.envWhy execFileSync, not execSync
execFileSync passes arguments as an array directly to the subprocess. execSync joins everything into a string and runs it through bash. Multi-line prompts with quotes and newlines get mangled by bash. This will bite you the moment your prompts contain JSON or markdown. Use execFileSync for all Claude CLI calls.
cwd: import.meta.dirname ensures Claude Code always runs from the project directory, no matter where you invoke the script from. Claude Code reads CLAUDE.md and skills/ from the working directory. Without cwd, running node ~/my-claw/claw.mjs from your home directory means Claude never finds its identity or skills.
Try it
node claw.mjs "What is 2 + 2?"You should see: 4 (or something equally concise, your CLAUDE.md says "be concise").
VERBOSE=1 node claw.mjs "What directory am I in?"Now you see the metadata: session ID, cost, turns, duration. The system tells you about itself.
node claw.mjs "What did I just ask you?"Session continuity. It remembers. The .claw-session file tracks the conversation.
What you have
A four-file claw that talks to Claude Code programmatically, returns structured JSON, and maintains conversational continuity. Everything else in this course layers on top of this atom.
What's missing
The claw answers when asked but doesn't know anything about your life. It has no context beyond what you type. Next, you give it real data to work with.