@secemp/elwood 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -0
  3. package/examples/01-basic-query.js +38 -0
  4. package/examples/02-bug-fixer.js +57 -0
  5. package/examples/03-custom-system-prompt.js +41 -0
  6. package/examples/04-read-only-agent.js +38 -0
  7. package/examples/05-find-todos.js +37 -0
  8. package/examples/06-session-resume.js +70 -0
  9. package/examples/07-hooks-pretooluse.js +74 -0
  10. package/examples/08-hooks-posttooluse-audit.js +66 -0
  11. package/examples/09-hooks-block-etc.js +68 -0
  12. package/examples/10-hooks-redirect-sandbox.js +70 -0
  13. package/examples/11-subagents.js +54 -0
  14. package/examples/12-mcp-stdio.js +48 -0
  15. package/examples/13-mcp-http.js +54 -0
  16. package/examples/14-custom-tool.js +84 -0
  17. package/examples/15-custom-tool-unit-converter.js +132 -0
  18. package/examples/16-mcp-github.js +71 -0
  19. package/examples/17-session-store-postgres.js +78 -0
  20. package/examples/18-session-store-redis.js +65 -0
  21. package/examples/19-session-store-s3.js +67 -0
  22. package/examples/20-session-list.js +72 -0
  23. package/examples/21-hooks-notification-slack.js +78 -0
  24. package/examples/22-hooks-webhook-posttooluse.js +78 -0
  25. package/examples/23-hooks-subagent-tracker.js +59 -0
  26. package/examples/24-v2-session-api.js +62 -0
  27. package/examples/README.md +95 -0
  28. package/examples/basic.js +240 -0
  29. package/examples/smoke-test.js +296 -0
  30. package/package.json +52 -0
  31. package/src/ast-tools.js +182 -0
  32. package/src/index.js +70 -0
  33. package/src/instrumenter.js +2921 -0
  34. package/src/loader.js +306 -0
  35. package/src/locate.js +296 -0
  36. package/src/query.js +2168 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * 10-hooks-redirect-sandbox.js
