@guildai/cli 0.9.0 → 0.9.1

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 (41) hide show
  1. package/dist/commands/agent/chat.d.ts +1 -0
  2. package/dist/commands/agent/chat.js +24 -1
  3. package/dist/commands/agent/init.js +1 -1
  4. package/dist/commands/agent/test.d.ts +1 -0
  5. package/dist/commands/agent/test.js +92 -34
  6. package/dist/commands/chat.d.ts +1 -0
  7. package/dist/commands/chat.js +178 -46
  8. package/dist/commands/integration/connect.js +1 -1
  9. package/dist/commands/integration/create.js +36 -36
  10. package/dist/commands/integration/operation/create.js +2 -1
  11. package/dist/commands/integration/operation/list.js +23 -15
  12. package/dist/commands/mcp.js +1 -1
  13. package/dist/commands/session/events.js +16 -7
  14. package/dist/commands/session/send.js +1 -1
  15. package/dist/commands/workspace/agent/add.js +16 -3
  16. package/dist/commands/workspace/agent/list.js +14 -1
  17. package/dist/commands/workspace/agent/remove.js +14 -1
  18. package/dist/commands/workspace/clear.d.ts +3 -0
  19. package/dist/commands/workspace/clear.js +45 -0
  20. package/dist/commands/workspace/select.js +3 -1
  21. package/dist/index.js +53 -6
  22. package/dist/lib/api-types.d.ts +1 -0
  23. package/dist/lib/generated-types.d.ts +1 -1
  24. package/dist/lib/generated-types.js +1 -0
  25. package/dist/lib/guild-config.d.ts +13 -0
  26. package/dist/lib/guild-config.js +19 -0
  27. package/dist/lib/npmrc.js +6 -2
  28. package/dist/lib/session-events.d.ts +32 -16
  29. package/dist/lib/session-events.js +22 -0
  30. package/dist/lib/session-polling.d.ts +4 -3
  31. package/dist/lib/session-polling.js +75 -17
  32. package/dist/lib/session-resume.js +4 -1
  33. package/dist/lib/stdin.d.ts +4 -0
  34. package/dist/lib/stdin.js +23 -0
  35. package/dist/lib/validate-input-schema.d.ts +19 -0
  36. package/dist/lib/validate-input-schema.js +208 -0
  37. package/dist/lib/workspace-helpers.d.ts +20 -0
  38. package/dist/lib/workspace-helpers.js +49 -0
  39. package/dist/mcp/tools.js +8 -52
  40. package/docs/skills/agent-dev.md +191 -128
  41. package/package.json +2 -1
package/dist/mcp/tools.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // Copyright 2026 Guild.ai
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { z } from 'zod';
4
+ import { pollForResponse as pollForSessionResponse } from '../lib/session-polling.js';
4
5
  // ---------------------------------------------------------------------------
5
6
  // Constants
6
7
  // ---------------------------------------------------------------------------
7
- const POLL_INTERVAL_MS = 2000;
8
8
  const POLL_TIMEOUT_MS = 120_000;
9
9
  // ---------------------------------------------------------------------------
10
10
  // Helpers
@@ -14,55 +14,6 @@ function debugLog(debug, message) {
14
14
  process.stderr.write(`[guild-mcp] ${message}\n`);
15
15
  }
16
16
  }
17
- /**
18
- * Poll session events until runtime_done or runtime_error.
19
- * Returns collected agent messages.
20
- */
21
- async function pollForResponse(apiClient, sessionId, debug) {
22
- const startTime = Date.now();
23
- const messages = [];
24
- let lastEventId;
25
- while (Date.now() - startTime < POLL_TIMEOUT_MS) {
26
- try {
27
- let url = `/sessions/${sessionId}/events`;
28
- if (lastEventId) {
29
- url += `?from_id=${lastEventId}`;
30
- }
31
- const response = await apiClient.get(url);
32
- const events = response.events || [];
33
- for (const event of events) {
34
- debugLog(debug, `Event: ${event.event_type}`);
35
- if (event.event_type === 'agent_notification_message') {
36
- const data = extractEventText(event);
37
- if (data) {
38
- messages.push(data);
39
- }
40
- }
41
- if (event.event_type === 'runtime_error') {
42
- const errorText = extractEventText(event);
43
- if (errorText) {
44
- return `Error: ${errorText}`;
45
- }
46
- return 'Error: Agent encountered an error';
47
- }
48
- if (event.event_type === 'runtime_done') {
49
- debugLog(debug, 'Runtime done');
50
- return messages.join('\n\n') || 'Agent completed without output.';
51
- }
52
- }
53
- if (events.length > 0) {
54
- lastEventId = events[events.length - 1].id;
55
- }
56
- }
57
- catch (error) {
58
- debugLog(debug, `Poll error: ${String(error)}`);
59
- }
60
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
61
- }
62
- return messages.length > 0
63
- ? messages.join('\n\n') + '\n\n(Timed out waiting for completion)'
64
- : 'Timed out waiting for agent response.';
65
- }
66
17
  function extractEventText(event) {
67
18
  if (!event.content)
68
19
  return undefined;
@@ -74,6 +25,11 @@ function extractEventText(event) {
74
25
  }
75
26
  return undefined;
76
27
  }
