@guildai/cli 0.5.2 → 0.5.4

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
@@ -2,6 +2,11 @@
2
2
 
3
3
  Command-line interface for [Guild.ai](https://guild.ai) — build, test, and deploy AI agents.
4
4
 
5
+ ## Prerequisites
6
+
7
+ - **Node.js 18+** and **npm** — [nodejs.org](https://nodejs.org)
8
+ - **A Guild account** — Guild.ai is in closed beta. Request an invitation at hello@guild.ai
9
+
5
10
  ## Installation
6
11
 
7
12
  ```bash
@@ -16,68 +21,126 @@ npm install -g @guildai/cli
16
21
  guild auth login
17
22
  ```
18
23
 
19
- Visit the displayed URL and complete authentication. Your token is stored securely in your system keyring.
24
+ This opens your browser to complete sign-in at [app.guild.ai](https://app.guild.ai). It also configures your local npm registry so the CLI can install Guild packages.
25
+
26
+ Verify:
27
+
28
+ ```bash
29
+ guild auth status
30
+ ```
20
31
 
21
- ### 2. Create an Agent
32
+ ### 2. Select a workspace
22
33
 
23
34
  ```bash
24
- mkdir my-agent && cd my-agent
25
- guild agent init --name my-agent
35
+ guild workspace select
26
36
  ```
27
37
 
28
- ### 3. Develop and Save
38
+ If you have one workspace, it's selected automatically. If you have several, pick one from the list.
39
+
40
+ ### 3. Create an agent
29
41
 
30
42
  ```bash
31
- # Edit agent.ts, then commit and save
32
- git add . && git commit -m "Initial implementation"
33
- guild agent save
43
+ mkdir hello-agent && cd hello-agent
44
+ guild agent init --name hello-agent --template LLM
45
+ ```
34
46
 
35
- # Or stage+commit+push in one step
36
- guild agent save -A --message "Initial implementation"
47
+ This creates the agent in the Guild backend, initializes a local git repo, and pulls starter files:
48
+
49
+ ```
50
+ hello-agent/
51
+ ├── agent.ts # Your agent code
52
+ ├── package.json # Dependencies
53
+ ├── tsconfig.json # TypeScript config
54
+ ├── guild.json # Local config (managed by the CLI, don't edit)
55
+ └── .gitignore
37
56
  ```
38
57
 
39
- ### 4. Test Your Agent
58
+ ### 4. Edit the agent
59
+
60
+ Open `agent.ts` and replace the contents:
61
+
62
+ ```typescript
63
+ import { llmAgent, guildTools, userInterfaceTools } from '@guildai/agents-sdk';
64
+
65
+ export default llmAgent({
66
+ description: 'A friendly greeting agent',
67
+ tools: { ...guildTools, ...userInterfaceTools },
68
+ systemPrompt: `You are a friendly assistant. Greet users warmly and answer their questions.`,
69
+ mode: 'multi-turn',
70
+ });
71
+ ```
72
+
73
+ ### 5. Test it
40
74
 
41
75
  ```bash
42
- guild agent test
76
+ guild agent test --ephemeral
43
77
  ```
44
78
 
45
- Full getting-started guide: [docs.guild.ai/cli/getting-started](https://docs.guild.ai/cli/getting-started)
79
+ Type a message and press Enter. Press `Ctrl+C` to exit.
80
+
81
+ ### 6. Save and publish
82
+
83
+ ```bash
84
+ guild agent save --message "First version" --wait --publish
85
+ ```
86
+
87
+ - `--wait` blocks until validation passes
88
+ - `--publish` makes the agent available in the Guild catalog
89
+
90
+ ## Templates
91
+
92
+ | Template | Use when |
93
+ | -------------------- | -------------------------------------------------------------------- |
94
+ | `LLM` | The LLM is the logic. You write a prompt and pick tools. Start here. |
95
+ | `AUTO_MANAGED_STATE` | You write procedural TypeScript that calls tools inline. |
96
+ | `BLANK` | You want full control over the agent lifecycle. |
97
+
98
+ ## Development Loop
99
+
100
+ The typical workflow: pull → edit → test → save.
101
+
102
+ ```bash
103
+ guild agent pull # Sync remote changes
104
+ # ... edit agent.ts ...
105
+ guild agent test # Interactive test session
106
+ guild agent save --message "Add Slack notifications" # Save as draft
107
+ guild agent save --message "Ready" --wait --publish # Save + validate + publish
108
+ ```
46
109
 
47
110
  ## Commands
48
111
 
49
112
  ### Authentication
50
113
 
51
114
  ```bash
52
- guild auth login # OAuth device flow login
53
- guild auth logout # Remove stored token
54
- guild auth status # Check authentication status
115
+ guild auth login # OAuth device flow login
116
+ guild auth logout # Remove stored token
117
+ guild auth status # Check authentication status
118
+ guild auth token # Print auth token to stdout (for scripting)
55
119
  ```
56
120
 
57
121
  ### Agents
58
122
 
59
123
  ```bash
60
124
  guild agent init --name my-agent # Initialize a new agent
61
- guild agent clone <agent-id> # Clone agent locally
62
- guild agent save # Push commits and create a version
63
- guild agent save -A --message "..." # Stage+commit+push in one step
125
+ guild agent clone <identifier> # Clone an existing agent
64
126
  guild agent pull # Pull remote changes
65
- guild agent test # Test agent interactively
66
- guild agent test --ephemeral # Test unsaved changes
67
- guild agent chat "message" # Send a single message
68
- guild agent get [agent-id] # Get agent details
69
- guild agent versions [agent-id] # List versions
70
- guild agent code [agent-id] # View latest published source
71
- guild agent list # List all agents
72
- guild agent grep <pattern> # Search agent source code
127
+ guild agent save --message "..." # Save changes (creates draft version)
128
+ guild agent save --message "..." --wait --publish # Save + validate + publish
73
129
  guild agent publish # Publish latest draft version
74
- guild agent unpublish # Remove from catalog
75
- guild agent revalidate # Re-run validation
76
- guild agent update # Update agent metadata
130
+ guild agent unpublish # Unpublish latest published version
131
+ guild agent test # Interactive test session
132
+ guild agent test --ephemeral # Test uncommitted changes
133
+ guild agent chat "Hello" # Send a single message
134
+ guild agent get [identifier] # Get agent details
135
+ guild agent versions [identifier] # List version history
136
+ guild agent code [identifier] # Fetch latest published code
137
+ guild agent list # List all agents
138
+ guild agent search <query> # Search published agents
139
+ guild agent grep <pattern> # Search agent code files
140
+ guild agent fork <agent-id>:<version> # Fork an existing agent version
77
141
  guild agent owners # List accounts that can own agents
78
- guild agent tags list # List tags
79
- guild agent tags add <tag...> # Add tags
80
- guild agent tags remove <tag...> # Remove tags
142
+ guild agent tags list|add|remove # Manage agent tags
143
+ guild agent revalidate # Re-run validation
81
144
  ```
82
145
 
83
146
  ### Workspaces
@@ -97,6 +160,29 @@ guild workspace context edit # Edit context (opens editor)
97
160
  guild workspace context publish # Publish draft context
98
161
  ```
99
162
 
163
+ ### Sessions
164
+
165
+ ```bash
166
+ guild session list # List sessions in a workspace
167
+ guild session get <session-id> # Get session details
168
+ guild session events <session-id> # List events in a session
169
+ guild session tasks <session-id> # List tasks in a session
170
+ guild session create # Create a new session
171
+ guild session send <session-id> # Send a message to a session
172
+ ```
173
+
174
+ ### Triggers
175
+
176
+ ```bash
177
+ guild trigger list # List triggers in a workspace
178
+ guild trigger get <trigger-id> # Get trigger details
179
+ guild trigger create # Create a new trigger
180
+ guild trigger update <trigger-id> # Update a trigger
181
+ guild trigger activate <trigger-id> # Activate a trigger
182
+ guild trigger deactivate <trigger-id> # Deactivate a trigger
183
+ guild trigger sessions <trigger-id> # List sessions spawned by a trigger
184
+ ```
185
+
100
186
  ### Chat
101
187
 
102
188
  ```bash
@@ -105,6 +191,29 @@ guild chat "your message" # One-shot message
105
191
  guild chat --agent <agent-id> # Chat with specific agent
106
192
  ```
107
193
 
194
+ ### Configuration
195
+
196
+ ```bash
197
+ guild config list # Show all configuration values
198
+ guild config get <key> # Get a configuration value
199
+ guild config set <key> <value> # Set a global configuration value
200
+ guild config path # Show configuration file paths
201
+ ```
202
+
203
+ ### Diagnostics
204
+
205
+ ```bash
206
+ guild doctor # Check CLI setup and diagnose issues
207
+ guild version # Show version info
208
+ ```
209
+
210
+ ### Coding Assistant Skills
211
+
212
+ ```bash
213
+ guild setup # Install Guild skills for coding assistants
214
+ guild setup --claude-md # Also create a CLAUDE.md template
215
+ ```
216
+
108
217
  ## Configuration
109
218
 
110
219
  **Global config** (`~/.guild/config.json`):
@@ -134,28 +243,67 @@ OAuth tokens are stored in your system's secure credential store:
134
243
 
135
244
  ## Troubleshooting
136
245
 
137
- ### Authentication Failed
246
+ ### "Connection refused" or "Cannot connect to server"
247
+
248
+ 1. Check your internet connection
249
+ 2. Run `guild doctor` to see which check is failing
250
+
251
+ ### "Not authenticated"
138
252
 
139
253
  ```bash
140
- guild auth status # Check authentication status
141
- guild auth logout # Clear token
142
- guild auth login # Re-authenticate
254
+ guild auth login
255
+ guild auth status
143
256
  ```
144
257
 
145
- ### Keychain Issues
258
+ ### "Workspace not found" or wrong workspace
259
+
260
+ ```bash
261
+ guild workspace select
262
+ ```
263
+
264
+ Or override for a single command:
265
+
266
+ ```bash
267
+ GUILD_WORKSPACE_ID=<workspace-id> guild agent test
268
+ ```
269
+
270
+ ### "No agent ID provided and not in an agent directory"
271
+
272
+ Either run from inside an agent directory (one with a `guild.json` file), or pass the agent ID explicitly:
273
+
274
+ ```bash
275
+ guild agent get <agent-id>
276
+ ```
277
+
278
+ ### "No changes to commit"
279
+
280
+ All changes are already committed. Make a code change first, then run `guild agent save` again.
281
+
282
+ ### Validation failures
283
+
284
+ ```bash
285
+ guild agent versions --limit 1 # Check for errors
286
+ # Fix the issue, then:
287
+ guild agent save --message "Fix" --wait
288
+ ```
289
+
290
+ ### Agent test not responding
291
+
292
+ 1. Check your agent code compiles — look for TypeScript errors in `agent.ts`
293
+ 2. Make sure you've saved at least once: `guild agent save --message "initial"`
294
+ 3. Try a single message instead: `guild agent chat "hello"`
146
295
 
147
- If you see errors about keychain or credential storage:
296
+ ### Keychain issues
148
297
 
149
298
  - **macOS**: Ensure Keychain Access is working and not locked
150
299
  - **Linux**: Install and configure libsecret (e.g., `gnome-keyring`)
151
300
  - **Windows**: Check Credential Manager is accessible
152
301
 
153
- ### Command Not Found
302
+ ### Command not found
154
303
 
155
304
  Ensure npm global bin directory is in your PATH:
156
305
 
157
306
  ```bash
158
- npm config get prefix
159
307
  export PATH="$(npm config get prefix)/bin:$PATH"
160
308
  ```
161
309
 
@@ -166,4 +314,4 @@ export PATH="$(npm config get prefix)/bin:$PATH"
166
314
 
167
315
  ## License
168
316
 
169
- Proprietary - All rights reserved.
317
+ MIT
@@ -3,7 +3,7 @@ import { Command } from 'commander';
3
3
  import { render } from 'ink';
4
4
  import React from 'react';
5
5
  import open from 'open';
6
- import chalk from 'chalk';
6
+ import { hyperlink } from '../../lib/colors.js';
7
7
  import { getAgentId, readAgentFiles } from '../../lib/agent-helpers.js';
8
8
  import { ChatApp, createSession, ensureAuthenticated } from '../chat.js';
9
9
  import { readFileSync } from 'fs';
@@ -172,10 +172,10 @@ export function createAgentChatCommand() {
172
172
  const session = await createSession(client, options.workspace, initialPrompt, version.id);
173
173
  if (!quiet) {
174
174
  console.log(`✓ Agent: ${config?.name || agentId}`);
175
- console.log(`✓ Session created: ${session.id}`);
176
- if (session.session_url) {
177
- console.log(chalk.dim(` View in dashboard: ${session.session_url}`));
178
- }
175
+ const sessionLink = session.session_url
176
+ ? hyperlink(session.id, session.session_url)
177
+ : session.id;
178
+ console.log(`✓ Session: ${sessionLink}`);
179
179
  console.log('');
180
180
  }
181
181
  if (options.open && session.session_url) {
@@ -45,6 +45,7 @@ export function createAgentPullCommand() {
45
45
  const { stdout: branchName } = await runGit(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd });
46
46
  const currentBranch = branchName.trim();
47
47
  // Pull remote changes with rebase
48
+ let gitPulledNewCommits = false;
48
49
  try {
49
50
  const { stdout: pullOutput } = await runGit(['pull', '--rebase', authenticatedUrl, currentBranch], { cwd });
50
51
  // Update remote-tracking ref so git status stays consistent
@@ -57,23 +58,21 @@ export function createAgentPullCommand() {
57
58
  if (pullOutput.includes('Already up to date') ||
58
59
  pullOutput.includes('Current branch') // "Current branch X is up to date."
59
60
  ) {
60
- output.progress(' Already up to date');
61
- output.data({ success: true, message: 'Already up to date' });
61
+ // Don't report yet — version check below may have more to say
62
62
  }
63
63
  else {
64
+ gitPulledNewCommits = true;
64
65
  // Count commits pulled
65
66
  const updateMatch = pullOutput.match(/Updating\s+[a-f0-9]+\.\.[a-f0-9]+/);
66
67
  const fastForward = pullOutput.includes('Fast-forward');
67
68
  let message = 'Pulled remote changes';
68
69
  if (fastForward || updateMatch) {
69
- // Try to count files changed from pull output
70
70
  const filesMatch = pullOutput.match(/(\d+)\s+files?\s+changed/);
71
71
  if (filesMatch) {
72
72
  message = `Pulled remote changes (${filesMatch[1]} files changed)`;
73
73
  }
74
74
  }
75
75
  output.progress(`✓ ${message}`);
76
- output.data({ success: true, message });
77
76
  }
78
77
  }
79
78
  catch (pullError) {
@@ -99,6 +98,66 @@ export function createAgentPullCommand() {
99
98
  // Unknown git error
100
99
  throw pullError;
101
100
  }
101
+ // Version check: compare local state against backend
102
+ const versions = await client.get(`/agents/${guildConfig.agent_id}/versions?limit=1`);
103
+ if (versions.items.length > 0) {
104
+ const latest = versions.items[0];
105
+ const { stdout: headSha } = await runGit(['rev-parse', 'HEAD'], { cwd });
106
+ const localHead = headSha.trim();
107
+ if (latest.sha && latest.sha === localHead) {
108
+ // Truly up to date — git pull + version match
109
+ if (!gitPulledNewCommits) {
110
+ output.progress('✓ Already up to date');
111
+ output.data({ success: true, message: 'Already up to date' });
112
+ }
113
+ else {
114
+ output.data({ success: true, message: 'Pulled remote changes' });
115
+ }
116
+ }
117
+ else if (latest.sha && latest.sha !== localHead) {
118
+ // SHA mismatch — warn user
119
+ output.progress(`⚠ Remote has a newer version (${latest.sha.slice(0, 7)}) not on this branch`);
120
+ output.progress(' Try: git fetch origin && git log --oneline origin/main');
121
+ output.data({
122
+ success: true,
123
+ message: 'SHA mismatch with latest version',
124
+ latest_sha: latest.sha,
125
+ local_sha: localHead,
126
+ });
127
+ }
128
+ else {
129
+ // Ephemeral version — download from API
130
+ output.progress('Remote has unpublished changes from the web editor');
131
+ // Check for local uncommitted changes
132
+ const { stdout: status } = await runGit(['status', '--porcelain'], { cwd });
133
+ if (status.trim()) {
134
+ output.error('Cannot download remote changes — you have uncommitted local changes', 'Commit or stash your changes first, then run guild agent pull again.');
135
+ process.exit(1);
136
+ }
137
+ const files = await client.get(`/agents/${guildConfig.agent_id}/code?include_unpublished=1`);
138
+ for (const file of files) {
139
+ const filePath = path.join(cwd, file.path);
140
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
141
+ await fs.writeFile(filePath, file.content, 'utf-8');
142
+ }
143
+ output.progress(`✓ Downloaded ${files.length} files from latest draft version`);
144
+ output.data({
145
+ success: true,
146
+ message: `Downloaded ${files.length} files from draft version`,
147
+ files_updated: files.length,
148
+ });
149
+ }
150
+ }
151
+ else {
152
+ // No versions exist yet — just report git pull result
153
+ if (!gitPulledNewCommits) {
154
+ output.progress('✓ Already up to date');
155
+ output.data({ success: true, message: 'Already up to date' });
156
+ }
157
+ else {
158
+ output.data({ success: true, message: 'Pulled remote changes' });
159
+ }
160
+ }
102
161
  }
103
162
  catch (error) {
104
163
  if (error instanceof GitError) {
@@ -17,13 +17,29 @@ export function createAgentSaveCommand() {
17
17
  .option('--message <text>', 'Commit message (required with --all)')
18
18
  .option('--wait', 'Wait for validation to complete before returning', false)
19
19
  .option('--publish', 'Publish after validation passes (implies --wait)', false)
20
+ .option('--bump [level]', 'Bump package.json version before saving (patch, or minor/major)', 'patch')
21
+ .option('--no-bump', 'Skip automatic version bump')
20
22
  .option('--json', 'Output JSON only (no progress messages)', false)
21
23
  .action(async (options) => {
22
24
  const cwd = process.cwd();
23
25
  let guildConfig = null;
24
26
  const output = createOutputWriter();
25
- // With --all, a commit message is required
26
- if (options.all && !options.message) {
27
+ // Resolve bump level: bare --bump defaults to 'patch', --no-bump skips
28
+ const bumpLevel = options.bump === true || options.bump === 'patch'
29
+ ? 'patch'
30
+ : options.bump === 'minor'
31
+ ? 'minor'
32
+ : options.bump === 'major'
33
+ ? 'major'
34
+ : options.bump === false
35
+ ? null
36
+ : null;
37
+ if (options.bump !== false && bumpLevel === null) {
38
+ output.error(`Invalid bump level: ${String(options.bump)}`, 'Valid levels are: patch, minor, major\n guild agent save --bump minor');
39
+ process.exit(1);
40
+ }
41
+ // With --all, a commit message is required (unless bump auto-generates one)
42
+ if (options.all && !options.message && !bumpLevel) {
27
43
  output.error('Commit message is required with --all', 'Provide a message describing your changes:\n guild agent save -A --message "Add new feature"');
28
44
  process.exit(1);
29
45
  }
@@ -40,6 +56,39 @@ export function createAgentSaveCommand() {
40
56
  }
41
57
  // Read guild.json
42
58
  guildConfig = JSON.parse(await fs.readFile(guildJsonPath, 'utf-8'));
59
+ // Handle --bump: bump package.json version before git operations
60
+ if (bumpLevel) {
61
+ const packageJsonPath = path.join(cwd, 'package.json');
62
+ let packageJsonContent;
63
+ try {
64
+ packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
65
+ }
66
+ catch {
67
+ output.error('package.json not found', 'A package.json file is required in the agent directory to use --bump.');
68
+ process.exit(1);
69
+ }
70
+ const packageJson = JSON.parse(packageJsonContent);
71
+ const currentVersion = packageJson.version || '0.0.0';
72
+ const parts = currentVersion.split('.').map(Number);
73
+ if (parts.length !== 3 || parts.some((p) => isNaN(p))) {
74
+ output.error(`Cannot bump invalid version: ${currentVersion}`, 'package.json version must be in MAJOR.MINOR.PATCH format (e.g. 1.0.0)');
75
+ process.exit(1);
76
+ }
77
+ const [major, minor, patch] = parts;
78
+ let newVersion;
79
+ if (bumpLevel === 'major') {
80
+ newVersion = `${major + 1}.0.0`;
81
+ }
82
+ else if (bumpLevel === 'minor') {
83
+ newVersion = `${major}.${minor + 1}.0`;
84
+ }
85
+ else {
86
+ newVersion = `${major}.${minor}.${patch + 1}`;
87
+ }
88
+ packageJson.version = newVersion;
89
+ await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
90
+ output.progress(`✓ Bumped version: ${currentVersion} → ${newVersion}`);
91
+ }
43
92
  // Check for uncommitted changes and unpushed commits
44
93
  const { stdout: statusOutput } = await runGit(['status', '--porcelain'], {
45
94
  cwd,
@@ -65,12 +114,15 @@ export function createAgentSaveCommand() {
65
114
  hasUnpushedCommits = false;
66
115
  }
67
116
  }
68
- if (options.all) {
69
- // --all: stage and commit before pushing
117
+ // --bump implies --all (the version change needs to be committed)
118
+ const shouldStageAll = options.all || !!bumpLevel;
119
+ if (shouldStageAll) {
120
+ // Stage and commit before pushing
70
121
  if (hasUncommittedChanges) {
71
122
  await runGit(['add', '-A'], { cwd });
72
123
  output.progress('✓ Staged changes');
73
- await runGit(['commit', '-m', options.message], { cwd });
124
+ const commitMsg = options.message || (bumpLevel ? `Bump version` : '');
125
+ await runGit(['commit', '-m', commitMsg], { cwd });
74
126
  output.progress('✓ Committed locally');
75
127
  hasUnpushedCommits = true;
76
128
  }
@@ -6,7 +6,7 @@ import { readFileSync } from 'fs';
6
6
  import path from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import open from 'open';
9
- import chalk from 'chalk';
9
+ import { hyperlink } from '../../lib/colors.js';
10
10
  import { GuildAPIClient } from '../../lib/api-client.js';
11
11
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
12
12
  import { format } from '../../lib/progress.js';
@@ -284,10 +284,10 @@ export function createAgentTestCommand() {
284
284
  : commitSha?.substring(0, 12) || 'unknown';
285
285
  console.log(`✓ Version: ${versionDisplay}`);
286
286
  console.log(`✓ Workspace: ${workspaceId}`);
287
- console.log(`✓ Session created: ${session.id}`);
288
- if (session.session_url) {
289
- console.log(chalk.dim(` View in dashboard: ${session.session_url}`));
290
- }
287
+ const sessionLink = session.session_url
288
+ ? hyperlink(session.id, session.session_url)
289
+ : session.id;
290
+ console.log(`✓ Session: ${sessionLink}`);
291
291
  console.log('');
292
292
  }
293
293
  if (options.open && session.session_url) {
@@ -2,16 +2,33 @@
2
2
  import { Command } from 'commander';
3
3
  import { getAuthStatus } from '../../lib/auth.js';
4
4
  import { createOutputWriter } from '../../lib/output.js';
5
+ import { GuildAPIClient } from '../../lib/api-client.js';
6
+ import { GuildCLIError, ErrorCodes } from '../../lib/errors.js';
5
7
  export function createAuthStatusCommand() {
6
8
  const cmd = new Command('status');
7
9
  cmd.description('Check authentication status').action(async () => {
8
10
  const output = createOutputWriter();
9
11
  const { authenticated } = await getAuthStatus();
10
- if (authenticated) {
11
- output.success('Authenticated');
12
- }
13
- else {
12
+ if (!authenticated) {
14
13
  output.error('Not authenticated');
14
+ process.exit(0);
15
+ return;
16
+ }
17
+ // Try to fetch user profile for richer output
18
+ try {
19
+ const client = new GuildAPIClient();
20
+ const me = await client.get('/me');
21
+ output.success(`Authenticated as ${me.name} (${me.id})`);
22
+ }
23
+ catch (error) {
24
+ if (error instanceof GuildCLIError &&
25
+ error.code === ErrorCodes.AUTH_TOKEN_INVALID) {
26
+ output.error('Not authenticated (token expired)', 'Run: guild auth login');
27
+ }
28
+ else {
29
+ // Offline or other network error — fall back to simple message
30
+ output.success('Authenticated');
31
+ }
15
32
  }
16
33
  process.exit(0);
17
34
  });
@@ -4,7 +4,7 @@ import { Box, Text, Static, render, useInput, useApp } from 'ink';
4
4
  import { Command } from 'commander';
5
5
  import { getAuthToken, showBetaGuidance } from '../lib/auth.js';
6
6
  import { GuildAPIClient } from '../lib/api-client.js';
7
- import { handleAxiosError, ErrorCodes, debug, retry } from '../lib/errors.js';
7
+ import { handleAxiosError, ErrorCodes, debug, isDebugMode, retry, } from '../lib/errors.js';
8
8
  import { createSpinner, format } from '../lib/progress.js';
9
9
  import { marked } from 'marked';
10
10
  import { markedTerminal } from 'marked-terminal';
@@ -17,7 +17,7 @@ import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessa
17
17
  import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
18
18
  import { getWorkspaceId } from '../lib/guild-config.js';
19
19
  import { ensureInteractiveStdin } from '../lib/stdin.js';
20
- import { brand, BRAND_COLOR, code as codeColor } from '../lib/colors.js';
20
+ import { brand, BRAND_COLOR, code as codeColor, hyperlink } from '../lib/colors.js';
21
21
  import { SplashAnimation } from '../components/SplashAnimation.js';
22
22
  import { LOADING_TIMINGS } from '../lib/loading-messages.js';
23
23
  import { suppressScrollbackClear } from '../lib/alternate-screen.js';
@@ -222,10 +222,20 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
222
222
  const resumeDisplayMessages = resumeEvents
223
223
  ? eventsToDisplayMessages(resumeEvents)
224
224
  : null;
225
+ const sessionLinkMessage = isActive && preConnectedSession?.session_url
226
+ ? [
227
+ {
228
+ key: 'session-link',
229
+ content: chalk.dim(`Session: ${hyperlink(preConnectedSession.id, preConnectedSession.session_url)}`),
230
+ type: 'progress',
231
+ },
232
+ ]
233
+ : [];
225
234
  const [messages, setMessages] = useState(resumeDisplayMessages
226
235
  ? resumeDisplayMessages
227
236
  : isActive
228
237
  ? [
238
+ ...sessionLinkMessage,
229
239
  {
230
240
  key: 'initial',
231
241
  content: `${brand('>')} ${initialPrompt}`,
@@ -567,6 +577,18 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
567
577
  ]);
568
578
  setCurrentOperation('');
569
579
  }
580
+ else if (event.type === 'agent_console' && isDebugMode()) {
581
+ // Show console logs when debug mode is enabled (matches www debug checkbox behavior)
582
+ const content = typeof event.content === 'string' ? event.content : '';
583
+ setMessages((prev) => [
584
+ ...prev,
585
+ {
586
+ key: `console-${Date.now()}-${Math.random()}`,
587
+ content: chalk.dim(`[console.${event.level}] ${content}`),
588
+ type: 'assistant',
589
+ },
590
+ ]);
591
+ }
570
592
  else if (event.type === 'runtime_done' &&
571
593
  !receivedResponseSinceLastInput.current &&
572
594
  event.content !== undefined &&
@@ -908,6 +930,10 @@ export function createChatCommand() {
908
930
  const client = new GuildAPIClient();
909
931
  const session = await createSession(client, options.workspace, initialPrompt, options.agent);
910
932
  spinner.succeed('Connected');
933
+ if (session.session_url) {
934
+ const sessionLink = hyperlink(session.id, session.session_url);
935
+ console.error(chalk.dim(`Session: ${sessionLink}`));
936
+ }
911
937
  console.error('');
912
938
  // Non-interactive mode: send message, wait for response, output JSON
913
939
  // Poll for messages until we get an agent MESSAGE response