@qodo/sdk 0.4.0 → 0.5.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 (43) hide show
  1. package/.claude/skills/qodo-agent/SKILL.md +658 -0
  2. package/.claude/skills/qodo-agent/assets/programmatic-agent.ts +317 -0
  3. package/.claude/skills/qodo-agent/references/builtin-tools.md +342 -0
  4. package/.claude/skills/qodo-agent/references/common-issues.md +430 -0
  5. package/dist/api/agent.d.ts +0 -1
  6. package/dist/api/agent.d.ts.map +1 -1
  7. package/dist/api/agent.js +5 -127
  8. package/dist/api/agent.js.map +1 -1
  9. package/dist/bin/install-skill.d.ts +14 -0
  10. package/dist/bin/install-skill.d.ts.map +1 -0
  11. package/dist/bin/install-skill.js +125 -0
  12. package/dist/bin/install-skill.js.map +1 -0
  13. package/dist/mcp/MCPManager.d.ts +0 -16
  14. package/dist/mcp/MCPManager.d.ts.map +1 -1
  15. package/dist/mcp/MCPManager.js +0 -41
  16. package/dist/mcp/MCPManager.js.map +1 -1
  17. package/dist/mcp/builtinServers.d.ts.map +1 -1
  18. package/dist/mcp/builtinServers.js +0 -12
  19. package/dist/mcp/builtinServers.js.map +1 -1
  20. package/dist/mcp/index.d.ts +0 -1
  21. package/dist/mcp/index.d.ts.map +1 -1
  22. package/dist/mcp/index.js +0 -1
  23. package/dist/mcp/index.js.map +1 -1
  24. package/dist/mcp/servers/filesystem.d.ts +0 -31
  25. package/dist/mcp/servers/filesystem.d.ts.map +1 -1
  26. package/dist/mcp/servers/filesystem.js +0 -222
  27. package/dist/mcp/servers/filesystem.js.map +1 -1
  28. package/dist/mcp/servers/shell.d.ts.map +1 -1
  29. package/dist/mcp/servers/shell.js +21 -105
  30. package/dist/mcp/servers/shell.js.map +1 -1
  31. package/dist/mcp/serversRegistry.js +2 -2
  32. package/dist/mcp/serversRegistry.js.map +1 -1
  33. package/dist/mcp/toolProcessor.d.ts.map +1 -1
  34. package/dist/mcp/toolProcessor.js +0 -43
  35. package/dist/mcp/toolProcessor.js.map +1 -1
  36. package/dist/sdk/QodoSDK.d.ts.map +1 -1
  37. package/dist/sdk/QodoSDK.js +0 -9
  38. package/dist/sdk/QodoSDK.js.map +1 -1
  39. package/package.json +5 -1
  40. package/dist/mcp/servers/gerrit.d.ts +0 -19
  41. package/dist/mcp/servers/gerrit.d.ts.map +0 -1
  42. package/dist/mcp/servers/gerrit.js +0 -515
  43. package/dist/mcp/servers/gerrit.js.map +0 -1