28
+ async function pollForMcpResponse(apiClient, sessionId, debug) {
29
+ debugLog(debug, `Polling response for session ${sessionId}`);
30
+ const result = await pollForSessionResponse(apiClient, sessionId, undefined, POLL_TIMEOUT_MS);
31
+ return result.response || 'Agent completed without output.';
32
+ }
77
33
  function errText(action, error) {
78
34
  return `Failed to ${action}: ${error instanceof Error ? error.message : String(error)}`;
79
35
  }
@@ -504,7 +460,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
504
460
  }
505
461
  const session = await apiClient.post(`/workspaces/${wsId}/sessions`, body);
506
462
  debugLog(debug, `Session created: ${session.id}`);
507
- const response = await pollForResponse(apiClient, session.id, debug);
463
+ const response = await pollForMcpResponse(apiClient, session.id, debug);
508
464
  return {
509
465
  content: [{ type: 'text', text: response }],
510
466
  };
@@ -526,7 +482,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
526
482
  event_type: 'user_message',
527
483
  content: { type: 'text', data: message },
528
484
  });
529
- const response = await pollForResponse(apiClient, session_id, debug);
485
+ const response = await pollForMcpResponse(apiClient, session_id, debug);
530
486
  return {
531
487
  content: [{ type: 'text', text: response }],
532
488
  };
@@ -5,7 +5,17 @@ description: Local agent development using the Guild CLI. Activated when user me
5
5
 
6
6
  # Guild Agent Development
7
7
 
8
- Build agents for Guild using the CLI. **Always use the Guild CLI for agent operations - never use raw git commands.**
8
+ Build agents for Guild using the CLI.
9
+
10
+ The Guild CLI is self-documenting:
11
+
12
+ - You may type `--help` after any command to learn more about how to use it
13
+ - You can type `guild --help` to learn about common options available across all commands.
14
+ - If you discover a discrepancy between these instructions and the Guild CLI's internal documentation, assume that the Guild CLI's version is correct.
15
+
16
+ When using the CLI, prefer to explicitly provide arguments as defaults can sometimes be unintuitive
17
+
18
+ **Always use the Guild CLI for agent operations - unless specified below, never use raw git commands.**
9
19
 
10
20
  ## MCP vs CLI
11
21
 
@@ -36,24 +46,34 @@ guild setup
36
46
  guild setup --claude-md
37
47
  ```
38
48
 
39
- ### Creating Agents
49
+ ### Creating a New Agent
40
50
 
41
51
  ```bash
42
52
  # Create and initialize a new agent (interactive - prompts for name and template)
43
53
  guild agent init
44
54
 
45
- # Create with specific name and template
46
- guild agent init --name my-agent --template LLM
47
- guild agent init --name my-agent --template AUTO_MANAGED_STATE
48
- guild agent init --name my-agent --template BLANK
55
+ # Create with specific name and template. You must specify an owner account.
56
+ guild agent init --owner account-name --name my-agent --template LLM
57
+ guild agent init --owner account-name --name my-agent --template AUTO_MANAGED_STATE
58
+ guild agent init --owner account-name --name my-agent --template BLANK
49
59
 
50
60
  # Initialize with fork of existing agent
51
- guild agent init --fork owner/agent-name
61
+ guild agent init --fork owner~agent-name
62
+ ```
63
+
64
+ IMPORTANT! When creating a new agent, it's important to understand who will be the owner of the agent; typically this is likely to be an organization to which the user belongs, or the user's own private account. If unsure, ask for clarification!
52
65
 
66
+ IMPORTANT! By default `guild agent init` will create an agent in the current working directory. Use the `--directory` option to specify an alternate location.
67
+
68
+ ### Modifying an Existing Agent
69
+
70
+ ```bash
53
71
  # Clone to work on existing agent
54
- guild agent clone owner/agent-name
72
+ guild agent clone owner~agent-name
55
73
  ```
56
74
 
75
+ IMPORTANT! By default `guild agent clone` will create an agent in the `<agent-name>` directory. Use the `--directory` option to specify an alternate location.
76
+
57
77
  ### Syncing and Saving
58
78
 
59
79
  Git owns the working tree, Guild owns the remote. Use normal git commands to stage and commit, then `guild agent save` to push and create a version.
@@ -76,8 +96,28 @@ guild agent save --message "Fix bug" --wait
76
96
  guild agent save -A --message "Release v1.0" --wait --publish
77
97
  ```