3
+ *
4
+ * Equivalent of the official "redirect writes to sandbox" hooks example.
5
+ *
6
+ * Official version:
7
+ * const redirectToSandbox: HookCallback = async (input, toolUseID, { signal }) => {
8
+ * if (input.hook_event_name !== "PreToolUse") return {};
9
+ * const preInput = input as PreToolUseHookInput;
10
+ * const toolInput = preInput.tool_input as Record<string, unknown>;
11
+ * if (preInput.tool_name === "Write") {
12
+ * const originalPath = toolInput.file_path as string;
13
+ * return {
14
+ * hookSpecificOutput: {
15
+ * hookEventName: preInput.hook_event_name,
16
+ * permissionDecision: "allow",
17
+ * updatedInput: { ...toolInput, file_path: `/sandbox${originalPath}` }
18
+ * }
19
+ * };
20
+ * }
21
+ * return {};
22
+ * };
23
+ *
24
+ * This elwood version intercepts Write tool calls and rewrites the file_path
25
+ * to prepend /sandbox, redirecting all file writes to a sandboxed directory.
26
+ *
27
+ * Prerequisites:
28
+ * - ANTHROPIC_API_KEY set in environment
29
+ * - Claude Code CLI installed
30
+ *
31
+ * Run:
32
+ * node examples/10-hooks-redirect-sandbox.js
33
+ */
34
+
35
+ import { query } from '../src/index.js';
36
+
37
+ // Redirect all file writes to a sandboxed directory
38
+ const redirectToSandbox = async (input, _toolUseID, { signal }) => {
39
+ if (input.hook_event_name !== 'PreToolUse') return {};
40
+
41
+ const toolInput = input.tool_input;
42
+ if (input.tool_name === 'Write' && typeof toolInput === 'object') {
43
+ const originalPath = toolInput.file_path;
44
+ return {
45
+ hookSpecificOutput: {
46
+ hookEventName: input.hook_event_name,
47
+ permissionDecision: 'allow',
48
+ updatedInput: {
49
+ ...toolInput,
50
+ file_path: `/sandbox${originalPath}`,
51
+ },
52
+ },
53
+ };
54
+ }
55
+ return {};
56
+ };
57
+
58
+ for await (const message of query({
59
+ prompt: 'Create a hello.txt file',
60
+ options: {
61
+ hooks: {
62
+ PreToolUse: [{ matcher: 'Write', hooks: [redirectToSandbox] }],
63
+ },
64
+ maxTurns: 3,
65
+ },
66
+ })) {
67
+ if (message.type === 'result') {
68
+ console.log(message);
69
+ }
70
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 11-subagents.js
3
+ *
4
+ * Equivalent of the official "subagents" example.
5
+ *
6
+ * Official version:
7
+ * import { query } from "@anthropic-ai/claude-agent-sdk";
8
+ * for await (const message of query({
9
+ * prompt: "Use the code-reviewer agent to review this codebase",
10
+ * options: {
11
+ * allowedTools: ["Read", "Glob", "Grep", "Agent"],
12
+ * agents: {
13
+ * "code-reviewer": {
14
+ * description: "Expert code reviewer for quality and security reviews.",
15
+ * prompt: "Analyze code quality and suggest improvements.",
16
+ * tools: ["Read", "Glob", "Grep"]
17
+ * }
18
+ * }
19
+ * }
20
+ * })) {
21
+ * if ("result" in message) console.log(message.result);
22
+ * }
23
+ *
24
+ * This elwood version spawns a specialized subagent for code review.
25
+ * Subagents are invoked via the Agent tool and report back to the main agent.
26
+ *
27
+ * Prerequisites:
28
+ * - ANTHROPIC_API_KEY set in environment
29
+ * - Claude Code CLI installed
30
+ *
31
+ * Run:
32
+ * node examples/11-subagents.js
33
+ */
34
+
35
+ import { query } from '../src/index.js';
36
+
37
+ for await (const message of query({
38
+ prompt: 'Use the code-reviewer agent to review this codebase',
39
+ options: {
40
+ allowedTools: ['Read', 'Glob', 'Grep', 'Agent'],
41
+ agents: {
42
+ 'code-reviewer': {
43
+ description: 'Expert code reviewer for quality and security reviews.',
44
+ prompt: 'Analyze code quality and suggest improvements.',
45
+ tools: ['Read', 'Glob', 'Grep'],
46
+ },
47
+ },
48
+ maxTurns: 10,
49
+ },
50
+ })) {
51
+ if ('result' in message) {
52
+ console.log(message.result);
53
+ }
54
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * 12-mcp-stdio.js
3
+ *
4
+ * Equivalent of the official "MCP stdio server" example (Playwright browser).
5
+ *
6
+ * Official version:
7
+ * import { query } from "@anthropic-ai/claude-agent-sdk";
8
+ * for await (const message of query({
9
+ * prompt: "Open example.com and describe what you see",
10
+ * options: {
11
+ * mcpServers: {
12
+ * playwright: { command: "npx", args: ["@playwright/mcp@latest"] }
13
+ * }
14
+ * }
15
+ * })) {
16
+ * if ("result" in message) console.log(message.result);
17
+ * }
18
+ *
19
+ * This elwood version connects to an external MCP server via stdio transport.
20
+ * The example uses the Playwright MCP server for browser automation.
21
+ *
22
+ * Prerequisites:
23
+ * - ANTHROPIC_API_KEY set in environment
24
+ * - Claude Code CLI installed
25
+ * - npx available in PATH
26
+ *
27
+ * Run:
28
+ * node examples/12-mcp-stdio.js
29
+ */
30
+
31
+ import { query } from '../src/index.js';
32
+
33
+ for await (const message of query({
34
+ prompt: 'Open example.com and describe what you see',
35
+ options: {
36
+ mcpServers: {
37
+ playwright: {
38
+ command: 'npx',
39
+ args: ['@playwright/mcp@latest'],
40
+ },
41
+ },
42
+ maxTurns: 5,
43
+ },
44
+ })) {
45
+ if ('result' in message) {
46
+ console.log(message.result);
47
+ }
48
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * 13-mcp-http.js
3
+ *
4
+ * Equivalent of the official "MCP HTTP/SSE server" example.
5
+ *
6
+ * Official version:
7
+ * import { query } from "@anthropic-ai/claude-agent-sdk";
8
+ * for await (const message of query({
9
+ * prompt: "Use the docs MCP server to explain what hooks are in Claude Code",
10
+ * options: {
11
+ * mcpServers: {
12
+ * "claude-code-docs": {
13
+ * type: "http",
14
+ * url: "https://code.claude.com/docs/mcp"
15
+ * }
16
+ * },
17
+ * allowedTools: ["mcp__claude-code-docs__*"]
18
+ * }
19
+ * })) {
20
+ * if (message.type === "result" && message.subtype === "success") {
21
+ * console.log(message.result);
22
+ * }
23
+ * }
24
+ *
25
+ * This elwood version connects to the Claude Code documentation MCP server
26
+ * via HTTP transport.
27
+ *
28
+ * Prerequisites:
29
+ * - ANTHROPIC_API_KEY set in environment
30
+ * - Claude Code CLI installed
31
+ *
32
+ * Run:
33
+ * node examples/13-mcp-http.js
34
+ */
35
+
36
+ import { query } from '../src/index.js';
37
+
38
+ for await (const message of query({
39
+ prompt: 'Use the docs MCP server to explain what hooks are in Claude Code',
40
+ options: {
41
+ mcpServers: {
42
+ 'claude-code-docs': {
43
+ type: 'http',
44
+ url: 'https://code.claude.com/docs/mcp',
45
+ },
46
+ },
47
+ allowedTools: ['mcp__claude-code-docs__*'],
48
+ maxTurns: 5,
49
+ },
50
+ })) {
51
+ if (message.type === 'result' && message.subtype === 'success') {
52
+ console.log(message.result);
53
+ }
54
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 14-custom-tool.js
3
+ *
4
+ * Equivalent of the official "custom tool" example (weather temperature tool).
5
+ *
6
+ * Official version:
7
+ * import { tool, createSdkMcpServer, query } from "@anthropic-ai/claude-agent-sdk";
8
+ * import { z } from "zod";
9
+ *
10
+ * const getTemperature = tool(
11
+ * "get_temperature",
12
+ * "Get the current temperature at a location",
13
+ * { latitude: z.number(), longitude: z.number() },
14
+ * async (args) => {
15
+ * const response = await fetch(`https://api.open-meteo.com/v1/forecast?...`);
16
+ * const data = await response.json();
17
+ * return { content: [{ type: "text", text: `Temperature: ${data.current.temperature_2m}F` }] };
18
+ * }
19
+ * );
20
+ *
21
+ * const weatherServer = createSdkMcpServer({
22
+ * name: "weather", version: "1.0.0", tools: [getTemperature]
23
+ * });
24
+ *
25
+ * Note: In elwood, tool() returns a simple { name, description, inputSchema, handler }
26
+ * object. The Zod schema validation from the official SDK is not included -- pass a
27
+ * plain JSON Schema object as inputSchema instead.
28
+ *
29
+ * Prerequisites:
30
+ * - ANTHROPIC_API_KEY set in environment
31
+ * - Claude Code CLI installed
32
+ *
33
+ * Run:
34
+ * node examples/14-custom-tool.js
35
+ */
36
+
37
+ import { query, tool, createSdkMcpServer } from '../src/index.js';
38
+
39
+ // Define a tool: name, description, input schema (JSON Schema), handler
40
+ const getTemperature = tool(
41
+ 'get_temperature',
42
+ 'Get the current temperature at a location',
43
+ {
44
+ type: 'object',
45
+ properties: {
46
+ latitude: { type: 'number', description: 'Latitude coordinate' },
47
+ longitude: { type: 'number', description: 'Longitude coordinate' },
48
+ },
49
+ required: ['latitude', 'longitude'],
50
+ },
51
+ async (args) => {
52
+ const response = await fetch(
53
+ `https://api.open-meteo.com/v1/forecast?latitude=${args.latitude}&longitude=${args.longitude}&current=temperature_2m&temperature_unit=fahrenheit`
54
+ );
55
+ const data = await response.json();
56
+
57
+ // Return a content array - Claude sees this as the tool result
58
+ return {
59
+ content: [
60
+ { type: 'text', text: `Temperature: ${data.current.temperature_2m}\u00B0F` },
61
+ ],
62
+ };
63
+ }
64
+ );
65
+
66
+ // Wrap the tool in an in-process MCP server
67
+ const weatherServer = await createSdkMcpServer({
68
+ name: 'weather',
69
+ version: '1.0.0',
70
+ tools: [getTemperature],
71
+ });
72
+
73
+ for await (const message of query({
74
+ prompt: "What's the temperature in San Francisco?",
75
+ options: {
76
+ mcpServers: { weather: weatherServer },
77
+ allowedTools: ['mcp__weather__get_temperature'],
78
+ maxTurns: 3,
79
+ },
80
+ })) {
81
+ if (message.type === 'result' && message.subtype === 'success') {
82
+ console.log(message.result);
83
+ }
84
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * 15-custom-tool-unit-converter.js
3
+ *
4
+ * Equivalent of the official "unit converter" custom tool example.
5
+ *
6
+ * Official version uses Zod schemas with z.enum(). This elwood version uses
7
+ * plain JSON Schema objects instead.
8
+ *
9
+ * This example demonstrates:
10
+ * - Enum schemas via JSON Schema "enum" constraint
11
+ * - Error handling with isError: true (keeps agent loop alive)
12
+ * - Running the same tool with multiple prompts
13
+ *
14
+ * Prerequisites:
15
+ * - ANTHROPIC_API_KEY set in environment
16
+ * - Claude Code CLI installed
17
+ *
18
+ * Run:
19
+ * node examples/15-custom-tool-unit-converter.js
20
+ */
21
+
22
+ import { query, tool, createSdkMcpServer } from '../src/index.js';
23
+
24
+ const convertUnits = tool(
25
+ 'convert_units',
26
+ 'Convert a value from one unit to another',
27
+ {
28
+ type: 'object',
29
+ properties: {
30
+ unit_type: {
31
+ type: 'string',
32
+ enum: ['length', 'temperature', 'weight'],
33
+ description: 'Category of unit',
34
+ },
35
+ from_unit: {
36
+ type: 'string',
37
+ description: 'Unit to convert from, e.g. kilometers, fahrenheit, pounds',
38
+ },
39
+ to_unit: {
40
+ type: 'string',
41
+ description: 'Unit to convert to',
42
+ },
43
+ value: {
44
+ type: 'number',
45
+ description: 'Value to convert',
46
+ },
47
+ },
48
+ required: ['unit_type', 'from_unit', 'to_unit', 'value'],
49
+ },
50
+ async (args) => {
51
+ const conversions = {
52
+ length: {
53
+ kilometers_to_miles: (v) => v * 0.621371,
54
+ miles_to_kilometers: (v) => v * 1.60934,
55
+ meters_to_feet: (v) => v * 3.28084,
56
+ feet_to_meters: (v) => v * 0.3048,
57
+ },
58
+ temperature: {
59
+ celsius_to_fahrenheit: (v) => (v * 9) / 5 + 32,
60
+ fahrenheit_to_celsius: (v) => ((v - 32) * 5) / 9,
61
+ celsius_to_kelvin: (v) => v + 273.15,
62
+ kelvin_to_celsius: (v) => v - 273.15,
63
+ },
64
+ weight: {
65
+ kilograms_to_pounds: (v) => v * 2.20462,
66
+ pounds_to_kilograms: (v) => v * 0.453592,
67
+ grams_to_ounces: (v) => v * 0.035274,
68
+ ounces_to_grams: (v) => v * 28.3495,
69
+ },
70
+ };
71
+
72
+ const key = `${args.from_unit}_to_${args.to_unit}`;
73
+ const fn = conversions[args.unit_type]?.[key];
74
+
75
+ if (!fn) {
76
+ return {
77
+ content: [
78
+ {
79
+ type: 'text',
80
+ text: `Unsupported conversion: ${args.from_unit} to ${args.to_unit}`,
81
+ },
82
+ ],
83
+ isError: true,
84
+ };
85
+ }
86
+
87
+ const result = fn(args.value);
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text',
92
+ text: `${args.value} ${args.from_unit} = ${result.toFixed(4)} ${args.to_unit}`,
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ );
98
+
99
+ // Wrap in an MCP server
100
+ const converterServer = await createSdkMcpServer({
101
+ name: 'converter',
102
+ version: '1.0.0',
103
+ tools: [convertUnits],
104
+ });
105
+
106
+ // Run multiple prompts through the same tool
107
+ const prompts = [
108
+ 'Convert 100 kilometers to miles.',
109
+ 'What is 72 degrees Fahrenheit in Celsius?',
110
+ 'How many pounds is 5 kilograms?',
111
+ ];
112
+
113
+ for (const prompt of prompts) {
114
+ for await (const message of query({
115
+ prompt,
116
+ options: {
117
+ mcpServers: { converter: converterServer },
118
+ allowedTools: ['mcp__converter__convert_units'],
119
+ maxTurns: 3,
120
+ },
121
+ })) {
122
+ if (message.type === 'assistant') {
123
+ for (const block of message.message?.content ?? []) {
124
+ if (block.type === 'tool_use') {
125
+ console.log(`[tool call] ${block.name}`, block.input);
126
+ }
127
+ }
128
+ } else if (message.type === 'result' && message.subtype === 'success') {
129
+ console.log(`Q: ${prompt}\nA: ${message.result}\n`);
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 16-mcp-github.js
3
+ *
4
+ * Equivalent of the official "GitHub MCP server" example.
5
+ *
6
+ * Official version:
7
+ * import { query } from "@anthropic-ai/claude-agent-sdk";
8
+ * for await (const message of query({
9
+ * prompt: "List the 3 most recent issues in anthropics/claude-code",
10
+ * options: {
11
+ * mcpServers: {
12
+ * github: {
13
+ * command: "npx",
14
+ * args: ["-y", "@modelcontextprotocol/server-github"],
15
+ * env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN }
16
+ * }
17
+ * },
18
+ * allowedTools: ["mcp__github__list_issues"]
19
+ * }
20
+ * })) { ... }
21
+ *
22
+ * This elwood version connects to the GitHub MCP server via stdio transport
23
+ * and lists recent issues with debug logging.
24
+ *
25
+ * Prerequisites:
26
+ * - ANTHROPIC_API_KEY set in environment
27
+ * - GITHUB_TOKEN set in environment (GitHub personal access token with repo scope)
28
+ * - Claude Code CLI installed
29
+ * - npx available in PATH
30
+ *
31
+ * Run:
32
+ * GITHUB_TOKEN=ghp_xxx node examples/16-mcp-github.js
33
+ */
34
+
35
+ import { query } from '../src/index.js';
36
+
37
+ for await (const message of query({
38
+ prompt: 'List the 3 most recent issues in anthropics/claude-code',
39
+ options: {
40
+ mcpServers: {
41
+ github: {
42
+ command: 'npx',
43
+ args: ['-y', '@modelcontextprotocol/server-github'],
44
+ env: {
45
+ GITHUB_TOKEN: process.env.GITHUB_TOKEN,
46
+ },
47
+ },
48
+ },
49
+ allowedTools: ['mcp__github__list_issues'],
50
+ maxTurns: 5,
51
+ },
52
+ })) {
53
+ // Verify MCP server connected successfully
54
+ if (message.type === 'system' && message.subtype === 'init') {
55
+ console.log('MCP servers:', message.mcp_servers);
56
+ }
57
+
58
+ // Log when Claude calls an MCP tool
59
+ if (message.type === 'assistant') {
60
+ for (const block of message.message?.content ?? []) {
61
+ if (block.type === 'tool_use' && block.name?.startsWith('mcp__')) {
62
+ console.log('MCP tool called:', block.name);
63
+ }
64
+ }
65
+ }
66
+
67
+ // Print the final result
68
+ if (message.type === 'result' && message.subtype === 'success') {
69
+ console.log(message.result);
70
+ }
71
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * 17-session-store-postgres.js
3
+ *
4
+ * Equivalent of the official examples/session-stores/postgres/demo.ts
5
+ *
6
+ * Official version:
7
+ * import { Pool } from 'pg';
8
+ * import { query } from '@anthropic-ai/claude-agent-sdk';
9
+ * import { PostgresSessionStore } from './src/PostgresSessionStore.ts';
10
+ *
11
+ * const pool = new Pool({ connectionString: url });
12
+ * const store = new PostgresSessionStore({ pool });
13
+ * await store.ensureSchema();
14
+ *
15
+ * for await (const m of query({
16
+ * prompt, options: { sessionStore: store, resume, maxTurns: 1 }
17
+ * })) { ... }
18
+ *
19
+ * This elwood version shows how to use a Postgres-backed session store
20
+ * with elwood's query() function. The session store is an external adapter
21
+ * that mirrors conversation transcripts to a Postgres table.
22
+ *
23
+ * Note: The SessionStore interface is passed directly to options.sessionStore.
24
+ * The PostgresSessionStore implementation would be identical to the official
25
+ * example since it is a pure database adapter with no SDK-specific logic.
26
+ *
27
+ * Prerequisites:
28
+ * - ANTHROPIC_API_KEY set in environment
29
+ * - Claude Code CLI installed
30
+ * - PostgreSQL server running
31
+ * - SESSION_STORE_POSTGRES_URL set (e.g., postgresql://postgres:postgres@localhost:5432/postgres)
32
+ * - npm install pg
33
+ *
34
+ * Run:
35
+ * SESSION_STORE_POSTGRES_URL=postgresql://postgres:postgres@localhost:5432/postgres \
36
+ * node examples/17-session-store-postgres.js
37
+ */
38
+
39
+ import { query } from '../src/index.js';
40
+
41
+ // Note: You would need to implement or import a PostgresSessionStore adapter.
42
+ // The adapter is identical to the official example since it is pure database logic.
43
+ // See: https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores/postgres
44
+
45
+ const url = process.env.SESSION_STORE_POSTGRES_URL;
46
+ if (!url) {
47
+ console.error('Set SESSION_STORE_POSTGRES_URL (see header)');
48
+ console.error('Example: postgresql://postgres:postgres@localhost:5432/postgres');
49
+ console.error(
50
+ '\nFor local testing:\n docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres:16-alpine'
51
+ );
52
+ process.exit(1);
53
+ }
54
+
55
+ console.log('To use this example, implement or import a PostgresSessionStore adapter.');
56
+ console.log('The adapter is identical to the official claude-agent-sdk example.');
57
+ console.log('');
58
+ console.log('Usage pattern with elwood:');
59
+ console.log('');
60
+ console.log(' import { query } from "elwood";');
61
+ console.log(' // import { PostgresSessionStore } from "./PostgresSessionStore.js";');
62
+ console.log('');
63
+ console.log(' const store = new PostgresSessionStore({ pool });');
64
+ console.log(' await store.ensureSchema();');
65
+ console.log('');
66
+ console.log(' async function run(prompt, resume) {');
67
+ console.log(' let sessionId;');
68
+ console.log(' for await (const m of query({');
69
+ console.log(' prompt,');
70
+ console.log(' options: { sessionStore: store, resume, maxTurns: 1 },');
71
+ console.log(' })) {');
72
+ console.log(' if (m.type === "system" && m.subtype === "init") sessionId = m.session_id;');
73
+ console.log(' if (m.type === "result") {');
74
+ console.log(' console.log(`[${m.subtype}]`, "result" in m ? m.result : "");');
75
+ console.log(' }');
76
+ console.log(' }');
77
+ console.log(' return sessionId;');
78
+ console.log(' }');
@@ -0,0 +1,65 @@
1
+ /**
2
+ * 18-session-store-redis.js
3
+ *
4
+ * Equivalent of the official examples/session-stores/redis/demo.ts
5
+ *
6
+ * Official version:
7
+ * import Redis from 'ioredis';
8
+ * import { query } from '@anthropic-ai/claude-agent-sdk';
9
+ * import { RedisSessionStore } from './src/RedisSessionStore.ts';
10
+ *
11
+ * const client = new Redis(url);
12
+ * const store = new RedisSessionStore({ client, prefix: 'demo' });
13
+ *
14
+ * for await (const m of query({
15
+ * prompt, options: { sessionStore: store, resume, maxTurns: 1 }
16
+ * })) { ... }
17
+ *
18
+ * This elwood version shows how to use a Redis-backed session store
19
+ * with elwood's query() function. The session store adapter is identical
20
+ * to the official example since it is a pure Redis adapter.
21
+ *
22
+ * Prerequisites:
23
+ * - ANTHROPIC_API_KEY set in environment
24
+ * - Claude Code CLI installed
25
+ * - Redis server running
26
+ * - SESSION_STORE_REDIS_URL set (default: redis://localhost:6379/0)
27
+ * - npm install ioredis
28
+ *
29
+ * Run:
30
+ * SESSION_STORE_REDIS_URL=redis://localhost:6379/0 node examples/18-session-store-redis.js
31
+ */
32
+
33
+ import { query } from '../src/index.js';
34
+
35
+ // Note: You would need to implement or import a RedisSessionStore adapter.
36
+ // The adapter is identical to the official example since it is pure Redis logic.
37
+ // See: https://github.com/anthropics/claude-agent-sdk-typescript/tree/main/examples/session-stores/redis
38
+
39
+ const url = process.env.SESSION_STORE_REDIS_URL ?? 'redis://localhost:6379/0';
40
+
41
+ console.log('To use this example, implement or import a RedisSessionStore adapter.');
42
+ console.log('The adapter is identical to the official claude-agent-sdk example.');
43
+ console.log('');
44
+ console.log('Usage pattern with elwood:');
45
+ console.log('');
46
+ console.log(' import { query } from "elwood";');
47
+ console.log(' // import Redis from "ioredis";');
48
+ console.log(' // import { RedisSessionStore } from "./RedisSessionStore.js";');
49
+ console.log('');
50
+ console.log(` const client = new Redis("${url}");`);
51
+ console.log(' const store = new RedisSessionStore({ client, prefix: "demo" });');
52
+ console.log('');
53
+ console.log(' async function run(prompt, resume) {');
54
+ console.log(' let sessionId;');
55
+ console.log(' for await (const m of query({');
56
+ console.log(' prompt,');
57
+ console.log(' options: { sessionStore: store, resume, maxTurns: 1 },');
58
+ console.log(' })) {');
59
+ console.log(' if (m.type === "system" && m.subtype === "init") sessionId = m.session_id;');
60
+ console.log(' if (m.type === "result") {');
61
+ console.log(' console.log(`[${m.subtype}]`, "result" in m ? m.result : "");');
62
+ console.log(' }');
63
+ console.log(' }');
64
+ console.log(' return sessionId;');
65
+ console.log(' }');