@guildai/cli 0.8.1 → 0.9.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.
@@ -15,7 +15,7 @@ import { format } from '../../lib/progress.js';
15
15
  import * as readline from 'readline';
16
16
  import { parseEventFilter } from '../../lib/event-filter.js';
17
17
  import { isQuietMode, getOutputMode } from '../../lib/output-mode.js';
18
- import { pollForResponse, pollForResponseWithEvents, } from '../../lib/session-polling.js';
18
+ import { pollForResponseWithEvents } from '../../lib/session-polling.js';
19
19
  import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
20
20
  import { loadLocalConfig, getWorkspaceId } from '../../lib/guild-config.js';
21
21
  import { GitError, formatGitError } from '../../lib/git.js';
@@ -36,15 +36,13 @@ export function createAgentTestCommand() {
36
36
  .option('--agent-version <id>', 'Test a specific version (UUID or version number)')
37
37
  .option('--resume <session-id>', 'Resume an existing test session')
38
38
  .option('--open', 'Open session in web dashboard')
39
- .option('--events <types>', 'Event types to stream (default: user). Shorthands: none, user, system, all, or comma-separated type names')
39
+ .option('--events <types>', 'Event types to stream (default: all). Shorthands: none, user, system, all, or comma-separated type names')
40
40
  .option('--bundle <file>', 'Path to a pre-built gzip+base64 bundle file')
41
41
  .option('--no-cache', 'Skip ephemeral build cache (force a fresh build)')
42
42
  .action(async (options) => {
43
43
  const cwd = process.cwd();
44
- // Parse --events filter once, before any branching
45
- const eventFilter = options.events
46
- ? parseEventFilter(options.events)
47
- : undefined;
44
+ // Parse --events filter once, before any branching (default: all)
45
+ const eventFilter = parseEventFilter(options.events ?? 'all');
48
46
  try {
49
47
  // Handle --resume: skip build, fetch existing session, hand off to ChatApp
50
48
  if (options.resume) {
@@ -272,9 +270,7 @@ export function createAgentTestCommand() {
272
270
  });
273
271
  // Poll for response (starting from beginning)
274
272
  // 3 minutes - allow time for agents that use LLM calls for input parsing
275
- const { response } = eventFilter
276
- ? await pollForResponseWithEvents(client, session.id, eventFilter, undefined, 180000)
277
- : await pollForResponse(client, session.id, undefined, 180000);
273
+ const { response } = await pollForResponseWithEvents(client, session.id, eventFilter, undefined, 180000);
278
274
  if (!response) {
279
275
  console.error('Error: No response received from agent within timeout');
280
276
  console.error('');
@@ -350,9 +346,7 @@ export function createAgentTestCommand() {
350
346
  content: jsonInput,
351
347
  });
352
348
  // Wait for response (looking for events after last seen)
353
- const result = eventFilter
354
- ? await pollForResponseWithEvents(client, session.id, eventFilter, lastEventId, 180000)
355
- : await pollForResponse(client, session.id, lastEventId, 180000);
349
+ const result = await pollForResponseWithEvents(client, session.id, eventFilter, lastEventId, 180000);
356
350
  lastEventId = result.lastEventId;
357
351
  if (!result.response) {
358
352
  console.error(`Timeout: No response for line ${lineNumber}`);
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function createJobGetStepCommand(): Command;
3
+ //# sourceMappingURL=get-step.d.ts.map
@@ -5,8 +5,8 @@ import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getAuthToken } from '../../lib/auth.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
7
  import { createOutputWriter } from '../../lib/output.js';
8
- export function createJobStepGetCommand() {
9
- const cmd = new Command('step-get');
8
+ export function createJobGetStepCommand() {
9
+ const cmd = new Command('get-step');
10
10
  cmd
11
11
  .description('Get details of a step in a job')
12
12
  .argument('<job-id>', 'Job ID')
@@ -37,4 +37,4 @@ export function createJobStepGetCommand() {
37
37
  });
38
38
  return cmd;
39
39
  }
40
- //# sourceMappingURL=step-get.js.map
40
+ //# sourceMappingURL=get-step.js.map
@@ -6,7 +6,7 @@ export function createMcpCommand() {
6
6
  const cmd = new Command('mcp');
7
7
  cmd
8
8
  .description('Start an MCP server over stdio for use with Claude Code, Cursor, and other MCP hosts')
9
- .option('--workspace <id>', 'Workspace ID (defaults to configured workspace)')
9
+ .option('--workspace <id>', 'Workspace ID or name (defaults to configured workspace)')
10
10
  .option('--debug', 'Enable debug logging to stderr')
11
11
  .addHelpText('after', `
12
12
  Configuration:
@@ -10,7 +10,7 @@ export function createSessionCreateCommand() {
10
10
  const cmd = new Command('create');
11
11
  cmd
12
12
  .description('Create a new session')
13
- .option('--workspace <id>', 'Workspace ID or name')
13
+ .option('--workspace <id>', 'Workspace ID or name (e.g., owner~workspace-name)')
14
14
  .option('--type <type>', 'Session type: chat or agent_test', 'chat')
15
15
  .option('--prompt <text>', 'Initial prompt (required for chat sessions)')
16
16
  .option('--agent <identifier>', 'Agent identifier, e.g., owner~agent-name')
@@ -11,7 +11,7 @@ export function createSessionListCommand() {
11
11
  const cmd = new Command('list');
12
12
  cmd
13
13
  .description('List sessions in a workspace')
14
- .option('--workspace <id>', 'Workspace ID or name')
14
+ .option('--workspace <id>', 'Workspace ID or name (e.g., owner~workspace-name)')
15
15
  .option('--type <type>', 'Filter by session type: chat, webhook, time, agent_test')
16
16
  .option('--limit <number>', 'Number of results to return', '20')
17
17
  .option('--offset <number>', 'Offset for pagination', '0')
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ import { createSessionCreateCommand } from './commands/session/create.js';
61
61
  import { createSessionSendCommand } from './commands/session/send.js';
62
62
  import { createSessionInterruptCommand } from './commands/session/interrupt.js';
63
63
  import { createJobGetCommand } from './commands/job/get.js';
64
- import { createJobStepGetCommand } from './commands/job/step-get.js';
64
+ import { createJobGetStepCommand } from './commands/job/get-step.js';
65
65
  import { createConfigListCommand } from './commands/config/list.js';
66
66
  import { createConfigGetCommand } from './commands/config/get.js';
67
67
  import { createConfigSetCommand } from './commands/config/set.js';
@@ -281,7 +281,10 @@ sessionCmd.addCommand(createSessionInterruptCommand());
281
281
  // Job command group
282
282
  const jobCmd = program.command('job').description('Job management');
283
283
  jobCmd.addCommand(createJobGetCommand());
284
- jobCmd.addCommand(createJobStepGetCommand());
284
+ jobCmd.addCommand(createJobGetStepCommand());
285
+ const stepGetAlias = createJobGetStepCommand();
286
+ stepGetAlias.name('step-get');
287
+ jobCmd.addCommand(stepGetAlias, { hidden: true });
285
288
  // Config command group
286
289
  const configCmd = program
287
290
  .command('config')
@@ -116,6 +116,12 @@ export interface ValidationStep {
116
116
  export interface ValidationStepsResponse {
117
117
  steps: ValidationStep[];
118
118
  }
119
+ /**
120
+ * Response from /versions/{id}/publish/steps
121
+ */
122
+ export interface PublishStepsResponse {
123
+ steps: ValidationStep[];
124
+ }
119
125
  /**
120
126
  * Workspace context entity from the API.
121
127
  * Used by: workspace context list, get, edit
@@ -38,6 +38,20 @@ function truncate(str, maxLen) {
38
38
  return str;
39
39
  return str.slice(0, maxLen - 1) + '…';
40
40
  }
41
+ /**
42
+ * Print the "Showing N of M <entity>" footer after a paginated table.
43
+ */
44
+ function printPaginationFooter(pagination, count, entityName) {
45
+ const showing = Math.min(pagination.limit, count);
46
+ if (pagination.has_more) {
47
+ const nextOffset = pagination.offset + pagination.limit;
48
+ console.log(`\nShowing ${showing} of ${pagination.total_count} ${entityName}. ` +
49
+ chalk.dim(`Use --offset ${nextOffset} to see more.`));
50
+ }
51
+ else if (pagination.total_count > showing) {
52
+ console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} ${entityName}`));
53
+ }
54
+ }
41
55
  /**
42
56
  * Format an agent list as a human-readable table.
43
57
  * Shared by agent list and agent search commands.
@@ -75,15 +89,7 @@ export function formatAgentTable(agents, pagination, showArchived = false) {
75
89
  });
76
90
  });
77
91
  table.printTable();
78
- const showing = Math.min(pagination.limit, agents.length);
79
- if (pagination.has_more) {
80
- const nextOffset = pagination.offset + pagination.limit;
81
- console.log(`\nShowing ${showing} of ${pagination.total_count} agents. ` +
82
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
83
- }
84
- else if (pagination.total_count > showing) {
85
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} agents`));
86
- }
92
+ printPaginationFooter(pagination, agents.length, 'agents');
87
93
  }
88
94
  /**
89
95
  * Format an integration list as a human-readable table.
@@ -113,15 +119,7 @@ export function formatIntegrationTable(integrations, pagination) {
113
119
  });
114
120
  });
115
121
  table.printTable();
116
- const showing = Math.min(pagination.limit, integrations.length);
117
- if (pagination.has_more) {
118
- const nextOffset = pagination.offset + pagination.limit;
119
- console.log(`\nShowing ${showing} of ${pagination.total_count} integrations. ` +
120
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
121
- }
122
- else if (pagination.total_count > showing) {
123
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} integrations`));
124
- }
122
+ printPaginationFooter(pagination, integrations.length, 'integrations');
125
123
  }
126
124
  /**
127
125
  * Format an integration version list as a human-readable table.
@@ -169,15 +167,7 @@ export function formatIntegrationVersionTable(versions, pagination, integrationN
169
167
  });
170
168
  });
171
169
  table.printTable();
172
- const showing = Math.min(pagination.limit, versions.length);
173
- if (pagination.has_more) {
174
- const nextOffset = pagination.offset + pagination.limit;
175
- console.log(`\nShowing ${showing} of ${pagination.total_count} versions. ` +
176
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
177
- }
178
- else if (pagination.total_count > showing) {
179
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} versions`));
180
- }
170
+ printPaginationFooter(pagination, versions.length, 'versions');
181
171
  }
182
172
  /**
183
173
  * Format an agent version list as a human-readable table.
@@ -214,15 +204,7 @@ export function formatVersionTable(versions, pagination) {
214
204
  });
215
205
  });
216
206
  table.printTable();
217
- const showing = Math.min(pagination.limit, versions.length);
218
- if (pagination.has_more) {
219
- const nextOffset = pagination.offset + pagination.limit;
220
- console.log(`\nShowing ${showing} of ${pagination.total_count} versions. ` +
221
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
222
- }
223
- else if (pagination.total_count > showing) {
224
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} versions`));
225
- }
207
+ printPaginationFooter(pagination, versions.length, 'versions');
226
208
  }
227
209
  /**
228
210
  * Format a context list as a human-readable table.
@@ -250,15 +232,7 @@ export function formatContextTable(contexts, pagination) {
250
232
  });
251
233
  });
252
234
  table.printTable();
253
- const showing = Math.min(pagination.limit, contexts.length);
254
- if (pagination.has_more) {
255
- const nextOffset = pagination.offset + pagination.limit;
256
- console.log(`\nShowing ${showing} of ${pagination.total_count} contexts. ` +
257
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
258
- }
259
- else if (pagination.total_count > showing) {
260
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} contexts`));
261
- }
235
+ printPaginationFooter(pagination, contexts.length, 'contexts');
262
236
  }
263
237
  /**
264
238
  * Format a workspace agent list as a human-readable table.
@@ -314,15 +288,7 @@ export function formatWorkspaceTable(workspaces, pagination) {
314
288
  });
315
289
  });
316
290
  table.printTable();
317
- const showing = Math.min(pagination.limit, workspaces.length);
318
- if (pagination.has_more) {
319
- const nextOffset = pagination.offset + pagination.limit;
320
- console.log(`\nShowing ${showing} of ${pagination.total_count} workspaces. ` +
321
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
322
- }
323
- else if (pagination.total_count > showing) {
324
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} workspaces`));
325
- }
291
+ printPaginationFooter(pagination, workspaces.length, 'workspaces');
326
292
  }
327
293
  /**
328
294
  * Format a session list as a human-readable table.
@@ -353,15 +319,7 @@ export function formatSessionTable(sessions, pagination) {
353
319
  });
354
320
  });
355
321
  table.printTable();
356
- const showing = Math.min(pagination.limit, sessions.length);
357
- if (pagination.has_more) {
358
- const nextOffset = pagination.offset + pagination.limit;
359
- console.log(`\nShowing ${showing} of ${pagination.total_count} sessions. ` +
360
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
361
- }
362
- else if (pagination.total_count > showing) {
363
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} sessions`));
364
- }
322
+ printPaginationFooter(pagination, sessions.length, 'sessions');
365
323
  }
366
324
  /**
367
325
  * Format a task list as a human-readable table.
@@ -403,15 +361,7 @@ export function formatTaskTable(tasks, pagination) {
403
361
  });
404
362
  });
405
363
  table.printTable();
406
- const showing = Math.min(pagination.limit, tasks.length);
407
- if (pagination.has_more) {
408
- const nextOffset = pagination.offset + pagination.limit;
409
- console.log(`\nShowing ${showing} of ${pagination.total_count} tasks. ` +
410
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
411
- }
412
- else if (pagination.total_count > showing) {
413
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} tasks`));
414
- }
364
+ printPaginationFooter(pagination, tasks.length, 'tasks');
415
365
  }
416
366
  /**
417
367
  * Format a trigger list as a human-readable table.
@@ -453,15 +403,7 @@ export function formatTriggerTable(triggers, pagination) {
453
403
  });
454
404
  });
455
405
  table.printTable();
456
- const showing = Math.min(pagination.limit, triggers.length);
457
- if (pagination.has_more) {
458
- const nextOffset = pagination.offset + pagination.limit;
459
- console.log(`\nShowing ${showing} of ${pagination.total_count} triggers. ` +
460
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
461
- }
462
- else if (pagination.total_count > showing) {
463
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} triggers`));
464
- }
406
+ printPaginationFooter(pagination, triggers.length, 'triggers');
465
407
  }
466
408
  export function formatCredentialsTable(credentials, pagination) {
467
409
  if (credentials.length === 0) {
@@ -485,15 +427,7 @@ export function formatCredentialsTable(credentials, pagination) {
485
427
  });
486
428
  });
487
429
  table.printTable();
488
- const showing = Math.min(pagination.limit, credentials.length);
489
- if (pagination.has_more) {
490
- const nextOffset = pagination.offset + pagination.limit;
491
- console.log(`\nShowing ${showing} of ${pagination.total_count} credentials. ` +
492
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
493
- }
494
- else if (pagination.total_count > showing) {
495
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} credentials`));
496
- }
430
+ printPaginationFooter(pagination, credentials.length, 'credentials');
497
431
  }
498
432
  export function formatPoliciesTable(policies, pagination) {
499
433
  if (policies.length === 0) {
@@ -534,15 +468,7 @@ export function formatPoliciesTable(policies, pagination) {
534
468
  });
535
469
  });
536
470
  table.printTable();
537
- const showing = Math.min(pagination.limit, policies.length);
538
- if (pagination.has_more) {
539
- const nextOffset = pagination.offset + pagination.limit;
540
- console.log(`\nShowing ${showing} of ${pagination.total_count} policies. ` +
541
- chalk.dim(`Use --offset ${nextOffset} to see more.`));
542
- }
543
- else if (pagination.total_count > showing) {
544
- console.log(chalk.dim(`\nShowing ${showing} of ${pagination.total_count} policies`));
545
- }
471
+ printPaginationFooter(pagination, policies.length, 'policies');
546
472
  }
547
473
  /**
548
474
  * Format job steps as a human-readable table.
@@ -37,6 +37,13 @@ export interface PollOptions<T = unknown> {
37
37
  * @default "Operation timed out"
38
38
  */
39
39
  timeoutMessage?: string;
40
+ /**
41
+ * Optional callback invoked after each poll when the operation is not yet
42
+ * complete. Return a non-null string to override the default spinner text
43
+ * (`${message} (${attempts}/${maxAttempts})`). Return null to keep the
44
+ * default. Errors thrown by the callback are silently ignored.
45
+ */
46
+ onPoll?: (response: T, attempts: number) => Promise<string | null>;
40
47
  }
41
48
  export interface PollResult<T = unknown> {
42
49
  /**
@@ -21,7 +21,7 @@ import { createSpinner } from './progress.js';
21
21
  * ```
22
22
  */
23
23
  export async function pollUntilComplete(options) {
24
- const { endpoint, isComplete, message = 'Waiting for operation to complete...', maxAttempts = 60, delayMs = 1000, successMessage = 'Operation complete', timeoutMessage = 'Operation timed out', } = options;
24
+ const { endpoint, isComplete, message = 'Waiting for operation to complete...', maxAttempts = 60, delayMs = 1000, successMessage = 'Operation complete', timeoutMessage = 'Operation timed out', onPoll, } = options;
25
25
  const client = new GuildAPIClient();
26
26
  const spinner = createSpinner(message);
27
27
  spinner.start();
@@ -40,8 +40,17 @@ export async function pollUntilComplete(options) {
40
40
  attempts,
41
41
  };
42
42
  }
43
- // Update spinner with progress
44
- spinner.text = `${message} (${attempts}/${maxAttempts})`;
43
+ // Update spinner with progress — prefer onPoll text when available
44
+ let spinnerText = null;
45
+ if (onPoll) {
46
+ try {
47
+ spinnerText = await onPoll(response, attempts);
48
+ }
49
+ catch {
50
+ // Silently ignore step-fetch errors; fall back to default text
51
+ }
52
+ }
53
+ spinner.text = spinnerText ?? `${message} (${attempts}/${maxAttempts})`;
45
54
  }
46
55
  catch {
47
56
  // If we can't fetch the resource, continue polling
@@ -2,12 +2,41 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { GuildAPIClient } from './api-client.js';
4
4
  import { pollUntilComplete } from './polling.js';
5
+ /**
6
+ * Format a list of validation steps into a compact single-line status string
7
+ * suitable for spinner text, e.g. `✓ build ⟳ test ○ deploy`.
8
+ * Returns null when the steps array is empty so callers can fall back to the
9
+ * default attempt-counter text.
10
+ */
11
+ function formatStepProgress(steps) {
12
+ if (steps.length === 0)
13
+ return null;
14
+ const tokens = steps.map((step) => {
15
+ let prefix;
16
+ switch (step.status) {
17
+ case 'SUCCEEDED':
18
+ prefix = '✓';
19
+ break;
20
+ case 'RUNNING':
21
+ prefix = '⟳';
22
+ break;
23
+ case 'FAILED':
24
+ prefix = '✗';
25
+ break;
26
+ default:
27
+ prefix = '○';
28
+ }
29
+ return `${prefix} ${step.name}`;
30
+ });
31
+ return tokens.join(' ');
32
+ }
5
33
  /**
6
34
  * Poll until a version's validation completes, then check the result.
7
35
  * Exits the process on timeout or validation failure.
8
36
  * Returns the updated version on success.
9
37
  */
10
38
  export async function waitForValidation(versionId, output) {
39
+ const client = new GuildAPIClient();
11
40
  const pollResult = await pollUntilComplete({
12
41
  resourceId: versionId,
13
42
  endpoint: `/versions/${versionId}`,
@@ -18,6 +47,10 @@ export async function waitForValidation(versionId, output) {
18
47
  timeoutMessage: 'Validation timed out',
19
48
  maxAttempts: 120,
20
49
  delayMs: 1000,
50
+ onPoll: async () => {
51
+ const r = await client.get(`/versions/${versionId}/validation/steps`);
52
+ return formatStepProgress(r.steps);
53
+ },
21
54
  });
22
55
  if (!pollResult.success || !pollResult.response) {
23
56
  output.error('Validation did not complete in time', 'Check status manually:\n guild agent versions');
@@ -64,6 +97,7 @@ async function fetchValidationFailureDetails(versionId) {
64
97
  * Returns the updated version on success.
65
98
  */
66
99
  export async function waitForPublish(versionId, output) {
100
+ const client = new GuildAPIClient();
67
101
  const pollResult = await pollUntilComplete({
68
102
  resourceId: versionId,
69
103
  endpoint: `/versions/${versionId}`,
@@ -73,6 +107,10 @@ export async function waitForPublish(versionId, output) {
73
107
  timeoutMessage: 'Publish timed out',
74
108
  maxAttempts: 60,
75
109
  delayMs: 1000,
110
+ onPoll: async () => {
111
+ const r = await client.get(`/versions/${versionId}/publish/steps`);
112
+ return formatStepProgress(r.steps);
113
+ },
76
114
  });
77
115
  if (!pollResult.success || pollResult.response?.status !== 'PUBLISHED') {
78
116
  output.error('Publish did not complete in time', 'Check status manually:\n guild agent versions');
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: Guild CLI Workflow
2
+ name: guild-cli-workflow
3
3
  description: Agent development using the Guild CLI. Activated when user mentions guild agent commands, saving/publishing agents, clone/pull workflow, or agent testing. Covers CLI commands and common workflows.
4
4
  ---
5
5
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: Guild Agent Development
2
+ name: guild-agent-development
3
3
  description: Local agent development using the Guild CLI. Activated when user mentions creating agents, guild agent commands, saving/publishing agents, or agent development workflow. Handles proper CLI workflow and prevents direct git operations.
4
4
  ---
5
5
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: Guild Integrations
2
+ name: guild-integrations
3
3
  description: Build custom integrations that connect external APIs to Guild agents. Activated when user mentions custom integrations, OpenAPI specs, connecting APIs via CLI, guild integration commands, or building service tools for agents.
4
4
  ---
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guildai/cli",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Guild.ai CLI - Build, test, and deploy AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- export declare function createJobStepGetCommand(): Command;
3
- //# sourceMappingURL=step-get.d.ts.map