@guildai/cli 0.6.1 → 0.7.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.
package/README.md CHANGED
@@ -229,6 +229,8 @@ guild version # Show version info
229
229
  ```bash
230
230
  guild setup # Install skills + configure MCP server
231
231
  guild setup --claude-md # Also create a CLAUDE.md template
232
+ guild setup --codex # Install Codex skills + configure MCP server
233
+ guild setup --codex --agents-md # Also create an AGENTS.md template
232
234
  guild setup --no-mcp # Install skills only, skip MCP configuration
233
235
  guild mcp # Start MCP server (used by Claude Code, etc.)
234
236
  ```
@@ -243,7 +245,7 @@ The CLI includes an [MCP](https://modelcontextprotocol.io/) server that exposes
243
245
  guild setup
244
246
  ```
245
247
 
246
- This installs Claude Code skills and adds a `guild` entry to `.mcp.json` in your project. Claude Code (and other MCP-compatible tools) will automatically connect when they detect it.
248
+ This installs Claude Code skills and adds a `guild` entry to `.mcp.json` in your project. Use `guild setup --codex` to install Codex skills instead. Claude Code and other MCP-compatible tools will automatically connect when they detect `.mcp.json`.
247
249
 
248
250
  **What it provides:**
249
251
 
@@ -16,7 +16,6 @@ import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
16
16
  import { format } from '../../lib/progress.js';
17
17
  import { showBetaGuidance } from '../../lib/auth.js';
18
18
  import { pollUntilComplete } from '../../lib/polling.js';
19
- import { runGit } from '../../lib/git.js';
20
19
  import { suppressScrollbackClear } from '../../lib/alternate-screen.js';
21
20
  import * as readline from 'readline';
22
21
  import { pollForResponse } from '../../lib/session-polling.js';
@@ -34,7 +33,7 @@ export function createAgentChatCommand() {
34
33
  .option('--path <dir>', 'Path to agent directory (defaults to current directory)')
35
34
  .option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
36
35
  .option('--mode <format>', 'Input mode: json (one-shot) or jsonl (line-by-line)')
37
- .option('--ephemeral', 'Chat using unsaved changes (no guild agent save required)')
36
+ .option('--version <id>', 'Chat with a specific version (UUID or version number)')
38
37
  .option('--no-splash', 'Skip the splash screen animation')
39
38
  .option('--resume <session-id>', 'Resume an existing session')
40
39
  .option('--open', 'Open session in web dashboard')
@@ -67,10 +66,23 @@ export function createAgentChatCommand() {
67
66
  const quiet = isQuietMode();
68
67
  const resolvedPath = path.resolve(agentPath);
69
68
  // Resolve the version to chat with.
70
- // Default: use the latest saved version (requires clean working tree).
71
- // --ephemeral: read local files and create an ephemeral version.
69
+ // Default: create ephemeral version from working directory.
70
+ // --version: use a specific existing version.
72
71
  let version;
73
- if (options.ephemeral) {
72
+ if (options.version) {
73
+ // Explicit version: look it up by ID or version number
74
+ const existingVersions = (await client.get(`/agents/${agentId}/versions`));
75
+ const match = existingVersions.items.find((v) => v.id === options.version || v.version_number === options.version);
76
+ if (!match) {
77
+ console.error(`Error: Version not found: ${options.version}`);
78
+ console.error('');
79
+ console.error('List available versions:');
80
+ console.error(` guild agent versions ${agentId}`);
81
+ process.exit(1);
82
+ }
83
+ version = match;
84
+ }
85
+ else {
74
86
  if (!quiet) {
75
87
  console.error('Building agent...');
76
88
  }
@@ -81,43 +93,6 @@ export function createAgentChatCommand() {
81
93
  version_type: 'EPHEMERAL',
82
94
  }));
83
95
  }
84
- else {
85
- // Require clean working tree
86
- const { stdout: statusOutput } = await runGit(['status', '--porcelain'], {
87
- cwd: resolvedPath,
88
- });
89
- if (statusOutput.trim().length > 0) {
90
- console.error('Error: Working tree has unsaved changes');
91
- console.error('');
92
- console.error('Options:');
93
- console.error(' 1. Save your changes first:');
94
- console.error(' guild agent save --message "your changes"');
95
- console.error('');
96
- console.error(' 2. Chat with unsaved changes:');
97
- console.error(' guild agent chat --ephemeral');
98
- process.exit(1);
99
- }
100
- // Get commit SHA and look for an existing version
101
- const { stdout: shaOutput } = await runGit(['rev-parse', 'HEAD'], {
102
- cwd: resolvedPath,
103
- });
104
- const commitSha = shaOutput.trim();
105
- const existingVersions = (await client.get(`/agents/${agentId}/versions`));
106
- const existing = existingVersions.items.find((v) => v.sha === commitSha);
107
- if (existing) {
108
- version = existing;
109
- }
110
- else {
111
- if (!quiet) {
112
- console.error('Building agent...');
113
- }
114
- version = (await client.post(`/agents/${agentId}/versions`, {
115
- commit_sha: commitSha,
116
- summary: '[Chat] Development version',
117
- version_type: 'COMMITTED',
118
- }));
119
- }
120
- }
121
96
  // Wait for build to complete (skip if version already validated)
122
97
  if (version.validation_status === 'PENDING' ||
123
98
  version.validation_status === 'RUNNING') {
@@ -4,8 +4,8 @@ import { Command } from 'commander';
4
4
  import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getAgentId, resolveAgentRef } from '../../lib/agent-helpers.js';
6
6
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
7
- import { pollUntilComplete } from '../../lib/polling.js';
8
7
  import { createOutputWriter } from '../../lib/output.js';
8
+ import { waitForValidation, waitForPublish } from '../../lib/version-helpers.js';
9
9
  export function createAgentPublishCommand() {
10
10
  const cmd = new Command('publish');
11
11
  cmd
@@ -37,71 +37,31 @@ export function createAgentPublishCommand() {
37
37
  process.exit(1);
38
38
  }
39
39
  // Check validation status before attempting publish
40
- let validationStatus = versionToPublish.validation_status;
41
40
  let currentVersion = versionToPublish;
41
+ const validationStatus = currentVersion.validation_status;
42
42
  if (validationStatus === 'PENDING' || validationStatus === 'RUNNING') {
43
43
  if (options.wait) {
44
- // Wait for validation to complete
45
- const result = await pollUntilComplete({
46
- resourceId: versionToPublish.id,
47
- endpoint: `/versions/${versionToPublish.id}`,
48
- isComplete: (response) => response.validation_status !== 'PENDING' &&
49
- response.validation_status !== 'RUNNING',
50
- message: 'Waiting for validation to complete...',
51
- successMessage: 'Validation complete',
52
- timeoutMessage: 'Validation timed out',
53
- maxAttempts: 120, // 2 minutes
54
- delayMs: 1000,
55
- });
56
- if (!result.success || !result.response) {
57
- output.error('Validation did not complete in time', 'Check status manually:\n guild agent versions');
58
- process.exit(1);
59
- }
60
- currentVersion = result.response;
61
- validationStatus = currentVersion.validation_status;
62
- // Re-check validation status after waiting - will be handled below
63
- // by the main validation check
44
+ currentVersion = await waitForValidation(currentVersion.id, output);
64
45
  }
65
46
  else {
66
- output.error('Cannot publish: validation is still in progress', `Validation status: ${validationStatus === 'PENDING' ? 'Waiting to start' : 'Running'}\n\nThe version number is extracted from package.json during validation.\n\nOptions:\n • Wait for validation: guild agent publish --wait\n • Check status: guild agent versions`);
47
+ output.error('Cannot publish: validation is still in progress', `Validation status: ${validationStatus === 'PENDING' ? 'Waiting to start' : 'Running'}\n\nOptions:\n • Wait for validation: guild agent publish --wait\n • Check status: guild agent versions`);
67
48
  process.exit(1);
68
49
  }
69
50
  }
70
- if (validationStatus === 'FAILED') {
71
- // Fetch validation steps to show the actual error
72
- let failureDetails = '';
73
- try {
74
- const stepsResponse = await client.get(`/versions/${currentVersion.id}/validation/steps`);
75
- const failedSteps = stepsResponse.steps.filter((step) => step.status === 'FAILED');
76
- if (failedSteps.length > 0) {
77
- const stepMessages = failedSteps.map((step) => {
78
- let msg = `Step "${step.name}" failed:`;
79
- if (step.content) {
80
- msg += `\n ${step.content}`;
81
- }
82
- return msg;
83
- });
84
- failureDetails = stepMessages.join('\n\n');
85
- }
86
- else {
87
- failureDetails =
88
- 'No failed steps found. Check validation logs for details.';
89
- }
90
- }
91
- catch {
92
- // If we can't fetch steps, just show generic message
93
- failureDetails = 'Could not fetch validation details.';
94
- }
95
- output.error('Cannot publish: validation failed', `${failureDetails}\n\nFix the issues, then save a new version:\n guild agent save --message "Fix validation errors"`);
51
+ else if (validationStatus === 'FAILED') {
52
+ output.error('Cannot publish: validation failed', 'Fix the issues, then save a new version:\n guild agent save --message "Fix validation errors"');
96
53
  process.exit(1);
97
54
  }
98
- // Note: version_number is only set after validation extracts it from package.json,
99
- // and only enforced at publish time (drafts don't require versions)
100
55
  // Publish version
101
- await client.post(`/versions/${currentVersion.id}/publish`, {});
56
+ currentVersion = await client.post(`/versions/${currentVersion.id}/publish`, {});
57
+ if (options.wait && currentVersion.status !== 'PUBLISHED') {
58
+ currentVersion = await waitForPublish(currentVersion.id, output);
59
+ }
102
60
  const details = {
103
61
  agent: `${currentVersion.agent?.name || config?.name || agentId}${config ? '' : ` (${agentId})`}`,
104
- status: 'DRAFT PUBLISHED',
62
+ status: currentVersion.status === 'PUBLISHED'
63
+ ? 'DRAFT → PUBLISHED'
64
+ : 'DRAFT → PUBLISHING',
105
65
  };
106
66
  if (currentVersion.version_number) {
107
67
  details.version = currentVersion.version_number;
@@ -9,7 +9,7 @@ import * as fs from 'fs/promises';
9
9
  import * as path from 'path';
10
10
  import { getAuthenticatedUrl } from '../../lib/auth.js';
11
11
  import { runGit, GitError, formatGitError } from '../../lib/git.js';
12
- import { pollUntilComplete } from '../../lib/polling.js';
12
+ import { waitForValidation, waitForPublish } from '../../lib/version-helpers.js';
13
13
  export function createAgentSaveCommand() {
14
14
  const cmd = new Command('save');
15
15
  cmd
@@ -255,58 +255,18 @@ export function createAgentSaveCommand() {
255
255
  version_type: 'COMMITTED',
256
256
  });
257
257
  output.progress(`✓ Created version (${version.id})`);
258
- // --publish implies --wait
259
- const shouldWait = options.wait || options.publish;
260
- // If --wait or --publish, wait for validation to complete
261
- if (shouldWait) {
262
- const pollResult = await pollUntilComplete({
263
- resourceId: version.id,
264
- endpoint: `/versions/${version.id}`,
265
- isComplete: (response) => response.validation_status !== 'PENDING' &&
266
- response.validation_status !== 'RUNNING',
267
- message: 'Waiting for validation to complete...',
268
- successMessage: 'Validation complete',
269
- timeoutMessage: 'Validation timed out',
270
- maxAttempts: 120, // 2 minutes
271
- delayMs: 1000,
272
- });
273
- if (!pollResult.success || !pollResult.response) {
274
- output.error('Validation did not complete in time', 'Check status manually:\n guild agent versions');
275
- process.exit(1);
276
- }
277
- version = pollResult.response;
278
- // Check validation status
279
- if (version.validation_status === 'FAILED') {
280
- let failureDetails = '';
281
- // Fetch validation steps to show the actual error
282
- try {
283
- const stepsResponse = await client.get(`/versions/${version.id}/validation/steps`);
284
- const failedSteps = stepsResponse.steps.filter((step) => step.status === 'FAILED');
285
- if (failedSteps.length > 0) {
286
- const stepMessages = failedSteps.map((step) => step.content
287
- ? `Step "${step.name}" failed: ${step.content}`
288
- : `Step "${step.name}" failed`);
289
- failureDetails = stepMessages.join('\n');
290
- }
291
- else {
292
- failureDetails =
293
- 'No failed steps found. Check validation logs for details.';
294
- }
295
- }
296
- catch {
297
- failureDetails = 'Could not fetch validation details.';
298
- }
299
- failureDetails +=
300
- '\n\nFix the issues, commit, and save a new version:\n git add . && git commit -m "Fix validation errors"\n guild agent save';
301
- output.error('Validation failed', failureDetails);
302
- process.exit(1);
303
- }
258
+ if (options.wait) {
259
+ version = await waitForValidation(version.id, output);
304
260
  }
305
- // If --publish and validation passed, publish the version
306
261
  if (options.publish) {
307
262
  output.progress('Validation passed, publishing...');
308
263
  version = await client.post(`/versions/${version.id}/publish`, {});
309
- output.progress('✓ Published');
264
+ if (options.wait && version.status !== 'PUBLISHED') {
265
+ version = await waitForPublish(version.id, output);
266
+ }
267
+ else {
268
+ output.progress('✓ Published');
269
+ }
310
270
  }
311
271
  output.progress('');
312
272
  output.progress('Version details:');
@@ -317,10 +277,14 @@ export function createAgentSaveCommand() {
317
277
  output.progress(` Published at: ${version.published_at}`);
318
278
  }
319
279
  output.progress(` Summary: ${version.summary}`);
320
- if (options.publish) {
280
+ if (options.publish && version.status === 'PUBLISHED') {
321
281
  output.progress('');
322
282
  output.progress('Version is now published and available in the catalog.');
323
283
  }
284
+ else if (options.publish) {
285
+ output.progress('');
286
+ output.progress('Publish is in progress. Use --wait to wait for completion.');
287
+ }
324
288
  else {
325
289
  output.progress('');
326
290
  output.progress('To publish this version:');
@@ -17,7 +17,7 @@ import { isQuietMode, getOutputMode } from '../../lib/output-mode.js';
17
17
  import { pollForResponse } from '../../lib/session-polling.js';
18
18
  import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
19
19
  import { loadLocalConfig, getWorkspaceId } from '../../lib/guild-config.js';
20
- import { runGit, GitError, formatGitError } from '../../lib/git.js';
20
+ import { GitError, formatGitError } from '../../lib/git.js';
21
21
  import { pollUntilComplete } from '../../lib/polling.js';
22
22
  import { readAgentFiles } from '../../lib/agent-helpers.js';
23
23
  import { fetchSession, fetchSessionEvents } from '../../lib/session-resume.js';
@@ -33,7 +33,7 @@ export function createAgentTestCommand() {
33
33
  .description('Test agent in interactive REPL session')
34
34
  .option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
35
35
  .option('--mode <format>', 'Input mode: json (one-shot) or jsonl (line-by-line)')
36
- .option('--ephemeral', 'Test unsaved changes via ephemeral version (no guild agent save required)')
36
+ .option('--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
39
  .action(async (options) => {
@@ -75,12 +75,10 @@ export function createAgentTestCommand() {
75
75
  console.error(' guild agent clone <agent-id>');
76
76
  process.exit(1);
77
77
  }
78
- // For ephemeral mode, we skip git checks and read files directly
79
- // For committed mode, we require a clean working tree
80
- let commitSha = null;
78
+ // Read agent files from disk for ephemeral version creation.
79
+ // Skipped when --version is provided (testing a specific existing version).
81
80
  let agentFiles = null;
82
- if (options.ephemeral) {
83
- // Ephemeral mode: read files from disk
81
+ if (!options.version) {
84
82
  try {
85
83
  agentFiles = await readAgentFiles(cwd);
86
84
  }
@@ -96,28 +94,6 @@ export function createAgentTestCommand() {
96
94
  process.exit(1);
97
95
  }
98
96
  }
99
- else {
100
- // Committed mode: require clean working tree
101
- const { stdout: statusOutput } = await runGit(['status', '--porcelain'], {
102
- cwd,
103
- });
104
- if (statusOutput.trim().length > 0) {
105
- console.error('Error: Working tree has unsaved changes');
106
- console.error('');
107
- console.error('Options:');
108
- console.error(' 1. Save your changes first:');
109
- console.error(' guild agent save --message "your changes"');
110
- console.error('');
111
- console.error(' 2. Test unsaved changes:');
112
- console.error(' guild agent test --ephemeral');
113
- process.exit(1);
114
- }
115
- // Get commit SHA
116
- const { stdout: shaOutput } = await runGit(['rev-parse', 'HEAD'], {
117
- cwd,
118
- });
119
- commitSha = shaOutput.trim();
120
- }
121
97
  // If using JSON input, read and validate it early (before auth/session creation)
122
98
  // This provides better UX - fail fast on bad JSON without needing auth
123
99
  let inputData;
@@ -156,33 +132,31 @@ export function createAgentTestCommand() {
156
132
  }
157
133
  // Validate auth before any API calls or UI rendering
158
134
  await ensureAuthenticated();
159
- // Create version for testing
135
+ // Resolve version for testing
160
136
  const client = new GuildAPIClient();
161
137
  let version;
162
138
  try {
163
- if (options.ephemeral && agentFiles) {
164
- // Ephemeral version: send file contents directly
139
+ if (options.version) {
140
+ // Explicit version: look it up by ID or version number
141
+ const existingVersions = (await client.get(`/agents/${guildConfig.agent_id}/versions`));
142
+ const match = existingVersions.items.find((v) => v.id === options.version || v.version_number === options.version);
143
+ if (!match) {
144
+ console.error(`Error: Version not found: ${options.version}`);
145
+ console.error('');
146
+ console.error('List available versions:');
147
+ console.error(` guild agent versions ${guildConfig.agent_id}`);
148
+ process.exit(1);
149
+ }
150
+ version = match;
151
+ }
152
+ else {
153
+ // Default: create ephemeral version from working directory
165
154
  version = (await client.post(`/agents/${guildConfig.agent_id}/versions`, {
166
155
  files: agentFiles,
167
156
  summary: '[Test] Ephemeral development version',
168
157
  version_type: 'EPHEMERAL',
169
158
  }));
170
159
  }
171
- else {
172
- // Committed mode: check if a version for this commit already exists
173
- const existingVersions = (await client.get(`/agents/${guildConfig.agent_id}/versions`));
174
- const existing = existingVersions.items.find((v) => v.sha === commitSha);
175
- if (existing) {
176
- version = existing;
177
- }
178
- else {
179
- version = (await client.post(`/agents/${guildConfig.agent_id}/versions`, {
180
- commit_sha: commitSha,
181
- summary: '[Test] Development version',
182
- version_type: 'COMMITTED',
183
- }));
184
- }
185
- }
186
160
  }
187
161
  catch (error) {
188
162
  const formattedError = handleAxiosError(error);
@@ -280,9 +254,9 @@ export function createAgentTestCommand() {
280
254
  const quiet = isQuietMode();
281
255
  if (!quiet) {
282
256
  console.log(`✓ Agent: ${guildConfig.name} (${guildConfig.agent_id})`);
283
- const versionDisplay = options.ephemeral
284
- ? 'ephemeral (unsaved changes)'
285
- : commitSha?.substring(0, 12) || 'unknown';
257
+ const versionDisplay = options.version
258
+ ? options.version
259
+ : 'ephemeral (working directory)';
286
260
  console.log(`✓ Version: ${versionDisplay}`);
287
261
  console.log(`✓ Workspace: ${workspaceId}`);
288
262
  const sessionLink = session.session_url
@@ -452,7 +426,6 @@ export function createAgentTestCommand() {
452
426
  console.error('Failed to start test session. Troubleshooting:');
453
427
  console.error(' - Verify the agent exists: guild agent get');
454
428
  console.error(' - Check your workspace: guild workspace list');
455
- console.error(' - Test unsaved changes: guild agent test --ephemeral');
456
429
  if (formattedError.code) {
457
430
  console.error(` - Error code: ${formattedError.code}`);
458
431
  }
@@ -17,8 +17,9 @@ export interface ChatAppProps {
17
17
  resumeEvents?: SessionEvent[];
18
18
  resumeCommand?: string;
19
19
  openDashboard?: boolean;
20
+ eventFilter?: Set<string>;
20
21
  }
21
- export declare function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash, resumeSession, resumeEvents, resumeCommand, openDashboard, }: ChatAppProps): React.JSX.Element;
22
+ export declare function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash, resumeSession, resumeEvents, resumeCommand, openDashboard, eventFilter, }: ChatAppProps): React.JSX.Element;
22
23
  export declare function ensureAuthenticated(): Promise<string>;
23
24
  export declare function createSession(client: GuildAPIClient, workspaceId: string | undefined, initialPrompt: string, versionId?: string, onProgress?: (status: string) => void): Promise<Session>;
24
25
  export declare function createChatCommand(): Command;