@@ -0,0 +1,658 @@
1
+ ---
2
+ name: qodo-agent
3
+ description: Development companion for building AI-powered agents with @qodo/sdk. Use when working with QodoSDK, sdkAgent(), sdkCommand(), agent.toml, or any Qodo agent development tasks.
4
+ compatibility: Requires Node.js >= 18, npm, and zod package for schema definitions.
5
+ metadata:
6
+ author: qodo
7
+ version: "1.0"
8
+ ---
9
+
10
+ # Qodo SDK Development Companion
11
+
12
+ Use this skill when working with `@qodo/sdk` to build AI-powered agents. This skill helps you:
13
+
14
+ - **Create** agent configurations (TypeScript and TOML)
15
+ - **Scaffold** Zod schemas with proper patterns and descriptions
16
+ - **Extend** existing agents with new commands, tools, and capabilities
17
+ - **Debug** streaming events, schema validation, and tool execution issues
18
+ - **Refactor** between config formats and improve type safety
19
+
20
+ ## Quick Reference
21
+
22
+ ### Core Exports
23
+
24
+ | Export | Purpose |
25
+ |--------|---------|
26
+ | `QodoSDK` | Main SDK class - initialize and run agents |
27
+ | `sdkAgent()` | Build agent config programmatically |
28
+ | `sdkCommand()` | Build command config with Zod schemas |
29
+ | `sdkArgs()` | Convenience wrapper for `z.object()` |
30
+
31
+ ### Schema Helpers
32
+
33
+ | Export | Purpose |
34
+ |--------|---------|
35
+ | `zodOutputSchema()` | Convert Zod to JSON Schema for output |
36
+ | `zJsonValue()` | Flexible primitive type (string \| number \| boolean \| null) |
37
+ | `zJsonAny()` | Flexible any type (primitives + arrays + objects) |
38
+ | `zCoerce` | CLI-style input coercion (boolean, number, jsonArray, jsonObject) |
39
+ | `parseArgsWithSchema()` | Validate args with Zod, get typed result |
40
+ | `QodoSchemaError` | Schema validation error class |
41
+
42
+ ### Event Handling
43
+
44
+ | Export | Purpose |
45
+ |--------|---------|
46
+ | `SdkEventType` | Event type constants (Init, MessageDelta, Final, etc.) |
47
+ | `matchSdkEvent()` | Type-safe event router |
48
+
49
+ ### Clients
50
+
51
+ | Export | Purpose |
52
+ |--------|---------|
53
+ | `QodoInfoClient` | Backend info (models, capabilities) |
54
+ | `QodoSessionsClient` | Session history management |
55
+
56
+ ### Errors
57
+
58
+ | Export | Purpose |
59
+ |--------|---------|
60
+ | `QodoSchemaError` | Schema validation failures |
61
+ | `QodoAuthError` | Authentication failures |
62
+ | `QodoBackendBootstrapError` | Backend initialization failures |
63
+
64
+ ### Built-in Tools
65
+
66
+ Use these in `available_tools` to give your agent capabilities:
67
+
68
+ | Tool | Purpose | Common Use Cases |
69
+ |------|---------|------------------|
70
+ | `filesystem` | File operations | Read/write files, navigate directories, edit code |
71
+ | `git` | Version control | Check status, view diffs, commit changes |
72
+ | `ripgrep` | Code search | Find patterns, locate definitions, search codebase |
73
+ | `shell` | Run commands | Build tools, scripts, system commands |
74
+
75
+ **Example:**
76
+ ```typescript
77
+ sdkCommand({
78
+ name: 'analyze',
79
+ available_tools: ['filesystem', 'ripgrep'], // Read files + search
80
+ // ...
81
+ });
82
+ ```
83
+
84
+ **Common Combinations:**
85
+ - **Code analysis**: `['filesystem', 'ripgrep']`
86
+ - **Code review**: `['git', 'filesystem']`
87
+ - **Full development**: `['filesystem', 'git', 'ripgrep', 'shell']`
88
+
89
+ See [references/builtin-tools.md](references/builtin-tools.md) for detailed documentation on each tool.
90
+
91
+ ---
92
+
93
+ ## Critical Rules
94
+
95
+ ### For SDK Users (Building Apps)
96
+
97
+ 1. **Every output property needs `.describe()`**
98
+ ```typescript
99
+ // WRONG - will throw QodoSchemaError
100
+ const output = z.object({
101
+ summary: z.string(),
102
+ score: z.number(),
103
+ });
104
+
105
+ // CORRECT
106
+ const output = z.object({
107
+ summary: z.string().describe('Brief summary of the analysis'),
108
+ score: z.number().describe('Quality score from 0-100'),
109
+ });
110
+ ```
111
+
112
+ 2. **Never use `z.any()` in output schemas**
113
+ ```typescript
114
+ // WRONG - produces invalid JSON Schema
115
+ const output = z.object({
116
+ data: z.any().describe('The data'),
117
+ });
118
+
119
+ // CORRECT - use zJsonValue() or zJsonAny()
120
+ import { zJsonValue, zJsonAny } from '@qodo/sdk';
121
+
122
+ const output = z.object({
123
+ // For primitives only:
124
+ value: zJsonValue().describe('A primitive value'),
125
+ // For any JSON (including objects/arrays):
126
+ data: zJsonAny().describe('Any JSON data'),
127
+ });
128
+ ```
129
+
130
+ 3. **Always call `sdk.dispose()`**
131
+ ```typescript
132
+ const sdk = new QodoSDK({ ... });
133
+ try {
134
+ const result = await sdk.run('analyze', { args: { path: './src' } });
135
+ console.log(result.structured_output);
136
+ } finally {
137
+ await sdk.dispose(); // Cleanup MCP servers and connections
138
+ }
139
+ ```
140
+
141
+ 4. **Handle errors appropriately**
142
+ ```typescript
143
+ import { QodoSchemaError, QodoAuthError } from '@qodo/sdk';
144
+
145
+ try {
146
+ const result = await sdk.run('analyze');
147
+ } catch (e) {
148
+ if (e instanceof QodoSchemaError) {
149
+ console.error('Schema validation failed:', e.message);
150
+ } else if (e instanceof QodoAuthError) {
151
+ console.error('Authentication failed:', e.message);
152
+ } else {
153
+ throw e;
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### For SDK Contributors (Modifying @qodo/sdk)
159
+
160
+ 1. **Never use `process.exit()`** - throw errors instead (library code)
161
+ 2. **Use `.js` extensions** in imports - ESM requirement
162
+ 3. **Export types separately** - TypeScript best practice
163
+
164
+ ---
165
+
166
+ ## Usage Patterns
167
+
168
+ ### Pattern 1: Simple Prompt (No Config)
169
+
170
+ ```typescript
171
+ import { QodoSDK } from '@qodo/sdk';
172
+
173
+ const sdk = new QodoSDK();
174
+ try {
175
+ const result = await sdk.prompt('Explain how async/await works in JavaScript');
176
+ console.log(result.final_output);
177
+ } finally {
178
+ await sdk.dispose();
179
+ }
180
+ ```
181
+
182
+ ### Pattern 2: Programmatic Agent with Zod
183
+
184
+ ```typescript
185
+ import { QodoSDK, sdkAgent, sdkCommand, sdkArgs } from '@qodo/sdk';
186
+ import { z } from 'zod';
187
+
188
+ const agent = sdkAgent({
189
+ instructions: 'You are a code analysis assistant.',
190
+ commands: {
191
+ analyze: sdkCommand({
192
+ name: 'analyze',
193
+ description: 'Analyze code quality',
194
+ instructions: 'Analyze the code and provide a quality assessment.',
195
+ available_tools: ['filesystem', 'ripgrep'],
196
+ args: sdkArgs({
197
+ path: z.string().describe('Path to analyze'),
198
+ depth: z.number().optional().default(3).describe('Max directory depth'),
199
+ }),
200
+ output: z.object({
201
+ summary: z.string().describe('Overall assessment'),
202
+ issues: z.array(z.object({
203
+ file: z.string().describe('File path'),
204
+ line: z.number().describe('Line number'),
205
+ severity: z.enum(['low', 'medium', 'high']).describe('Issue severity'),
206
+ message: z.string().describe('Issue description'),
207
+ })).describe('List of issues found'),
208
+ score: z.number().describe('Quality score 0-100'),
209
+ }),
210
+ }),
211
+ },
212
+ });
213
+
214
+ const sdk = QodoSDK.fromAgent(agent);
215
+ try {
216
+ const result = await sdk.run('analyze', {
217
+ args: { path: './src', depth: 5 },
218
+ });
219
+ console.log('Score:', result.structured_output?.score);
220
+ console.log('Issues:', result.structured_output?.issues);
221
+ } finally {
222
+ await sdk.dispose();
223
+ }
224
+ ```
225
+
226
+ ### Pattern 3: TOML Configuration
227
+
228
+ Create `agent.toml`:
229
+ ```toml
230
+ version = "1.0"
231
+ instructions = "You are a helpful coding assistant."
232
+
233
+ [commands.review]
234
+ name = "review"
235
+ description = "Review code changes"
236
+ instructions = "Review the provided code changes and suggest improvements."
237
+ available_tools = ["filesystem", "git", "ripgrep"]
238
+
239
+ [commands.review.output_schema]
240
+ type = "json_schema"
241
+
242
+ [commands.review.output_schema.json_schema]
243
+ name = "review_output"
244
+ strict = true
245
+
246
+ [commands.review.output_schema.json_schema.schema]
247
+ type = "object"
248
+ required = ["summary", "suggestions"]
249
+
250
+ [commands.review.output_schema.json_schema.schema.properties.summary]
251
+ type = "string"
252
+ description = "Overall review summary"
253
+
254
+ [commands.review.output_schema.json_schema.schema.properties.suggestions]
255
+ type = "array"
256
+ description = "List of improvement suggestions"
257
+
258
+ [commands.review.output_schema.json_schema.schema.properties.suggestions.items]
259
+ type = "object"
260
+ required = ["file", "suggestion"]
261
+
262
+ [commands.review.output_schema.json_schema.schema.properties.suggestions.items.properties.file]
263
+ type = "string"
264
+ description = "File path"
265
+
266
+ [commands.review.output_schema.json_schema.schema.properties.suggestions.items.properties.suggestion]
267
+ type = "string"
268
+ description = "The suggestion"
269
+ ```
270
+
271
+ Then use it:
272
+ ```typescript
273
+ import { QodoSDK } from '@qodo/sdk';
274
+
275
+ const sdk = new QodoSDK({
276
+ agentFile: './agent.toml',
277
+ });
278
+ try {
279
+ const result = await sdk.run('review');
280
+ console.log(result.structured_output);
281
+ } finally {
282
+ await sdk.dispose();
283
+ }
284
+ ```
285
+
286
+ ### Pattern 4: Streaming Events
287
+
288
+ ```typescript
289
+ import { QodoSDK, SdkEventType, matchSdkEvent } from '@qodo/sdk';
290
+
291
+ const sdk = new QodoSDK();
292
+ try {
293
+ for await (const event of sdk.streamPrompt('Write a haiku about coding')) {
294
+ matchSdkEvent(event, {
295
+ [SdkEventType.MessageDelta]: (e) => {
296
+ process.stdout.write(e.data.delta);
297
+ },
298
+ [SdkEventType.ToolRequested]: (e) => {
299
+ console.log(`\nTool: ${e.data.server_name}.${e.data.tool_name}`);
300
+ },
301
+ [SdkEventType.ToolExecuted]: (e) => {
302
+ console.log(`Tool result: ${e.data.result.isError ? 'error' : 'success'}`);
303
+ },
304
+ [SdkEventType.Error]: (e) => {
305
+ console.error('Error:', e.data.message);
306
+ },
307
+ [SdkEventType.Final]: (e) => {
308
+ console.log('\n\nDone! Success:', e.data.success);
309
+ },
310
+ default: () => {}, // Ignore other events
311
+ });
312
+ }
313
+ } finally {
314
+ await sdk.dispose();
315
+ }
316
+ ```
317
+
318
+ ### Pattern 5: Custom Tool Approval
319
+
320
+ ```typescript
321
+ import { QodoSDK } from '@qodo/sdk';
322
+ import * as readline from 'readline';
323
+
324
+ const sdk = new QodoSDK({
325
+ autoApproveTools: false,
326
+ toolApproval: async ({ tool_name, server_name, tool_args }) => {
327
+ const rl = readline.createInterface({
328
+ input: process.stdin,
329
+ output: process.stdout,
330
+ });
331
+
332
+ return new Promise((resolve) => {
333
+ rl.question(
334
+ `Allow ${server_name}.${tool_name}(${JSON.stringify(tool_args)})? [y/N] `,
335
+ (answer) => {
336
+ rl.close();
337
+ resolve(answer.toLowerCase() === 'y');
338
+ }
339
+ );
340
+ });
341
+ },
342
+ });
343
+
344
+ try {
345
+ const result = await sdk.prompt('List files in current directory');
346
+ console.log(result.final_output);
347
+ } finally {
348
+ await sdk.dispose();
349
+ }
350
+ ```
351
+
352
+ ### Pattern 6: Session Continuity
353
+
354
+ ```typescript
355
+ import { QodoSDK } from '@qodo/sdk';
356
+
357
+ // First conversation
358
+ const sdk1 = new QodoSDK();
359
+ let sessionId: string;
360
+
361
+ try {
362
+ for await (const event of sdk1.streamPrompt('Remember: my favorite color is blue')) {
363
+ if (event.type === 'sdk.run.started') {
364
+ sessionId = event.data.session_id;
365
+ }
366
+ }
367
+ } finally {
368
+ await sdk1.dispose();
369
+ }
370
+
371
+ // Continue the conversation
372
+ const sdk2 = new QodoSDK().withSession(sessionId!);
373
+ try {
374
+ const result = await sdk2.prompt('What is my favorite color?');
375
+ console.log(result.final_output); // Should mention "blue"
376
+ } finally {
377
+ await sdk2.dispose();
378
+ }
379
+ ```
380
+
381
+ ### Pattern 7: Multi-Agent Orchestration
382
+
383
+ ```typescript
384
+ import { QodoSDK, sdkAgent, sdkCommand } from '@qodo/sdk';
385
+ import { z } from 'zod';
386
+
387
+ // Agent 1: Code analyzer
388
+ const analyzerAgent = sdkAgent({
389
+ instructions: 'You analyze code and identify issues.',
390
+ commands: {
391
+ analyze: sdkCommand({
392
+ name: 'analyze',
393
+ description: 'Analyze code',
394
+ available_tools: ['filesystem', 'ripgrep'],
395
+ output: z.object({
396
+ issues: z.array(z.string().describe('Issue description')).describe('Issues found'),
397
+ }),
398
+ }),
399
+ },
400
+ });
401
+
402
+ // Agent 2: Code fixer
403
+ const fixerAgent = sdkAgent({
404
+ instructions: 'You fix code issues.',
405
+ commands: {
406
+ fix: sdkCommand({
407
+ name: 'fix',
408
+ description: 'Fix code issues',
409
+ available_tools: ['filesystem'],
410
+ args: sdkArgs({
411
+ issues: z.array(z.string()).describe('Issues to fix'),
412
+ }),
413
+ output: z.object({
414
+ fixed: z.array(z.string().describe('Fixed issue')).describe('Issues that were fixed'),
415
+ }),
416
+ }),
417
+ },
418
+ });
419
+
420
+ // Orchestrate
421
+ const analyzer = QodoSDK.fromAgent(analyzerAgent);
422
+ const fixer = QodoSDK.fromAgent(fixerAgent);
423
+
424
+ try {
425
+ // Step 1: Analyze
426
+ const analysis = await analyzer.run('analyze');
427
+ const issues = analysis.structured_output?.issues || [];
428
+
429
+ // Step 2: Fix
430
+ if (issues.length > 0) {
431
+ const fixes = await fixer.run('fix', { args: { issues } });
432
+ console.log('Fixed:', fixes.structured_output?.fixed);
433
+ }
434
+ } finally {
435
+ await Promise.all([analyzer.dispose(), fixer.dispose()]);
436
+ }
437
+ ```
438
+
439
+ ---
440
+
441
+ ## Zod Schema Patterns
442
+
443
+ ### Basic Types with Descriptions
444
+
445
+ ```typescript
446
+ import { z } from 'zod';
447
+
448
+ const output = z.object({
449
+ // Primitives
450
+ name: z.string().describe('The name'),
451
+ count: z.number().describe('Total count'),
452
+ enabled: z.boolean().describe('Whether enabled'),
453
+
454
+ // Optional fields
455
+ notes: z.string().optional().describe('Optional notes'),
456
+
457
+ // With defaults
458
+ priority: z.number().default(5).describe('Priority level 1-10'),
459
+
460
+ // Enums
461
+ status: z.enum(['pending', 'done', 'failed']).describe('Current status'),
462
+
463
+ // Arrays
464
+ tags: z.array(z.string().describe('Tag name')).describe('List of tags'),
465
+
466
+ // Nested objects
467
+ metadata: z.object({
468
+ created: z.string().describe('Creation date'),
469
+ author: z.string().describe('Author name'),
470
+ }).describe('Metadata information'),
471
+ });
472
+ ```
473
+
474
+ ### Flexible Types with zJsonValue and zJsonAny
475
+
476
+ ```typescript
477
+ import { z } from 'zod';
478
+ import { zJsonValue, zJsonAny } from '@qodo/sdk';
479
+
480
+ const output = z.object({
481
+ // When you need a primitive but don't know which type
482
+ // Allows: string | number | boolean | null
483
+ dynamicValue: zJsonValue().describe('A dynamic primitive value'),
484
+
485
+ // When you need truly flexible data (including objects/arrays)
486
+ // Allows: primitives + arrays of primitives + objects with primitive values
487
+ rawData: zJsonAny().describe('Raw data in any JSON format'),
488
+
489
+ // Array of mixed primitives
490
+ mixedArray: z.array(zJsonValue()).describe('Array of mixed primitive values'),
491
+ });
492
+ ```
493
+
494
+ ### Coerced Input Types
495
+
496
+ ```typescript
497
+ import { z } from 'zod';
498
+ import { zCoerce } from '@qodo/sdk';
499
+
500
+ // For CLI-style string inputs that need type conversion
501
+ const args = z.object({
502
+ // "true"/"false" strings -> boolean
503
+ verbose: zCoerce.boolean().describe('Enable verbose output'),
504
+
505
+ // "123" string -> number
506
+ limit: zCoerce.number().describe('Result limit'),
507
+
508
+ // '["a","b"]' string -> string[]
509
+ files: zCoerce.jsonArray(z.string()).describe('Files to process'),
510
+
511
+ // '{"key":"value"}' string -> object
512
+ config: zCoerce.jsonObject({ key: z.string() }).describe('Configuration'),
513
+ });
514
+ ```
515
+
516
+ ---
517
+
518
+ ## Event Types Reference
519
+
520
+ | Event Type | When Emitted | Key Data Fields |
521
+ |------------|--------------|-----------------|
522
+ | `sdk.init` | SDK initialized | `sdk_version`, `backend.base_url`, `model` |
523
+ | `sdk.run.started` | Run begins | `session_id`, `command`, `prompt_mode`, `cwd` |
524
+ | `sdk.message.delta` | Streaming text | `delta`, `message_id`, `role` |
525
+ | `sdk.message.full` | Full message update | `messages.langchain`, `messages.openai` |
526
+ | `sdk.progress` | Progress update | `title`, `status`, `percent` |
527
+ | `sdk.tool.requested` | Tool call requested | `tool_call_id`, `server_name`, `tool_name`, `tool_args`, `pending_approval` |
528
+ | `sdk.tool.approved` | Tool approved/denied | `tool_call_id`, `approved`, `reason` |
529
+ | `sdk.tool.executed` | Tool finished | `tool_call_id`, `result.isError`, `result.content` |
530
+ | `sdk.error` | Error occurred | `message`, `cause` |
531
+ | `sdk.final` | Run completed | `success`, `result.structured_output`, `result.final_output`, `messages` |
532
+
533
+ ---
534
+
535
+ ## QodoSDK Options Reference
536
+
537
+ ```typescript
538
+ interface QodoSDKOptions {
539
+ // Agent configuration (mutually exclusive)
540
+ agentFile?: string; // Path to agent.toml/yaml
541
+ agentContent?: string; // Raw TOML/YAML content
542
+ agentObject?: AIAssistantConfig; // Programmatic config
543
+
544
+ // MCP servers
545
+ mcpFile?: string; // Path to mcp.json
546
+ mcpServers?: Record<string, MCPConfig>; // Inline server configs
547
+
548
+ // Execution settings
549
+ model?: string; // Model override
550
+ projectPath?: string; // Working directory
551
+ additionalPaths?: string[]; // Extra accessible paths
552
+
553
+ // Tool handling
554
+ autoApproveTools?: boolean; // Auto-approve tools (default: true)
555
+ toolApproval?: (req) => Promise<boolean> | boolean; // Custom approval
556
+
557
+ // Session
558
+ contextSessionIds?: string[]; // Previous sessions for context
559
+
560
+ // Backend
561
+ backend?: { baseUrl?: string }; // Backend URL override
562
+
563
+ // Debugging
564
+ debug?: boolean; // Enable debug logging
565
+ logger?: QodoSDKLogger; // Custom logger
566
+
567
+ // Mode
568
+ interactiveMode?: boolean; // Enable interactive clarifications
569
+ }
570
+ ```
571
+
572
+ ---
573
+
574
+ ## Common Tasks
575
+
576
+ ### Adding a New Command to an Existing Agent
577
+
578
+ 1. Define the command with `sdkCommand()`:
579
+ ```typescript
580
+ const newCommand = sdkCommand({
581
+ name: 'newCommand',
582
+ description: 'What this command does',
583
+ instructions: 'Detailed instructions for the AI',
584
+ available_tools: ['filesystem', 'ripgrep'],
585
+ args: sdkArgs({
586
+ // Define your args
587
+ }),
588
+ output: z.object({
589
+ // Define your output with .describe() on every field
590
+ }),
591
+ });
592
+ ```
593
+
594
+ 2. Add to agent's commands:
595
+ ```typescript
596
+ const agent = sdkAgent({
597
+ commands: {
598
+ existingCommand,
599
+ newCommand, // Add here
600
+ },
601
+ });
602
+ ```
603
+
604
+ ### Converting TOML to Programmatic TypeScript
605
+
606
+ Read the TOML and translate each section:
607
+
608
+ ```typescript
609
+ // From TOML:
610
+ // [commands.analyze]
611
+ // name = "analyze"
612
+ // description = "Analyze code"
613
+ // available_tools = ["filesystem"]
614
+
615
+ // To TypeScript:
616
+ import { sdkCommand, sdkArgs } from '@qodo/sdk';
617
+ import { z } from 'zod';
618
+
619
+ const analyze = sdkCommand({
620
+ name: 'analyze',
621
+ description: 'Analyze code',
622
+ available_tools: ['filesystem'],
623
+ args: sdkArgs({ /* from [commands.analyze.arguments] */ }),
624
+ output: z.object({ /* from [commands.analyze.output_schema] */ }),
625
+ });
626
+ ```
627
+
628
+ ### Debugging Schema Validation Errors
629
+
630
+ When you see `QodoSchemaError: output schema is missing description for: fieldName`:
631
+
632
+ 1. Find the field in your Zod schema
633
+ 2. Add `.describe('...')` to it
634
+ 3. Check nested objects - all properties need descriptions
635
+
636
+ ```typescript
637
+ // Before (error)
638
+ z.object({
639
+ items: z.array(z.object({
640
+ name: z.string(), // Missing description!
641
+ })),
642
+ })
643
+
644
+ // After (fixed)
645
+ z.object({
646
+ items: z.array(z.object({
647
+ name: z.string().describe('Item name'),
648
+ })).describe('List of items'),
649
+ })
650
+ ```
651
+
652
+ ---
653
+
654
+ ## Additional Resources
655
+
656
+ - [assets/programmatic-agent.ts](assets/programmatic-agent.ts) - Complete working TypeScript agent template
657
+ - [references/builtin-tools.md](references/builtin-tools.md) - Detailed guide to filesystem, git, ripgrep, and shell tools
658
+ - [references/common-issues.md](references/common-issues.md) - Troubleshooting guide for common problems