Module 6: Advanced Hooks and Headless SDK
5. Headless SDK — TypeScript: Claude Code in the Node.js Ecosystem
5. Headless SDK — TypeScript: Claude Code in the Node.js Ecosystem
Description
If your main stack is JavaScript or TypeScript, you don't need to switch to Python to automate Claude Code. Everything you learned in the previous capsule — headless mode with -p, JSON output, --allowedTools — works the same from Node.js. But the TypeScript ecosystem adds advantages: static typing to parse Claude's responses, direct integration with build tools like esbuild and Vite, and the @anthropic-ai/claude-code package that offers a native API without going through subprocess.
This capsule covers two approaches: subprocess (using Node.js's child_process, similar to Python) and the SDK package (@anthropic-ai/claude-code). The first approach works in any environment where the Claude CLI is installed. The second offers a cleaner, typed API, but requires installing the package.
By the end you'll know how to run Claude Code from TypeScript scripts, integrate the automation with your Node.js tooling (build scripts, deploy scripts, monitoring), and choose between Python and TypeScript based on the use case.
Approach 1: subprocess with child_process
The basic pattern
Node.js has child_process as the equivalent of Python's subprocess:
import { execSync } from "child_process";
const result = execSync(
'claude -p "How many TypeScript files are in src/?" --output-format json --allowedTools "Read,Glob,Grep"',
{ encoding: "utf-8", timeout: 60000 }
);
const output = JSON.parse(result);
console.log(output.result);
console.log(`Cost: $${output.cost_usd?.toFixed(4)}`);
execSync vs exec vs spawn
| Method | Type | When to use it |
|---|---|---|
execSync | Synchronous, blocks | Simple scripts, short tasks |
exec | Async with callback | Medium-duration tasks |
spawn | Async with streams | Long executions, streaming |
Reusable function with typing
import { execSync } from "child_process";
interface ClaudeResult {
type: string;
subtype: string;
is_error: boolean;
result: string;
session_id: string;
cost_usd: number;
duration_ms: number;
num_turns: number;
}
interface ClaudeError {
is_error: true;
error: string;
error_type: "timeout" | "not_found" | "parse_error" | "exit_code";
}
type ClaudeOutput = ClaudeResult | ClaudeError;
function runClaude(
prompt: string,
tools: string[] = [],
timeoutMs: number = 300000
): ClaudeOutput {
const toolsArg = tools.length > 0
? `--allowedTools "${tools.join(",")}"`
: "";
const cmd = `claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json ${toolsArg}`;
try {
const result = execSync(cmd, {
encoding: "utf-8",
timeout: timeoutMs,
maxBuffer: 10 * 1024 * 1024,
});
return JSON.parse(result) as ClaudeResult;
} catch (error: any) {
if (error.killed) {
return { is_error: true, error: "Timeout", error_type: "timeout" };
}
if (error.code === "ENOENT") {
return { is_error: true, error: "claude not found", error_type: "not_found" };
}
return {
is_error: true,
error: error.message || "Unknown error",
error_type: "exit_code",
};
}
}
const output = runClaude(
"List the exported functions in src/api/",
["Read", "Grep", "Glob"]
);
if (output.is_error) {
console.error(`Error: ${(output as ClaudeError).error}`);
} else {
const result = output as ClaudeResult;
console.log(result.result);
console.log(`Cost: $${result.cost_usd.toFixed(4)}`);
}
Async version with exec
For scripts that need to run multiple invocations without blocking:
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
async function runClaudeAsync(
prompt: string,
tools: string[] = [],
timeoutMs: number = 300000
): Promise<ClaudeResult | ClaudeError> {
const toolsArg = tools.length > 0
? `--allowedTools "${tools.join(",")}"`
: "";
const cmd = `claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json ${toolsArg}`;
try {
const { stdout } = await execAsync(cmd, {
encoding: "utf-8",
timeout: timeoutMs,
maxBuffer: 10 * 1024 * 1024,
});
return JSON.parse(stdout) as ClaudeResult;
} catch (error: any) {
return {
is_error: true,
error: error.message || "Unknown error",
error_type: "exit_code",
};
}
}
async function main() {
const result = await runClaudeAsync(
"Analyze src/components/ and list all the React components",
["Read", "Glob", "Grep"]
);
if (!result.is_error) {
console.log((result as ClaudeResult).result);
}
}
main();
Approach 2: The @anthropic-ai/claude-code Package
Installation
npm install @anthropic-ai/claude-code
Basic API
The package offers a cleaner API than subprocess:
import { claude } from "@anthropic-ai/claude-code";
async function main() {
const result = await claude({
prompt: "Analyze src/ and report the project structure",
allowedTools: ["Read", "Glob", "Grep"],
options: {
maxTurns: 10,
},
});
console.log(result.text);
console.log(`Cost: $${result.costUsd.toFixed(4)}`);
}
main();
Native typing
The package's main advantage is the typing:
import { claude, ClaudeResult } from "@anthropic-ai/claude-code";
async function analyzeModule(path: string): Promise<ClaudeResult> {
return claude({
prompt: `Analyze ${path} and report: structure, dependencies, issues`,
allowedTools: ["Read", "Glob", "Grep"],
options: {
maxTurns: 15,
},
});
}
async function main() {
const result = await analyzeModule("src/api/");
if (result.isError) {
console.error("Analysis failed:", result.text);
return;
}
console.log("Analysis:", result.text);
console.log("Turns used:", result.numTurns);
console.log("Duration:", result.durationMs, "ms");
}
main();
Streaming with the package
import { claude } from "@anthropic-ai/claude-code";
async function main() {
const stream = claude.stream({
prompt: "Generate a code quality report for src/",
allowedTools: ["Read", "Glob", "Grep"],
});
for await (const event of stream) {
if (event.type === "text") {
process.stdout.write(event.content);
}
}
const result = await stream.finalResult();
console.log(`\n\nCost: $${result.costUsd.toFixed(4)}`);
}
main();
Automation Scripts in TypeScript
Script 1: Component Documentation Generator
#!/usr/bin/env npx tsx
/**
* Generates automatic documentation for React components.
* Usage: npx tsx scripts/gen-component-docs.ts
*/
import { execSync } from "child_process";
import * as fs from "fs";
import * as path from "path";
interface ClaudeResult {
result: string;
is_error: boolean;
cost_usd: number;
}
function runClaude(prompt: string, tools: string[]): ClaudeResult | null {
try {
const toolsStr = tools.join(",");
const result = execSync(
`claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json --allowedTools "${toolsStr}"`,
{ encoding: "utf-8", timeout: 120000 }
);
return JSON.parse(result);
} catch {
return null;
}
}
function getComponentFiles(): string[] {
const result = execSync(
'find src/components -name "*.tsx" -not -name "*.test.*" -not -name "*.spec.*"',
{ encoding: "utf-8" }
);
return result.trim().split("\n").filter(Boolean);
}
function main() {
const components = getComponentFiles();
console.log(`Found ${components.length} components\n`);
const docs: string[] = ["# Component Documentation\n"];
let totalCost = 0;
for (const file of components) {
console.log(`Documenting: ${file}`);
const output = runClaude(
`Read ${file} and generate documentation for the React component it contains.
Include: name, props (with types), description, usage example.
Markdown format. Be concise.`,
["Read"]
);
if (output && !output.is_error) {
docs.push(output.result);
docs.push("\n---\n");
totalCost += output.cost_usd || 0;
}
}
const outputPath = "docs/components.md";
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
fs.writeFileSync(outputPath, docs.join("\n"));
console.log(`\nDocs written to ${outputPath}`);
console.log(`Total cost: $${totalCost.toFixed(4)}`);
}
main();
Script 2: Build Validator
#!/usr/bin/env npx tsx
/**
* Validates the build and asks Claude to fix errors.
* Usage: npx tsx scripts/build-validator.ts
*/
import { execSync } from "child_process";
interface ClaudeResult {
result: string;
is_error: boolean;
cost_usd: number;
}
function runBuild(): { success: boolean; output: string } {
try {
const output = execSync("npm run build 2>&1", { encoding: "utf-8" });
return { success: true, output };
} catch (error: any) {
return { success: false, output: error.stdout || error.message };
}
}
function fixWithClaude(buildErrors: string): ClaudeResult | null {
const prompt = `The TypeScript build is failing with these errors:
${buildErrors}
Read the files mentioned in the errors and fix them.
Only fix TypeScript errors (types, imports, syntax).
Don't change the business logic.`;
try {
const result = execSync(
`claude -p "${prompt.replace(/"/g, '\\"').replace(/\n/g, "\\n")}" --output-format json --allowedTools "Read,Write,Edit,Grep,Glob"`,
{ encoding: "utf-8", timeout: 300000 }
);
return JSON.parse(result);
} catch {
return null;
}
}
function main() {
const maxAttempts = 3;
let totalCost = 0;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
console.log(`\n--- Build attempt ${attempt}/${maxAttempts} ---`);
const { success, output } = runBuild();
if (success) {
console.log("Build successful!");
console.log(`Total fix cost: $${totalCost.toFixed(4)}`);
return;
}
console.log("Build failed. Asking Claude to fix...");
const fix = fixWithClaude(output.slice(0, 3000));
if (fix && !fix.is_error) {
totalCost += fix.cost_usd || 0;
console.log(`Fix applied ($${(fix.cost_usd || 0).toFixed(4)})`);
} else {
console.log("Claude fix failed");
}
}
console.log(`Build still failing after ${maxAttempts} attempts`);
console.log(`Total cost: $${totalCost.toFixed(4)}`);
process.exit(1);
}
main();
Script 3: Project Monitoring and Report
#!/usr/bin/env npx tsx
/**
* Generates a project health report.
* Usage: npx tsx scripts/project-health.ts
*/
import { execSync } from "child_process";
import * as fs from "fs";
interface ClaudeResult {
result: string;
is_error: boolean;
cost_usd: number;
duration_ms: number;
}
function runClaude(prompt: string, tools: string[]): ClaudeResult | null {
try {
const result = execSync(
`claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json --allowedTools "${tools.join(",")}"`,
{ encoding: "utf-8", timeout: 300000, maxBuffer: 10 * 1024 * 1024 }
);
return JSON.parse(result);
} catch {
return null;
}
}
interface HealthCheck {
name: string;
prompt: string;
tools: string[];
}
const checks: HealthCheck[] = [
{
name: "Code Quality",
prompt: "Analyze src/ for code quality: complexity, duplication, naming. Score 1-10 with a brief justification.",
tools: ["Read", "Grep", "Glob"],
},
{
name: "Security",
prompt: "Look in src/ for security problems: secrets, injection, eval, unsafe patterns. List findings with severity.",
tools: ["Read", "Grep", "Glob"],
},
{
name: "Dependencies",
prompt: "Analyze package.json and look for: outdated deps, unused deps, known vulnerabilities. Report findings.",
tools: ["Read", "Grep", "Glob"],
},
];
async function main() {
console.log("Project Health Report\n");
const results: { name: string; result: string; cost: number }[] = [];
let totalCost = 0;
for (const check of checks) {
console.log(`Running: ${check.name}...`);
const output = runClaude(check.prompt, check.tools);
if (output && !output.is_error) {
results.push({
name: check.name,
result: output.result,
cost: output.cost_usd || 0,
});
totalCost += output.cost_usd || 0;
} else {
results.push({
name: check.name,
result: "Check failed",
cost: 0,
});
}
}
const report = [
`# Project Health Report — ${new Date().toISOString().split("T")[0]}`,
"",
...results.map((r) => [
`## ${r.name}`,
"",
r.result,
"",
`*Cost: $${r.cost.toFixed(4)}*`,
"",
"---",
"",
]).flat(),
`**Total cost: $${totalCost.toFixed(4)}**`,
].join("\n");
fs.mkdirSync("reports", { recursive: true });
const reportPath = `reports/health-${new Date().toISOString().split("T")[0]}.md`;
fs.writeFileSync(reportPath, report);
console.log(`\nReport saved: ${reportPath}`);
console.log(`Total cost: $${totalCost.toFixed(4)}`);
}
main();
Parallel Execution in TypeScript
Promise.all for concurrent analysis
import { execSync } from "child_process";
interface ClaudeResult {
result: string;
is_error: boolean;
cost_usd: number;
}
async function analyzeModuleAsync(modulePath: string): Promise<{
path: string;
analysis: ClaudeResult | null;
}> {
return new Promise((resolve) => {
try {
const output = execSync(
`claude -p "Analyze ${modulePath}: structure, exports, issues. Concise." --output-format json --allowedTools "Read,Grep,Glob"`,
{ encoding: "utf-8", timeout: 120000 }
);
resolve({ path: modulePath, analysis: JSON.parse(output) });
} catch {
resolve({ path: modulePath, analysis: null });
}
});
}
async function main() {
const modules = ["src/api/", "src/components/", "src/services/"];
console.log(`Analyzing ${modules.length} modules in parallel...\n`);
const results = await Promise.all(
modules.map((m) => analyzeModuleAsync(m))
);
let totalCost = 0;
for (const { path, analysis } of results) {
console.log(`\n## ${path}`);
if (analysis && !analysis.is_error) {
console.log(analysis.result.slice(0, 300));
totalCost += analysis.cost_usd || 0;
} else {
console.log("Analysis failed");
}
}
console.log(`\nTotal cost: $${totalCost.toFixed(4)}`);
}
main();
Comparison: Python SDK vs TypeScript SDK
When to use Python
| Scenario | Why Python |
|---|---|
| CI/CD scripts | Python is the standard in DevOps and CI pipelines |
| Data processing | Pandas, numpy to analyze results |
| ML/AI workflows | Integration with the ML ecosystem |
| Backend automation | Migration, seeding, maintenance scripts |
| Quick scripts | Less boilerplate for one-off scripts |
When to use TypeScript
| Scenario | Why TypeScript |
|---|---|
| Build tools | Integration with esbuild, Vite, webpack |
| Frontend tooling | Scripts that interact with the frontend codebase |
| Type safety | Static typing to parse Claude's responses |
| Node.js ecosystem | npm scripts, JS development tools |
| Full-stack JS projects | Language consistency across the whole stack |
Direct comparison
| Aspect | Python | TypeScript |
|---|---|---|
| Invocation | subprocess.run() | execSync() / exec() |
| Async | asyncio / ThreadPoolExecutor | Promise.all() / async/await |
| JSON parsing | json.loads() | JSON.parse() |
| Typing | Optional (type hints) | Native and strict |
| SDK package | subprocess approach | @anthropic-ai/claude-code |
| Boilerplate | Less | More (types, interfaces) |
| Error handling | try/except | try/catch |
| Streaming | Popen + line iteration | spawn + event listeners |
| CI ecosystem | More common | Less common |
| Script runner | python script.py | npx tsx script.ts |
The decision rule
Is your project JS/TS? ──→ TypeScript
Is it a CI/CD script? ──→ Python
Do you need strong typing for the results? ──→ TypeScript
Is it a quick one-off script? ──→ Python
Does it integrate with JS build tools? ──→ TypeScript
Does it integrate with data pipelines? ──→ Python
If you're not sure: use the language you use most day-to-day. Both work equally well for invoking Claude Code in headless mode.
Integration with npm scripts
package.json
{
"scripts": {
"review": "npx tsx scripts/code-review.ts",
"docs:generate": "npx tsx scripts/gen-component-docs.ts",
"health": "npx tsx scripts/project-health.ts",
"build:fix": "npx tsx scripts/build-validator.ts",
"changelog": "npx tsx scripts/changelog.ts"
},
"devDependencies": {
"tsx": "^4.0.0",
"typescript": "^5.0.0"
}
}
npm run review
npm run docs:generate
npm run health
npm run build:fix
This integrates the automation scripts directly into your usual npm workflow.
Troubleshooting
"Cannot find module '@anthropic-ai/claude-code'"
Cause: The package isn't installed.
Solution:
npm install @anthropic-ai/claude-code
If the package isn't available in your version of npm or doesn't yet exist as a public package, use the subprocess approach, which works with any Claude CLI installation.
"execSync: command not found: claude"
Cause: The Claude CLI isn't in the PATH when Node.js runs the subprocess.
Solution:
import { execSync } from "child_process";
const claudePath = execSync("which claude", { encoding: "utf-8" }).trim();
const result = execSync(
`${claudePath} -p "prompt" --output-format json`,
{ encoding: "utf-8" }
);
"MaxBuffer exceeded"
Cause: Claude's output exceeds execSync's default buffer (1MB).
Solution:
const result = execSync(cmd, {
encoding: "utf-8",
maxBuffer: 10 * 1024 * 1024, // 10MB
});
"SyntaxError: Unexpected token in JSON"
Cause: The output includes non-JSON text (warnings, mixed stderr).
Solution:
const raw = execSync(cmd, { encoding: "utf-8" });
const jsonStart = raw.indexOf("{");
const jsonEnd = raw.lastIndexOf("}") + 1;
if (jsonStart >= 0 && jsonEnd > jsonStart) {
const output = JSON.parse(raw.slice(jsonStart, jsonEnd));
}
"Timeout with npx tsx"
Cause: npx tsx adds startup overhead. The execSync timeout may not be enough.
Solution: Increase the timeout or compile the script to JavaScript first:
npx tsc scripts/review.ts --outDir dist/
node dist/review.js
Exercises
Exercise 1: Headless "Hello World" in TypeScript (Easy)
Write a TypeScript script that runs Claude in headless mode to count .ts files in src/, parses the JSON, and prints the result and cost.
See solution
#!/usr/bin/env npx tsx
import { execSync } from "child_process";
const result = execSync(
'claude -p "How many .ts files are in src/?" --output-format json --allowedTools "Glob"',
{ encoding: "utf-8", timeout: 60000 }
);
const output = JSON.parse(result);
console.log(`Result: ${output.result}`);
console.log(`Cost: $${(output.cost_usd || 0).toFixed(4)}`);
Exercise 2: Reusable typed function (Easy)
Create an askClaude function with TypeScript types for input and output that encapsulates the headless invocation with complete error handling.
See solution
import { execSync } from "child_process";
interface ClaudeInput {
prompt: string;
tools?: string[];
timeoutMs?: number;
}
interface ClaudeSuccess {
is_error: false;
result: string;
cost_usd: number;
duration_ms: number;
}
interface ClaudeFailure {
is_error: true;
error: string;
}
type ClaudeOutput = ClaudeSuccess | ClaudeFailure;
function askClaude(input: ClaudeInput): ClaudeOutput {
const { prompt, tools = [], timeoutMs = 300000 } = input;
const toolsArg = tools.length ? `--allowedTools "${tools.join(",")}"` : "";
const cmd = `claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json ${toolsArg}`;
try {
const raw = execSync(cmd, {
encoding: "utf-8",
timeout: timeoutMs,
maxBuffer: 10 * 1024 * 1024,
});
const parsed = JSON.parse(raw);
return { is_error: false, result: parsed.result, cost_usd: parsed.cost_usd || 0, duration_ms: parsed.duration_ms || 0 };
} catch (error: any) {
return { is_error: true, error: error.message || "Unknown error" };
}
}
const output = askClaude({ prompt: "List exports of src/api/", tools: ["Read", "Grep"] });
if (!output.is_error) {
console.log(output.result);
} else {
console.error(output.error);
}
Exercise 3: Component docs generator (Medium)
Write a script that reads all the .tsx files in src/components/, asks Claude to document each one, and generates a docs/components.md file.
See solution
#!/usr/bin/env npx tsx
import { execSync } from "child_process";
import * as fs from "fs";
function runClaude(prompt: string, tools: string[]): string | null {
try {
const result = execSync(
`claude -p "${prompt.replace(/"/g, '\\"')}" --output-format json --allowedTools "${tools.join(",")}"`,
{ encoding: "utf-8", timeout: 120000, maxBuffer: 5 * 1024 * 1024 }
);
const parsed = JSON.parse(result);
return parsed.is_error ? null : parsed.result;
} catch {
return null;
}
}
const files = execSync('find src/components -name "*.tsx" -not -name "*.test.*"', { encoding: "utf-8" })
.trim()
.split("\n")
.filter(Boolean);
const docs: string[] = ["# Components\n"];
for (const file of files) {
console.log(`Documenting: ${file}`);
const doc = runClaude(`Read ${file}. Document: name, props, description, example. Concise Markdown.`, ["Read"]);
if (doc) {
docs.push(doc, "\n---\n");
}
}
fs.mkdirSync("docs", { recursive: true });
fs.writeFileSync("docs/components.md", docs.join("\n"));
console.log("Done: docs/components.md");
Exercise 4: Build fixer with retry (Medium)
Write a TypeScript script that: (1) runs npm run build, (2) if it fails, passes the errors to Claude to fix, (3) repeats up to 3 times, (4) reports whether it succeeded and the total cost.
See solution
#!/usr/bin/env npx tsx
import { execSync } from "child_process";
function build(): { ok: boolean; output: string } {
try {
return { ok: true, output: execSync("npm run build 2>&1", { encoding: "utf-8" }) };
} catch (e: any) {
return { ok: false, output: e.stdout || e.message };
}
}
function fixWithClaude(errors: string): number {
try {
const prompt = errors.slice(0, 3000).replace(/"/g, '\\"').replace(/\n/g, "\\n");
const result = execSync(
`claude -p "Fix these TypeScript build errors:\\n${prompt}" --output-format json --allowedTools "Read,Write,Edit,Grep,Glob"`,
{ encoding: "utf-8", timeout: 300000 }
);
return JSON.parse(result).cost_usd || 0;
} catch {
return 0;
}
}
let totalCost = 0;
for (let i = 1; i <= 3; i++) {
console.log(`\nAttempt ${i}/3`);
const { ok, output } = build();
if (ok) {
console.log(`Build passed! Cost: $${totalCost.toFixed(4)}`);
process.exit(0);
}
totalCost += fixWithClaude(output);
}
console.log(`Build still failing. Cost: $${totalCost.toFixed(4)}`);
process.exit(1);
Exercise 5: Parallel analysis with Promise.all (Hard)
Write a script that analyzes 4 directories in parallel, each with its own headless invocation, using Promise.all. Consolidate the results into a Markdown report.
See solution
#!/usr/bin/env npx tsx
import { exec } from "child_process";
import { promisify } from "util";
import * as fs from "fs";
const execAsync = promisify(exec);
async function analyze(dir: string): Promise<{ dir: string; result: string; cost: number }> {
try {
const { stdout } = await execAsync(
`claude -p "Analyze ${dir}: files, exports, issues. Concise." --output-format json --allowedTools "Read,Grep,Glob"`,
{ encoding: "utf-8", timeout: 120000, maxBuffer: 5 * 1024 * 1024 }
);
const parsed = JSON.parse(stdout);
return { dir, result: parsed.result || "No result", cost: parsed.cost_usd || 0 };
} catch (e: any) {
return { dir, result: `Error: ${e.message}`, cost: 0 };
}
}
async function main() {
const dirs = ["src/api/", "src/components/", "src/services/", "src/utils/"];
console.log(`Analyzing ${dirs.length} modules in parallel...\n`);
const results = await Promise.all(dirs.map(analyze));
const totalCost = results.reduce((sum, r) => sum + r.cost, 0);
const report = [
`# Module Analysis — ${new Date().toISOString().split("T")[0]}`,
"",
...results.flatMap((r) => [`## ${r.dir}`, "", r.result, "", "---", ""]),
`**Total cost: $${totalCost.toFixed(4)}**`,
].join("\n");
fs.mkdirSync("reports", { recursive: true });
fs.writeFileSync("reports/analysis.md", report);
console.log(`Report: reports/analysis.md ($${totalCost.toFixed(4)})`);
}
main();
Summary
- TypeScript offers two approaches for the headless SDK:
child_process(subprocess) and@anthropic-ai/claude-code(native package) execSyncfor simple scripts,exec(promisified) for async,spawnfor streaming- TypeScript's static typing gives safety when parsing Claude's JSON responses
- Scripts are run with
npx tsxfor direct TypeScript support without compilation Promise.allenables parallel analysis of multiple modules- Direct integration with npm scripts (
npm run review,npm run docs:generate) - Python vs TypeScript: Python for CI/CD and data, TypeScript for build tools and JS projects
- The SDK package (
@anthropic-ai/claude-code) offers a cleaner API but requires installation — subprocess always works maxBuffermust be increased (10MB+) for large Claude outputs
Additional Resources
- Claude Code CLI Reference — The
-p,--output-format,--allowedToolsflags - Node.js child_process — Official documentation of exec, execSync, spawn
- tsx (TypeScript Execute) — TypeScript runner for scripts
- TypeScript Handbook — Reference for types and interfaces
- Claude Code Best Practices — Automation best practices
- npm Scripts — Integration with npm
- Claude Code Hooks — Hooks that complement the SDK
- Claude Code Overview — General context of Claude Code
Next capsule: In capsule 06 (the project) you integrate everything: hooks for all the events + an SDK script that orchestrates the complete pipeline. SessionStart configures, PreToolUse validates, PostToolUse lints, SubagentStop reports, and a Python script triggers and processes the whole flow. The project closes the module by demonstrating that hooks + SDK turn Claude Code into an automated development system.