78
98
 
99
+ ### Building
100
+
101
+ An agent is just an `npm` TypeScript project.
102
+
103
+ ```bash
104
+ # Install the agent's dependencies
105
+ npm install
106
+
107
+ # Build the agent's code
108
+ npm run build
109
+ ```
110
+
111
+ IMPORTANT! You must be logged in to guild to `npm install` dependencies; run `guild auth login` if you get a "not authorized" error when running `npm install`.
112
+
113
+ TIP. Once you've identified the integrations upon which your agent will depend, update `package.json` and run `npm install` _before_ attempting to write any code that uses the integration. You're much less likely to make a poor assumption about what tools exist or how you must use them.
114
+
115
+ IMPORTANT! Always make sure that your agent builds correctly before testing.
116
+
79
117
  ### Testing
80
118
 
119
+ An agent must be tested using the `guild` tool: this will upload the agent to the server runtime environment where the agent will operate.
120
+
81
121
  ```bash
82
122
  # Interactive test session
83
123
  guild agent test
@@ -99,6 +139,8 @@ EOF
99
139
  guild agent chat "Hello, can you help me?"
100
140
  ```
101
141
 
142
+ IMPORTANT! Most integrations require that you provide credentials to connect them appropriately. Choose a workspace (e.g. with `guild workspace list`) that has appropriate credentials installed for each integration that your agent uses. If you cannot find one, instruct the user of your conundrum and ask for advice; specifically, tell them "I cannot test the agent without access to a workspace that has access to all of the integrations that this agent requires. I can't seem to find one. If I've overlooked a workspace with the correct credentials, please let me know which one to use. Otherwise, please configure a workspace appropriately and let me know its name when it is ready."
143
+
102
144
  ### Chatting with Agents
103
145
 
