@guildai/cli 0.5.12 → 0.5.13

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.
@@ -8,6 +8,7 @@ import * as readline from 'readline';
8
8
  import { runGit, GitError, formatGitError } from '../../lib/git.js';
9
9
  import { createOutputWriter } from '../../lib/output.js';
10
10
  import { resolveOwnerId } from '../../lib/owner-helpers.js';
11
+ import { isInteractive } from '../../lib/stdin.js';
11
12
  async function promptForName() {
12
13
  const rl = readline.createInterface({
13
14
  input: process.stdin,
@@ -32,9 +33,6 @@ async function promptForDescription(defaultDescription) {
32
33
  });
33
34
  });
34
35
  }
35
- function isInteractive() {
36
- return process.stdin.isTTY === true;
37
- }
38
36
  async function isDirectoryEmpty(dirPath) {
39
37
  try {
40
38
  const files = await fs.readdir(dirPath);
@@ -11,6 +11,7 @@ import * as readline from 'readline';
11
11
  import { getAuthenticatedUrl } from '../../lib/auth.js';
12
12
  import { runGit, GitError, formatGitError, installPrePushHook } from '../../lib/git.js';
13
13
  import { resolveOwnerId } from '../../lib/owner-helpers.js';
14
+ import { isInteractive } from '../../lib/stdin.js';
14
15
  const TEMPLATE_CHOICES = [
15
16
  {
16
17
  name: 'LLM - Simple language model agent (recommended)',
@@ -71,9 +72,6 @@ async function promptForTemplate() {
71
72
  ]);
72
73
  return template;
73
74
  }
74
- function isInteractive() {
75
- return process.stdin.isTTY === true;
76
- }
77
75
  export function createAgentInitCommand() {
78
76
  const cmd = new Command('init');
79
77
  cmd
@@ -2,10 +2,11 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { Command } from 'commander';
4
4
  import * as fs from 'fs/promises';
5
- import * as readline from 'readline';
5
+ import search from '@inquirer/search';
6
6
  import { GuildAPIClient } from '../../lib/api-client.js';
7
7
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
8
8
  import { createOutputWriter } from '../../lib/output.js';
9
+ import { isInteractive } from '../../lib/stdin.js';
9
10
  import { isAgentDirectory, loadLocalConfig, getLocalConfigPath, getWorkspaceId, saveGlobalConfig, } from '../../lib/guild-config.js';
10
11
  /**
11
12
  * Format workspace for display with owner name.
@@ -68,44 +69,39 @@ export function createWorkspaceSelectCommand() {
68
69
  }
69
70
  return;
70
71
  }
71
- // Interactive mode: fetch workspaces for selection
72
- const workspaces = await client.get('/me/workspaces?filter=all');
73
- if (workspaces.items.length === 0) {
72
+ if (!isInteractive()) {
73
+ output.error('Interactive mode requires a terminal.', 'Provide a workspace argument:\n guild workspace select <name|id>');
74
+ process.exit(1);
75
+ }
76
+ // Interactive mode: fetch all workspaces across all pages
77
+ const workspaces = await client.fetchAll('/me/workspaces?filter=all');
78
+ if (workspaces.length === 0) {
74
79
  output.error('No workspaces found.', 'Create a workspace first:\n guild workspace create <name>');
75
80
  process.exit(1);
76
81
  }
77
82
  // Resolve the currently selected workspace (if any)
78
83
  const current = await getWorkspaceId();
79
- const currentIndex = current
80
- ? workspaces.items.findIndex((w) => w.id === current.workspaceId)
81
- : -1;
82
- // Show interactive selection
83
- console.log('Select a default workspace:\n');
84
- workspaces.items.forEach((workspace, index) => {
85
- const marker = index === currentIndex ? ' (current)' : '';
86
- console.log(` ${index + 1}. ${formatWorkspaceDisplay(workspace)}${marker}`);
87
- });
88
- console.log('');
89
- const rl = readline.createInterface({
90
- input: process.stdin,
91
- output: process.stdout,
92
- });
93
- const defaultHint = currentIndex >= 0 ? ` [${currentIndex + 1}]` : '';
94
- const answer = await new Promise((resolve) => {
95
- rl.question(`Enter number (1-${workspaces.items.length})${defaultHint}: `, resolve);
84
+ const currentId = current?.workspaceId;
85
+ // Interactive searchable selection
86
+ const selectedId = await search({
87
+ message: 'Select a workspace (type to filter)',
88
+ pageSize: 15,
89
+ default: currentId,
90
+ source: (input) => {
91
+ const term = input?.toLowerCase() ?? '';
92
+ const filtered = term
93
+ ? workspaces.filter((w) => w.name.toLowerCase().includes(term) ||
94
+ w.full_name?.toLowerCase().includes(term) ||
95
+ w.owner?.name.toLowerCase().includes(term))
96
+ : workspaces;
97
+ return filtered.map((w) => ({
98
+ name: formatWorkspaceDisplay(w) + (w.id === currentId ? ' (current)' : ''),
99
+ value: w.id,
100
+ short: w.name,
101
+ }));
102
+ },
96
103
  });
97
- rl.close();
98
- // Empty input keeps current selection
99
- if (answer.trim() === '' && currentIndex >= 0) {
100
- console.log('Workspace unchanged.');
101
- return;
102
- }
103
- const selection = parseInt(answer.trim(), 10);
104
- if (isNaN(selection) || selection < 1 || selection > workspaces.items.length) {
105
- output.error('Invalid selection');
106
- process.exit(1);
107
- }
108
- const selectedWorkspace = workspaces.items[selection - 1];
104
+ const selectedWorkspace = workspaces.find((w) => w.id === selectedId);
109
105
  const target = await saveWorkspaceConfig(selectedWorkspace.id, selectedWorkspace.name);
110
106
  if (target === 'local') {
111
107
  output.success(`Workspace set for this agent: ${formatWorkspaceDisplay(selectedWorkspace)}`);
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Check if stdin is connected to an interactive terminal (TTY).
3
+ */
4
+ export declare function isInteractive(): boolean;
1
5
  /**
2
6
  * Exit with a helpful error when stdin is piped but no --mode flag was given.
3
7
  * Call this before rendering an interactive UI (Ink render()) so users who
package/dist/lib/stdin.js CHANGED
@@ -1,5 +1,11 @@
1
1
  // Copyright 2026 Guild.ai
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Check if stdin is connected to an interactive terminal (TTY).
5
+ */
6
+ export function isInteractive() {
7
+ return process.stdin.isTTY === true;
8
+ }
3
9
  /**
4
10
  * Exit with a helpful error when stdin is piped but no --mode flag was given.
5
11
  * Call this before rendering an interactive UI (Ink render()) so users who
@@ -151,16 +151,22 @@ import { ask, output, callTools } from '@guildai/agents-sdk';
151
151
  import { guildTools, userInterfaceTools } from '@guildai/agents-sdk';
152
152
 
153
153
  // Service tools (from separate packages - NOT from SDK)
154
+ import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
155
+ import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
156
+ import { confluenceTools } from '@guildai-services/guildai~confluence';
157
+ import { cypressTools } from '@guildai-services/guildai~cypress';
158
+ import { figmaTools } from '@guildai-services/guildai~figma';
154
159
  import { gitHubTools } from '@guildai-services/guildai~github';
155
- import { slackTools } from '@guildai-services/guildai~slack';
160
+ import { googleComputeTools } from '@guildai-services/guildai~google-compute';
161
+ import { googleLoggingTools } from '@guildai-services/guildai~google-logging';
156
162
  import { jiraTools } from '@guildai-services/guildai~jira';
157
- import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
158
- import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
159
163
  import { pipedreamTools } from '@guildai-services/guildai~pipedream';
160
- import { cypressTools } from '@guildai-services/guildai~cypress';
164
+ import { slackTools } from '@guildai-services/guildai~slack';
165
+ import { testrailTools } from '@guildai-services/guildai~testrail';
166
+ import { zendeskTools } from '@guildai-services/guildai~zendesk';
161
167
 
162
168
  // Utilities
163
- import { pick, progressLogNotifyEvent } from '@guildai/agents-sdk';
169
+ import { pick, omit, progressLogNotifyEvent } from '@guildai/agents-sdk';
164
170
 
165
171
  // Advanced (for compiled agents with LLM tool loops)
166
172
  import { delegatedCallsOf, asToolResultContent } from '@guildai/agents-sdk';
@@ -173,19 +179,25 @@ import { z } from 'zod';
173
179
 
174
180
  Service tools are in separate `@guildai-services/*` packages. The runtime resolves them automatically.
175
181
 
176
- | Service | Package | Export | Tool Name Prefix |
177
- | -------------- | ---------------------------------------- | -------------------- | ---------------- |
178
- | Azure DevOps | `@guildai-services/guildai~azure-devops` | `azureDevOpsTools` | `azure_devops_` |
179
- | Bitbucket | `@guildai-services/guildai~bitbucket` | `bitbucketTools` | `bitbucket_` |
180
- | Cypress | `@guildai-services/guildai~cypress` | `cypressTools` | `cypress_` |
181
- | GitHub | `@guildai-services/guildai~github` | `gitHubTools` | `github_` |
182
- | Guild | `@guildai/agents-sdk` | `guildTools` | `guild_` |
183
- | Jira | `@guildai-services/guildai~jira` | `jiraTools` | `jira_` |
184
- | Linear | `@guildai-services/guildai~linear` | `linearTools` | `linear_` |
185
- | NewRelic | `@guildai-services/guildai~newrelic` | `newrelicTools` | `newrelic_` |
186
- | Pipedream | `@guildai-services/guildai~pipedream` | `pipedreamTools` | `pipedream_` |
187
- | Slack | `@guildai-services/guildai~slack` | `slackTools` | `slack_` |
188
- | User Interface | `@guildai/agents-sdk` | `userInterfaceTools` | `ui_` |
182
+ | Service | Package | Export | Tool Name Prefix |
183
+ | -------------- | ------------------------------------------ | -------------------- | ----------------- |
184
+ | Azure DevOps | `@guildai-services/guildai~azure-devops` | `azureDevOpsTools` | `azure_devops_` |
185
+ | Bitbucket | `@guildai-services/guildai~bitbucket` | `bitbucketTools` | `bitbucket_` |
186
+ | Confluence | `@guildai-services/guildai~confluence` | `confluenceTools` | `confluence_` |
187
+ | Cypress | `@guildai-services/guildai~cypress` | `cypressTools` | `cypress_` |
188
+ | Figma | `@guildai-services/guildai~figma` | `figmaTools` | `figma_` |
189
+ | GitHub | `@guildai-services/guildai~github` | `gitHubTools` | `github_` |
190
+ | Google Compute | `@guildai-services/guildai~google-compute` | `googleComputeTools` | `google_compute_` |
191
+ | Google Logging | `@guildai-services/guildai~google-logging` | `googleLoggingTools` | `google_logging_` |
192
+ | Guild | `@guildai/agents-sdk` | `guildTools` | `guild_` |
193
+ | Jira | `@guildai-services/guildai~jira` | `jiraTools` | `jira_` |
194
+ | Linear | `@guildai-services/guildai~linear` | `linearTools` | `linear_` |
195
+ | NewRelic | `@guildai-services/guildai~newrelic` | `newrelicTools` | `newrelic_` |
196
+ | Pipedream | `@guildai-services/guildai~pipedream` | `pipedreamTools` | `pipedream_` |
197
+ | Slack | `@guildai-services/guildai~slack` | `slackTools` | `slack_` |
198
+ | TestRail | `@guildai-services/guildai~testrail` | `testrailTools` | `testrail_` |
199
+ | User Interface | `@guildai/agents-sdk` | `userInterfaceTools` | `ui_` |
200
+ | Zendesk | `@guildai-services/guildai~zendesk` | `zendeskTools` | `zendesk_` |
189
201
 
190
202
  ### Tool Access via `task.tools.*`
191
203
 
@@ -220,12 +232,16 @@ await task.tools.guild_credentials_request({ service: 'GITHUB' });
220
232
 
221
233
  ### Task Properties
222
234
 
223
- | Property | Description |
224
- | ---------------- | ------------------------------------------------------------------------------------ |
225
- | `task.sessionId` | Session ID for correlating operations |
226
- | `task.console` | Debug logging (`task.console.debug(...)`, `.info(...)`, `.warn(...)`, `.error(...)`) |
227
- | `task.tools` | Primary API for calling all tools |
228
- | `task.guild` | Guild service (available when **all** `guildTools` included see warning below) |
235
+ | Property | Description |
236
+ | ---------------- | ------------------------------------------------------------------------------------------- |
237
+ | `task.sessionId` | Session ID for correlating operations |
238
+ | `task.tools` | Primary API for calling all tools |
239
+ | `task.llm` | LLM service — call `task.llm.generateText({ messages, system, tools })` for AI model access |
240
+ | `task.console` | Debug logging (`task.console.debug(...)`, `.info(...)`, `.warn(...)`, `.error(...)`) |
241
+ | `task.save()` | Persist agent state (self-managed state agents only) |
242
+ | `task.restore()` | Retrieve previously saved state (self-managed state agents only) |
243
+ | `task.guild` | **Deprecated** — use `task.tools.guild_*` instead |
244
+ | `task.ui` | **Deprecated** — use `task.tools.ui_*` instead |
229
245
 
230
246
  ---
231
247
 
@@ -340,7 +356,7 @@ export default agent({
340
356
 
341
357
  - `run()` returns the OUTPUT directly (not wrapped in `{ type: "output", output: ... }`)
342
358
  - The runtime handles continuations — you can `await` tool calls inline
343
- - Requires `"use agent"` directive at top of file
359
+ - `"use agent"` directive at top of file is optional (the Babel compiler recognizes it but strips it)
344
360
  - No `identifier` field needed
345
361
 
346
362
  ### 3. Self-Managed State Agent (`start()`/`onToolResults()`)
@@ -474,7 +490,7 @@ import {
474
490
  } from '@guildai/agents-sdk';
475
491
  import { gitHubTools } from '@guildai-services/guildai~github';
476
492
  import { slackTools } from '@guildai-services/guildai~slack';
477
- import { generateText, type ModelMessage } from 'ai';
493
+ import type { ModelMessage } from 'ai';
478
494
  import { z } from 'zod';
479
495
 
480
496
  const tools = { ...gitHubTools, ...slackTools, ...userInterfaceTools };
@@ -485,15 +501,12 @@ const llmTools = { ...gitHubTools }; // LLM gets execute access to these
485
501
  const agentTools = { ...slackTools, ...userInterfaceTools }; // These get delegated
486
502
 
487
503
  async function start(input, task: Task<Tools>) {
488
- const model = await task.llm.model();
489
504
  const messages: ModelMessage[] = [{ role: 'user', content: input.text }];
490
505
 
491
- const result = await generateText({
492
- model,
506
+ const result = await task.llm.generateText({
493
507
  system: 'You are a helpful assistant.',
494
508
  messages,
495
509
  tools: llmTools, // Only give LLM the tools it can execute
496
- maxSteps: 10,
497
510
  });
498
511
 
499
512
  // Check for delegated (unexecuted) tool calls
@@ -525,6 +538,7 @@ async function onToolResults(
525
538
 
526
539
  **Key utilities:**
527
540
 
541
+ - `task.llm.generateText({ messages, system, tools })` — call the LLM with automatic authentication and provider selection. The runtime handles model selection and credential injection.
528
542
  - `delegatedCallsOf<Tools>(content)` — extracts unexecuted tool calls from `generateText` results that need runtime delegation
529
543
  - `asToolResultContent(results)` — converts `TypedToolResult[]` into LLM message format for conversation history
530
544
 
@@ -572,13 +586,20 @@ await task.tools.slack_chat_post_message({
572
586
 
573
587
  **CRITICAL: Only use tool names listed below.** If a tool isn't listed here, it doesn't exist. Do not guess tool names based on API patterns.
574
588
 
575
- Use `pick()` to select specific tools:
589
+ Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
576
590
 
577
591
  ```typescript
592
+ // Include only specific tools
578
593
  const tools = {
579
594
  ...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
580
595
  ...guildTools,
581
596
  };
597
+
598
+ // Include all tools except specific ones
599
+ const tools = {
600
+ ...omit(gitHubTools, ['github_repos_delete', 'github_repos_update']),
601
+ ...guildTools,
602
+ };
582
603
  ```
583
604
 
584
605
  #### Azure DevOps (`azure_devops_` prefix, 122 tools)
@@ -1673,8 +1694,8 @@ export default agent({ run: async (input, task) => { ... } })
1673
1694
  ## Versioning
1674
1695
 
1675
1696
  - Use semver: `1.0.0` → `1.0.1` (patch), `1.1.0` (minor), `2.0.0` (breaking)
1676
- - Keep `package.json` version in sync with your releases
1677
- - Bump version before `guild agent save --wait --publish`
1697
+ - Use `--bump [patch|minor|major]` with `guild agent save` to auto-bump `package.json` version
1698
+ - Or bump manually in `package.json` before saving
1678
1699
 
1679
1700
  ## File Structure
1680
1701
 
@@ -1709,15 +1730,21 @@ guild agent pull # Pull remote changes
1709
1730
  guild agent save # Push commits and create a draft version
1710
1731
  guild agent save -A --message "description" # Stage+commit+push in one step
1711
1732
  guild agent save --message "v1.0" --wait --publish # Save + validate + publish
1733
+ guild agent save --bump minor --message "v1.1" # Auto-bump version before saving
1712
1734
  guild agent test # Interactive test
1713
1735
  guild agent test --ephemeral # Ephemeral test
1714
1736
  guild agent chat "Hello" # Test with input
1715
1737
  guild agent get [agent-id] # View agent info
1738
+ guild agent list # List agents
1739
+ guild agent list --search "github" --published # Search published agents
1740
+ guild agent search <query> # Search published agents
1716
1741
  guild agent versions [agent-id] # Version history
1717
1742
  guild agent clone <agent-id> # Clone existing agent
1743
+ guild agent fork <agent-id>:<version-id> # Fork a specific version as a new agent
1718
1744
  guild agent publish # Publish a version
1719
1745
  guild agent unpublish # Remove from catalog
1720
1746
  guild agent update # Update agent metadata
1747
+ guild agent workspaces [agent-id] # List workspaces using an agent
1721
1748
  guild agent tags list|add|remove|set # Manage agent tags
1722
1749
  guild agent revalidate # Re-run validation
1723
1750
  guild agent code [agent-id] # View agent source
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guildai/cli",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "description": "Guild.ai CLI - Build, test, and deploy AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -55,6 +55,7 @@
55
55
  "url": "https://docs.guild.ai/support"
56
56
  },
57
57
  "dependencies": {
58
+ "@inquirer/search": "^4.1.7",
58
59
  "@napi-rs/canvas": "^0.1.85",
59
60
  "@napi-rs/keyring": "^1.2.0",
60
61
  "axios": "^1.13.2",