104
146
  ```bash
@@ -157,70 +199,13 @@ To chat with the agent you are developing locally, use `guild agent chat` from w
157
199
 
158
200
  The SDK core comes from `@guildai/agents-sdk`. Service tools are in separate `@guildai-services/*` packages.
159
201
 
160
- ```typescript
161
- // Agent factories
162
- import { agent, llmAgent } from '@guildai/agents-sdk';
163
-
164
- // Types
165
- import type {
166
- Task,
167
- AgentResult,
168
- TypedToolResult,
169
- TypedToolError,
170
- } from '@guildai/agents-sdk';
202
+ You can inspect this package locally after you `npm install` the agent's dependencies.
171
203
 
172
- // Result helpers (for self-managed state agents)
173
- import { ask, output, callTools } from '@guildai/agents-sdk';
204
+ You can review [online documentation for the API](https://docs.guild.ai/guide/sdk-introduction): this includes guides and references.
174
205
 
175
- // Platform tools (from SDK)
176
- import { guildTools, userInterfaceTools } from '@guildai/agents-sdk';
206
+ ### Common Integrations
177
207
 
178
- // Service tools (from separate packages - NOT from SDK)
179
- import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
180
- import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
181
- import { confluenceTools } from '@guildai-services/guildai~confluence';
182
- import { cypressTools } from '@guildai-services/guildai~cypress';
183
- import { figmaTools } from '@guildai-services/guildai~figma';
184
- import { gitHubTools } from '@guildai-services/guildai~github';
185
- import { googleComputeTools } from '@guildai-services/guildai~google-compute';
186
- import { googleLoggingTools } from '@guildai-services/guildai~google-logging';
187
- import { jiraTools } from '@guildai-services/guildai~jira';
188
- import { linearTools } from '@guildai-services/guildai~linear';
189
- import { newrelicTools } from '@guildai-services/guildai~newrelic';
190
- import { pipedreamTools } from '@guildai-services/guildai~pipedream';
191
- import { slackTools } from '@guildai-services/guildai~slack';
192
- import { testrailTools } from '@guildai-services/guildai~testrail';
193
- import { zendeskTools } from '@guildai-services/guildai~zendesk';
194
-
195
- // Utilities
196
- import {
197
- pick,
198
- omit,
199
- guildAgentTool,
200
- progressLogNotifyEvent,
201
- } from '@guildai/agents-sdk';
202
-
203
- // Calling another agent as a tool (import from its /tool sub-package)
204
- import subagentTool from '@guildai/owner~agent-name/tool';
205
-
206
- // Coding agent (container-based code execution)
207
- import { ExperimentalCodingTools as codingTools } from '@guildai-services/guildai~experimental-coding';
208
- import {
209
- CONTAINER_IMAGE,
210
- codingAgentToolsFrom,
211
- } from '@guildai/guildai~sys-experimental-coding';
212
- import codingAgentTool from '@guildai/guildai~sys-experimental-coding/tool';
213
-
214
- // Advanced (for compiled agents with LLM tool loops)
215
- import { delegatedCallsOf, asToolResultContent } from '@guildai/agents-sdk';
216
-
217
- // Zod (provided by runtime, do NOT add to dependencies)
218
- import { z } from 'zod';
219
- ```
220
-
221
- ### Service Packages Table
222
-
223
- Service tools are in separate `@guildai-services/*` packages. The runtime resolves them automatically.
208
+ An _integration_ provides the agent the tools it needs to interact with the world. An integration's API is made available via a separate `@guildai-services/*` package. To use an integration, you add it as a dependency in `package.json` and then `import` it in the agent's TypeScript code. Here are some examples:
224
209
 
225
210
  | Service | Package | Export | Tool Name Prefix |
226
211
  | -------------- | ------------------------------------------ | -------------------- | ----------------- |
@@ -242,9 +227,15 @@ Service tools are in separate `@guildai-services/*` packages. The runtime resolv
242
227
  | User Interface | `@guildai/agents-sdk` | `userInterfaceTools` | `ui_` |
243
228
  | Zendesk | `@guildai-services/guildai~zendesk` | `zendeskTools` | `zendesk_` |
244
229
 
230
+ You can use the CLI to search for a full list of integrations using `guild integration list`.
231
+
245
232
  ### Tool Access via `task.tools.*`
246
233
 
247
- All tool calls go through `task.tools.<toolName>(args)`. This is the primary API.
234
+ To make use of an integration, you must:
235
+
236
+ 1. Import the integration's tools
237
+ 2. Include the relevant tools in the agent's tool set. This will automatically create functions you can call on the `task.tools` object.
238
+ 3. Invoke a tool using the `task.tools.<toolName>(args)`.
248
239
 
249
240
  ```typescript
250
241
  // GitHub
@@ -293,25 +284,41 @@ await task.tools.guild_credentials_request({ service: 'GITHUB' });
293
284
 
294
285
  Three patterns, ordered by simplicity:
295
286
 
287
+ 1. LLM agent - just a prompt and tools
288
+ 2. Coded agent - automatic state management
289
+ 3. Coded agent - explicit state management
290
+
291
+ Considerations:
292
+
293
+ - Maintenance. An LLM agent will be easiest for a human to understand and maintain. A coded agent requires expert knowledge, and one with explicit state management results in a state machine implementation that will be difficult even for an expert programmer to maintain.
294
+ - Cost. An LLM agent requires inference to operate and so incurs a high per-invocation cost. If a task is sufficiently simple, a coded agent may be preferable and will certainly be cheaper to operate.
295
+ - Latency. An LLM agent requires inference to operate and so can incur a non-trivial latency.
296
+ - Control. An LLM agent is ultimately stochastic. If precise, fine-grained control is required, it may be easier to achieve with a coded agent than through natural language instructions.
297
+ - Reasoning and decision making. An LLM agent allows for nuanced judgment calls; a coded agent requires strict rules.
298
+
299
+ TIP. A coded agent can make use of the `task.llm.generateText` call to use an LLM as a subroutine: this may be a reasonable trade-off to make.
300
+
301
+ A detailed description and example of each is provided below.
302
+
296
303
  ### 1. LLM Agent (`llmAgent()`) — Simplest
297
304
 
298
- For conversational/prompt-driven agents where the LLM IS the logic. No `run()` or `start()` needed.
305
+ For conversational/prompt-driven agents where the LLM IS the logic.
299
306
 
300
307
  ```typescript
301
- import { guildTools, llmAgent, pick } from '@guildai/agents-sdk';
308
+ import { llmAgent, pick } from '@guildai/agents-sdk';
302
309
  import { gitHubTools } from '@guildai-services/guildai~github';
303
310
 
304
311
  export default llmAgent({
305
312
  description: 'Helps users with GitHub questions',
306
313
  tools: {
307
314
  ...pick(gitHubTools, ['github_issues_list_for_repo', 'github_issues_get']),
308
- ...guildTools, // ALWAYS spread fully — never pick() from guildTools
309
315
  },
310
316
  systemPrompt: `
311
317
  You are a helpful assistant that answers questions about GitHub repositories.
312
318
  Use the GitHub tools to look up information when asked.
313
319
  `,
314
- mode: 'multi-turn', // "one-shot" (default) or "multi-turn"
320
+ mode: 'one-shot', // "one-shot" (default) or "multi-turn"
321
+ useWorkspaceAgents: false,
315
322
  });
316
323
  ```
317
324
 
@@ -320,7 +327,7 @@ export default llmAgent({
320
327
  By default, `llmAgent` accepts `{ type: "text", text: string }` and sends `text` as the first user message. Override this for structured inputs:
321
328
 
322
329
  ```typescript
323
- import { guildTools, llmAgent, pick } from '@guildai/agents-sdk';
330
+ import { llmAgent, pick } from '@guildai/agents-sdk';
324
331
  import { gitHubTools } from '@guildai-services/guildai~github';
325
332
  import { z } from 'zod';
326
333
 
@@ -333,20 +340,47 @@ export default llmAgent({
333
340
  inputTemplate: 'Analyze repo {{repo}} on branch {{branch}}',
334
341
  tools: {
335
342
  ...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
336
- ...guildTools,
337
343
  },
338
344
  systemPrompt: 'You analyze GitHub repositories.',
345
+ mode: 'one-shot', // "one-shot" (default) or "multi-turn"
346
+ useWorkspaceAgents: false,
339
347
  });
340
348
  ```
341
349
 
342
350
  - `inputSchema` — Zod schema for the agent's input (replaces the default `{ type: "text", text: string }`)
343
351
  - `inputTemplate` — Mustache-style template that renders the input as the initial LLM user message (default: `"{{text}}"`)
344
352
 
345
- **`useWorkspaceAgents`** — When `true`, the agent dynamically discovers and can call other agents installed in the workspace (like Guild's built-in assistant does). Defaults to `true`, but most agents should set this to `false` unless they specifically need dynamic agent discovery. For deterministic orchestration, use `guildAgentTool()` to delegate to specific agents instead.
353
+ **`mode`: `one-shot` versus `multi-turn`**
354
+
355
+ An LLM agent whose `mode` is `one-shot` will run for a single turn and then return its output as a result. The "single turn" may include rounds of tool calls, thinking, etc. but once its computation is complete it will terminate and control will be restored to the agent that invoked it. This appropriate for most agents.
356
+
357
+ TIP: use this agent whenever you need fully autonomous operation without user interaction.
358
+
359
+ An LLM agent whose `mode` is `multi-turn` does not automatically return control to its caller and will instead proceed interactively -- i.e., prompting the user -- until a specific termination criteria is defined. You must specify this exact termination criteria and instruct the agent to call the `__submit__` tool once that criteria is achieved. This will end the interactive loop and restore control to the caller.
360
+
361
+ WARNING: this is **rarely** the mode that an agent should operate in: it is **only** useful for agents that are guaranteed to be invoked interactively. USE WITH CAUTION!
362
+
363
+ **`useWorkspaceAgents`**
364
+
365
+ When `true`, the agent dynamically discovers and can call other agents installed in the workspace (like Guild's built-in assistant does). Defaults to `true`, but most agents should explicitly set this to `false` unless they specifically need dynamic agent discovery. For deterministic orchestration, use `guildAgentTool()` to delegate to specific agents instead.
366
+
367
+ IMPORTANT! Understand whether or not your agent will operate in an interactive environment; i.e., one where a user will be able to directly chat with the agent. (An agent that is activated from a trigger -- i.e., a webhook or a timer -- is considered **non-interactive** since no user is present.) If the agent is non-interactive, **NEVER** make use of the `ui_prompt` tool: this tool attempts to solicit a response from an interactive user who will not be available.
346
368
 
347
- ### 2. Automatic State Agent (`run()`) — Recommended for Code-First
369
+ **Tools**
348
370
 
349
- Runtime manages state via continuations. Return the output object directly. Use `task.tools.*` for all tool calls. Requires `"use agent"` directive.
371
+ - Provide an LLM agent with the minimum set of tools that it needs to accomplish its task.
372
+ - Many models have strict limits on the number of tools that are allowed.
373
+ - Unnecessary tools waste context space and risk confusion.
374
+ - Use the `pick` utility to select multiple tools from an integration in a succinct way.
375
+ - NEVER spread an entire toolset (e.g. `...gitHubTools`) in an LLM agent: this is bad form, and you always want to look good.
376
+
377
+ ### 2. Code Agent with Automatic State Management
378
+
379
+ An agent that is implemented as a simple TypeScript `run` function. Ideal for most situations that require coding.
380
+
381
+ - Implement a single `run()` function that accepts the input and returns the output: the function is implemented in a natural procedural style that is easy to understand and maintain.
382
+ - Requires the `"use agent"` directive at top of the file: this triggers a compilation step that converts the TypeScript code into a resumable state machine that can be suspended at for long-running tasks.
383
+ - Use `task.tools.*` for all integration tool calls.
350
384
 
351
385
  ```typescript
352
386
  'use agent';
@@ -377,7 +411,6 @@ type Output = z.infer<typeof outputSchema>;
377
411
 
378
412
  const tools = {
379
413
  ...pick(gitHubTools, ['github_search_issues_and_pull_requests']),
380
- ...guildTools, // ALWAYS spread fully — never pick() from guildTools
381
414
  ...userInterfaceTools,
382
415
  };
383
416
 
@@ -411,16 +444,18 @@ export default agent({
411
444
  });
412
445
  ```
413
446
 
414
- **Key points:**
447
+ ### 3. Coded agent with Self-Managed State
415
448
 
416
- - `run()` returns the OUTPUT directly (not wrapped in `{ type: "output", output: ... }`)
417
- - The runtime handles continuations — you can `await` tool calls inline
418
- - `"use agent"` directive at top of file is optional (the Babel compiler recognizes it but strips it)
419
- - No `identifier` field needed
449
+ For explicit state control.
420
450
 
421
- ### 3. Self-Managed State Agent (`start()`/`onToolResults()`)
422
-
423
- For explicit state control. Uses `ask()`, `output()`, `callTools()` helpers and `task.save()`/`task.restore()`.
451
+ - Implement the `start()` and `onToolResults()` functions: these return `AgentResult<Output, Tools>`
452
+ - `return ask(prompt)` — sends a `ui_prompt` tool call to get user input
453
+ - `return output(value)` wraps your output as `{ type: "output", output: value }`
454
+ - `return callTools([...])` — requests the runtime to execute tool calls
455
+ - `task.save(state)` / `task.restore()` persist state between invocations: you must explicitly specify the schema of the state.
456
+ - May be necessary for more elaborate situations; notably, invoking multiple agents or tools in parallel.
457
+ - Generally results in a much more complicated program that can be difficult to understand and maintain; avoid if possible.
458
+ - No `"use agent"` directive since there is no compilation step.
424
459
 
425
460
  ```typescript
426
461
  import {
@@ -488,19 +523,11 @@ export default agent({
488
523
  });
489
524
  ```
490
525
 
491
- **Key points:**
492
-
493
- - `start()` and `onToolResults()` return `AgentResult<Output, Tools>`
494
- - `ask(prompt)` — sends a `ui_prompt` tool call to get user input
495
- - `output(value)` — wraps your output as `{ type: "output", output: value }`
496
- - `callTools([...])` — requests the runtime to execute tool calls
497
- - `task.save(state)` / `task.restore()` — persist state between invocations
498
-
499
526
  ---
500
527
 
501
528
  ## Agent-to-Agent Delegation
502
529
 
503
- Every published agent exposes a `/tool` sub-package that wraps it as a callable tool. Import it and add it to your tools object — the runtime handles dispatch.
530
+ Every published agent exposes a `/tool` sub-package that allows it to be used just like any other callable tool. Import it and add it to your tools object — the runtime handles dispatch.
504
531
 
505
532
  ### Using a published agent as a tool (preferred)
506
533
 
@@ -580,7 +607,7 @@ export default agent({
580
607
 
581
608
  ### Using the Coding Agent
582
609
 
583
- The coding agent runs instructions inside an isolated container. Use it when your agent needs to read/write files, run shell commands, or work with a cloned repository.
610
+ The coding agent runs instructions inside a dedicated (but isolated) virtual machine. The agent has full access to the computer: use it when your agent needs to read/write files, run shell commands, or work with a cloned repository.
584
611
 
585
612
  ```typescript
586
613
  import { ExperimentalCodingTools as codingTools } from '@guildai-services/guildai~experimental-coding';
@@ -672,6 +699,8 @@ Using `session_id` preserves the container's working directory, environment vari
672
699
 
673
700
  **GitHub tools for common container tasks:**
674
701
 
702
+ The GitHub API provides primitives that must be composed in the coding container for common operations. If you need the agent to perform any of the following tasks, be sure to include the tools upon which the task depends. Use the table below as a reference.
703
+
675
704
  | Task | Required Tools |
676
705
  | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
677
706
  | Clone a repository | `github_repos_download_zipball_archive` |
@@ -684,7 +713,9 @@ Using `session_id` preserves the container's working directory, environment vari
684
713
 
685
714
  ---
686
715
 
687
- ## Advanced: Compiled Agent Patterns
716
+ ## Advanced Topics
717
+
718
+ ### Example: Using an LLM from within a coded agent
688
719
 
689
720
  For agents that need an LLM tool-calling loop with fine-grained control over which tool calls the LLM handles vs which get delegated to the runtime:
690
721
 
@@ -755,10 +786,9 @@ async function onToolResults(
755
786
  - `delegatedCallsOf<Tools>(content)` — extracts unexecuted tool calls from `generateText` results that need runtime delegation
756
787
  - `asToolResultContent(results)` — converts `TypedToolResult[]` into LLM message format for conversation history
757
788
 
758
- ### Slack-Specific Patterns
789
+ ### Example: Slack-Specific Patterns
759
790
 
760
- When posting to Slack, convert markdown to Slack's mrkdwn format. Use an inline converter
761
- (`slackify-markdown` is CJS and breaks in the ESM agent runtime):
791
+ When posting to Slack, convert markdown to Slack's mrkdwn format. Use an inline converter (`slackify-markdown` is CJS and breaks in the ESM agent runtime):
762
792
 
763
793
  ```typescript
764
794
  // Simple markdown-to-Slack-mrkdwn converter (inline — do NOT use slackify-markdown)
@@ -793,7 +823,7 @@ await task.tools.slack_chat_post_message({
793
823
 
794
824
  ### Discovering Available Tools
795
825
 
796
- **Do NOT guess tool names.** Use the Guild CLI to discover what tools exist for each integration:
826
+ **NEVER attempt to guess a tool's name.** Use the Guild CLI to discover what tools exist for each integration:
797
827
 
798
828
  ```bash
799
829
  # List all available integrations
@@ -806,7 +836,10 @@ guild integration operation list guildai~github
806
836
  guild integration operation list guildai~github --json
807
837
  ```
808
838
 
809
- Tool names follow the pattern `{service_prefix}_{operation_name}` (e.g., `github_pulls_get`). Check the service packages table above for each service's prefix.
839
+ Tool names follow the pattern `{service_prefix}_{operation_name}` (e.g., `github_pulls_get`).
840
+
841
+ - Check the service packages table above for common service prefixes.
842
+ - For an integration not listed above (e.g., `@guildai-services/some-owner~some-integration`), the service prefix will be the _integration name_ (e.g., `some-integration`) with any hyphens replaced by underscores (e.g., `some_integration`).
810
843
 
811
844
  Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
812
845
 
@@ -814,41 +847,69 @@ Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
814
847
  // Include only specific tools
815
848
  const tools = {
816
849
  ...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
817
- ...guildTools,
818
850
  };
819
851
 
820
852
  // Include all tools except specific ones
821
853
  const tools = {
822
854
  ...omit(gitHubTools, ['github_repos_delete', 'github_repos_update']),
823
- ...guildTools,
824
855
  };
825
856
  ```
826
857
 
827
- ### NEVER `pick()` from `guildTools`
858
+ ### Adding an integration for your agent
828
859
 
829
- **CRITICAL: Always spread `guildTools` fully. NEVER use `pick(guildTools, [...])`.**
860
+ To add an integration for you agent, use `npm install`:
830
861
 
831
- The SDK's `Task` type conditionally provides `task.guild: GuildService` based on whether the **full** `guildTools` set is in the tools type. Using `pick()` creates a subset type that doesn't satisfy this constraint, causing:
862
+ ```bash
863
+ # Add an integration
864
+ npm install --save @guildai-services/some-owner~some-integration
832
865
 
833
- - `task.guild` becomes `undefined` instead of `GuildService`
834
- - Type errors in any function that expects `task.guild` to exist
835
- - Build failures during agent validation (which blocks publishing)
866
+ # Add an agent as a tool
867
+ npm install --save @guildai/some-owner~some-agent
868
+ ```
869
+
870
+ The integration or agent should be added as a dependency (i.e., _not_ a `devDependency`)
871
+
872
+ Once you've done that, import the agent into your agent as you would any other package:
836
873
 
837
874
  ```typescript
838
- // WRONG task.guild will be undefined, TypeScript build fails
839
- const tools = {
840
- ...pick(guildTools, ['guild_get_me', 'guild_create_agent']),
841
- ...userInterfaceTools,
842
- };
875
+ // An integration. This object will be a ToolSet from which you should select the tools you need.
876
+ import { SomeIntegration } from '@guildai-services/some-owner~some-integration';
877
+
878
+ // An agent... note that it's imported from the `/tool` sub-package as the default export
879
+ import SomeAgent from '@guildai/some-onwer~some-agent/tool';
843
880
 
844
- // CORRECT always spread fully
881
+ // Include the relevant tools in your agent's tool object.
845
882
  const tools = {
846
- ...guildTools,
847
- ...userInterfaceTools,
883
+ // The integration tools.
884
+ ...pick(SomeIntegration, ['some_integration_tool_one', 'some_integration_tool_two']),
885
+
886
+ // The agent tool
887
+ someAgentTool: SomeAgent,
848
888
  };
849
889
  ```
850
890
 
851
- **`pick()` is fine for service tools** like `gitHubTools`, `slackTools`, etc. It's only `guildTools` that must be spread fully.
891
+ **For a coded agent...**
892
+
893
+ Once you've done the above, you can use the TypeScript LSP to determine the exact parameter and return type for each tool. OR... you can inspect the TypeScript types for the package directly in the `node_modules` subdirectory (this is often an expedient way to understand the type details).
894
+
895
+ IMPORTANT! Always let TypeScript help you:
896
+
897
+ - Let the TypeScript compiler infer the type of the `tools` object: DO NOT provide an explicit type annotation as it is likely incorrect!
898
+ - NEVER cast to `any`: failure to resolve correct types likely means that something else is going wrong so keep investigating.
899
+ - Adding a tool to the agent's `tools` creates the correct signature on the `task` object:
900
+
901
+ ```typescript
902
+ // Assume `tools` is defined as above, and that `Input` and `Output` are the `z.infer'd` types
903
+ type Tools = typeof tools;
904
+
905
+ async function run(input: Input, task: Task<Tools>): Promise<Output> {
906
+ // These should *not* resolve to `any` type, but should instead be strongly typed if all your imports have been done correctly.
907
+ const { foo, bar } = await task.tools.some_integration_tool_one({
908
+ baz: 'bop',
909
+ bip: 12,
910
+ });
911
+ }
912
+ ```
852
913
 
853
914
  ### DO NOT USE (Common Mistakes)
854
915
 
@@ -883,10 +944,6 @@ import { gitHubTools } from "@guildai-services/guildai~github/src/service"
883
944
  import { agent } from "@guildai/agents-sdk"
884
945
  // (no "use agent" at top)
885
946
  export default agent({ run: async (input, task) => { ... } })
886
-
887
- // ❌ WRONG: picking from guildTools breaks task.guild typing
888
- ...pick(guildTools, ["guild_get_me", "guild_create_agent"])
889
- // Use ...guildTools instead (spread fully)
890
947
  ```
891
948
 
892
949
  ### CORRECT Patterns
@@ -995,8 +1052,9 @@ For simple `llmAgent()` agents that don't use `"use agent"`, you can skip the Ba
995
1052
 
996
1053
  **CRITICAL:**
997
1054
 
998
- - Do NOT add `@guildai/agents-sdk`, `@guildai-services/*`, or `zod` to dependencies. The runtime provides them.
999
- - DO add third-party ESM-compatible packages your agent uses to `dependencies`. Note: CJS-only packages (e.g., `slackify-markdown`) will break in the ESM agent runtime — use inline alternatives instead.
1055
+ - DO NOT modify the `@guildai/agents-sdk` and `zod` dependencies provided in the agent's template.
1056
+ - You may add third-party ESM-compatible packages your agent uses to `dependencies`.
1057
+ - DO NOT include CJS packages: an agent that includes a CJS module will fail at runtime.
1000
1058
  - `devDependencies` is for build tools only (`esbuild` for bundling, `typescript` for compilation).
1001
1059
 
1002
1060
  ## Versioning
@@ -1028,6 +1086,12 @@ my-agent/
1028
1086
 
1029
1087
  ## CLI Commands
1030
1088
 
1089
+ Below is a quick reference of common CLI commands.
1090
+
1091
+ - Use `guild help` to discover the full set of commands
1092
+ - Use `guild <command> [...<subcommand>] --help` with any command for full documentation on its usage
1093
+ - Prefer to explicitly provide all command arguments rather than relying on defaults
1094
+
1031
1095
  ```bash
1032
1096
  guild setup # Install coding assistant skills
1033
1097
  guild setup --claude-md # Also create CLAUDE.md template
@@ -1068,7 +1132,6 @@ guild workspace select # Set default workspace (writ
1068
1132
 
1069
1133
  ```bash
1070
1134
  GUILD_WORKSPACE_ID=<id> guild agent test # Override workspace for this command
1071
- GUILD_OWNER_ID=<id> guild agent init --name my-agent # Override owner for agent creation
1072
1135
  ```
1073
1136
 
1074
1137
  ## Troubleshooting
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guildai/cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Guild.ai CLI - Build, test, and deploy AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -63,6 +63,7 @@
63
63
  "chalk": "^5.3.0",
64
64
  "commander": "^12.1.0",
65
65
  "dotenv": "^16.4.7",
66
+ "esbuild": "^0.28.0",
66
67
  "execa": "^9.6.1",
67
68
  "ink": "^5.0.1",
68
69
  "ink-text-input": "^6.0.0",