@guildai/cli 0.3.16 → 0.3.17

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 (58) hide show
  1. package/dist/commands/agent/clone.js +21 -40
  2. package/dist/commands/agent/code.js +12 -32
  3. package/dist/commands/agent/create.js +14 -30
  4. package/dist/commands/agent/fork.js +27 -73
  5. package/dist/commands/agent/get.js +5 -4
  6. package/dist/commands/agent/grep.js +7 -9
  7. package/dist/commands/agent/init.js +2 -0
  8. package/dist/commands/agent/list.js +5 -12
  9. package/dist/commands/agent/publish.js +27 -44
  10. package/dist/commands/agent/pull.js +8 -35
  11. package/dist/commands/agent/revalidate.js +8 -16
  12. package/dist/commands/agent/save.js +23 -74
  13. package/dist/commands/agent/search.js +5 -12
  14. package/dist/commands/agent/tags/add.js +14 -24
  15. package/dist/commands/agent/tags/list.js +12 -23
  16. package/dist/commands/agent/tags/remove.js +16 -27
  17. package/dist/commands/agent/tags/set.js +14 -19
  18. package/dist/commands/agent/unpublish.js +12 -17
  19. package/dist/commands/agent/update.js +12 -29
  20. package/dist/commands/agent/versions.js +7 -11
  21. package/dist/commands/auth/login.js +4 -2
  22. package/dist/commands/auth/logout.js +3 -1
  23. package/dist/commands/auth/status.js +4 -3
  24. package/dist/commands/auth/token.js +3 -2
  25. package/dist/commands/config/get.js +7 -9
  26. package/dist/commands/config/list.js +13 -11
  27. package/dist/commands/config/path.js +6 -4
  28. package/dist/commands/config/set.js +17 -22
  29. package/dist/commands/doctor.js +9 -7
  30. package/dist/commands/session/create.js +7 -5
  31. package/dist/commands/session/events.js +5 -3
  32. package/dist/commands/session/get.js +5 -3
  33. package/dist/commands/session/list.js +5 -4
  34. package/dist/commands/session/send.js +7 -5
  35. package/dist/commands/session/tasks.js +5 -3
  36. package/dist/commands/setup.js +15 -14
  37. package/dist/commands/trigger/activate.js +7 -6
  38. package/dist/commands/trigger/create.js +16 -15
  39. package/dist/commands/trigger/deactivate.js +7 -6
  40. package/dist/commands/trigger/get.js +5 -4
  41. package/dist/commands/trigger/list.js +5 -5
  42. package/dist/commands/trigger/sessions.js +7 -6
  43. package/dist/commands/trigger/update.js +11 -10
  44. package/dist/commands/version.js +7 -5
  45. package/dist/commands/workspace/agent/add.js +16 -22
  46. package/dist/commands/workspace/agent/list.js +9 -30
  47. package/dist/commands/workspace/agent/remove.js +9 -15
  48. package/dist/commands/workspace/context/edit.js +13 -27
  49. package/dist/commands/workspace/context/get.js +8 -14
  50. package/dist/commands/workspace/context/list.js +7 -38
  51. package/dist/commands/workspace/context/publish.js +7 -11
  52. package/dist/commands/workspace/create.js +7 -11
  53. package/dist/commands/workspace/current.js +19 -31
  54. package/dist/commands/workspace/get.js +7 -11
  55. package/dist/commands/workspace/list.js +5 -8
  56. package/dist/commands/workspace/select.js +17 -22
  57. package/dist/lib/output.js +4 -4
  58. package/package.json +1 -1
@@ -7,6 +7,7 @@ import * as path from 'path';
7
7
  import * as readline from 'readline';
8
8
  import { getAuthenticatedUrl } from '../../lib/auth.js';
9
9
  import { runGit, GitError, formatGitError } from '../../lib/git.js';
10
+ import { createOutputWriter } from '../../lib/output.js';
10
11
  async function promptForDirectory(suggestedDir) {
11
12
  const rl = readline.createInterface({
12
13
  input: process.stdin,
@@ -40,17 +41,16 @@ export function createAgentCloneCommand() {
40
41
  .option('--directory <path>', 'Target directory for clone')
41
42
  .option('--force', 'Clone even if directory is not empty', false)
42
43
  .action(async (agentId, options) => {
44
+ const output = createOutputWriter();
43
45
  try {
44
46
  // Fetch agent details
45
47
  const client = new GuildAPIClient();
46
48
  const agent = await client.get(`/agents/${agentId}`);
47
49
  if (!agent.git_url) {
48
- console.error('Error: Agent does not have a git repository');
49
- console.error('');
50
- console.error('This agent may not have been initialized with git.');
50
+ output.error('Error: Agent does not have a git repository', 'This agent may not have been initialized with git.');
51
51
  process.exit(1);
52
52
  }
53
- console.log(`✓ Fetched agent '${agent.name}' (${agent.id})`);
53
+ output.progress(`✓ Fetched agent '${agent.name}' (${agent.id})`);
54
54
  // Determine target directory
55
55
  let targetDir = options.directory;
56
56
  if (!targetDir) {
@@ -59,12 +59,7 @@ export function createAgentCloneCommand() {
59
59
  targetDir = await promptForDirectory(suggestedDir);
60
60
  }
61
61
  else {
62
- console.error('Error: Target directory required in non-interactive mode');
63
- console.error('');
64
- console.error('Provide a directory:');
65
- console.error(` guild agent clone ${agentId} --directory ./my-agent`);
66
- console.error('');
67
- console.error('Or run interactively to be prompted.');
62
+ output.error('Error: Target directory required in non-interactive mode', `Provide a directory:\n guild agent clone ${agentId} --directory ./my-agent\n\nOr run interactively to be prompted.`);
68
63
  process.exit(1);
69
64
  }
70
65
  }
@@ -76,25 +71,18 @@ export function createAgentCloneCommand() {
76
71
  if (dirExists) {
77
72
  const isEmpty = await isDirectoryEmpty(targetDir);
78
73
  if (!isEmpty && !options.force) {
79
- console.error(`Error: Directory '${targetDir}' is not empty`);
80
- console.error('');
81
- console.error(`To clone anyway, use: guild agent clone ${agentId} --force`);
82
- console.error('');
83
- console.error('Or choose a different directory:');
84
- console.error(` guild agent clone ${agentId} --directory ./path/to/dir`);
74
+ output.error(`Error: Directory '${targetDir}' is not empty`, `To clone anyway, use: guild agent clone ${agentId} --force\n\nOr choose a different directory:\n guild agent clone ${agentId} --directory ./path/to/dir`);
85
75
  process.exit(1);
86
76
  }
87
77
  }
88
78
  // Clone repository
89
79
  const cloneUrl = await getAuthenticatedUrl(agent.git_url);
90
80
  if (!cloneUrl) {
91
- console.error('Error: Not authenticated');
92
- console.error('');
93
- console.error('Run: guild auth login');
81
+ output.error('Error: Not authenticated', 'Run: guild auth login');
94
82
  process.exit(1);
95
83
  }
96
84
  await runGit(['clone', cloneUrl, targetDir]);
97
- console.log(`✓ Cloned repository to ${targetDir}`);
85
+ output.progress(`✓ Cloned repository to ${targetDir}`);
98
86
  // Create guild.json if it doesn't exist
99
87
  const guildJsonPath = path.join(targetDir, 'guild.json');
100
88
  const guildJsonExists = await fs
@@ -102,7 +90,7 @@ export function createAgentCloneCommand() {
102
90
  .then(() => true)
103
91
  .catch(() => false);
104
92
  if (guildJsonExists) {
105
- console.log('✓ Verified guild.json');
93
+ output.progress('✓ Verified guild.json');
106
94
  }
107
95
  else {
108
96
  // Create guild.json for local development
@@ -111,42 +99,35 @@ export function createAgentCloneCommand() {
111
99
  name: agent.name,
112
100
  };
113
101
  await fs.writeFile(guildJsonPath, JSON.stringify(guildConfig, null, 2) + '\n');
114
- console.log('✓ Created guild.json');
102
+ output.progress('✓ Created guild.json');
115
103
  }
116
104
  // Display next steps
117
- console.log('');
118
- console.log('Next steps:');
119
- console.log(` 1. cd ${targetDir}`);
120
- console.log(' 2. Make your changes to the code');
121
- console.log(` 3. Run 'guild agent save --message "your changes"'`);
122
- console.log(` 4. Run 'guild agent test' to test your changes`);
105
+ output.progress('');
106
+ output.progress('Next steps:');
107
+ output.progress(` 1. cd ${targetDir}`);
108
+ output.progress(' 2. Make your changes to the code');
109
+ output.progress(` 3. Run 'guild agent save --message "your changes"'`);
110
+ output.progress(` 4. Run 'guild agent test' to test your changes`);
123
111
  }
124
112
  catch (error) {
125
113
  if (error instanceof GitError) {
126
- console.error('Error: Failed to clone repository');
127
- console.error('');
128
- console.error(formatGitError(error));
114
+ output.error('Error: Failed to clone repository', formatGitError(error));
129
115
  process.exit(1);
130
116
  }
131
117
  const formattedError = handleAxiosError(error);
132
118
  if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
133
- console.error('Not authenticated. Please log in first.');
134
- console.error('');
135
- console.error('Run: guild auth login');
119
+ output.error('Not authenticated. Please log in first.', 'Run: guild auth login');
136
120
  process.exit(1);
137
121
  }
138
122
  if (formattedError.code === ErrorCodes.NOT_FOUND) {
139
- console.error(`Error: Agent not found: ${agentId}`);
140
- console.error('');
141
- console.error('Check the agent ID:');
142
- console.error(' guild agent list');
123
+ output.error(`Error: Agent not found: ${agentId}`, 'Check the agent ID:\n guild agent list');
143
124
  process.exit(1);
144
125
  }
145
126
  if (formattedError.code === ErrorCodes.CONN_REFUSED) {
146
- console.error('Cannot connect to Guild servers');
127
+ output.error('Cannot connect to Guild servers');
147
128
  process.exit(1);
148
129
  }
149
- console.error(`Failed to clone agent: ${formattedError.details}`);
130
+ output.error(`Failed to clone agent: ${formattedError.details}`);
150
131
  process.exit(1);
151
132
  }
152
133
  });
@@ -3,6 +3,7 @@ import { Command } from 'commander';
3
3
  import { GuildAPIClient } from '../../lib/api-client.js';
4
4
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
5
5
  import { getAgentId } from '../../lib/agent-helpers.js';
6
+ import { createOutputWriter } from '../../lib/output.js';
6
7
  import * as fs from 'fs/promises';
7
8
  import * as path from 'path';
8
9
  export function createAgentCodeCommand() {
@@ -13,6 +14,7 @@ export function createAgentCodeCommand() {
13
14
  .option('--draft', 'Include draft versions (default: only published)', false)
14
15
  .option('--output <directory>', 'Write files to directory instead of printing JSON')
15
16
  .action(async (agentIdArg, options) => {
17
+ const output = createOutputWriter();
16
18
  // Get agent ID from argument or guild.json
17
19
  const { agentId } = await getAgentId(agentIdArg);
18
20
  const client = new GuildAPIClient();
@@ -24,38 +26,25 @@ export function createAgentCodeCommand() {
24
26
  catch (error) {
25
27
  const formattedError = handleAxiosError(error);
26
28
  if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
27
- console.error('Not logged in.');
28
- console.error('');
29
- console.error('Please authenticate first:');
30
- console.error(' guild auth login');
29
+ output.error('Not logged in.', 'Please authenticate first:\n guild auth login');
31
30
  }
32
31
  else if (formattedError.code === ErrorCodes.CONN_REFUSED) {
33
- console.error('Cannot connect to Guild servers');
32
+ output.error('Cannot connect to Guild servers');
34
33
  }
35
34
  else if (formattedError.code === ErrorCodes.NOT_FOUND) {
36
- console.error(`Agent not found: ${agentId}`);
37
- console.error('');
38
- console.error('Check the agent ID:');
39
- console.error(' guild agent list');
35
+ output.error(`Agent not found: ${agentId}`, 'Check the agent ID:\n guild agent list');
40
36
  }
41
37
  else {
42
- console.error(`Failed to fetch agent code: ${formattedError.details}`);
38
+ output.error(`Failed to fetch agent code: ${formattedError.details}`);
43
39
  }
44
40
  process.exit(1);
45
41
  }
46
42
  if (files.length === 0) {
47
- console.error('No code found for this agent.');
48
- console.error('');
49
43
  if (options.draft) {
50
- console.error('This agent has no versions saved yet.');
51
- console.error('');
52
- console.error('To save code: cd <agent-directory> && guild agent save --message "..."');
44
+ output.error('No code found for this agent.', 'This agent has no versions saved yet.\n\nTo save code: cd <agent-directory> && guild agent save --message "..."');
53
45
  }
54
46
  else {
55
- console.error('This agent has no published versions.');
56
- console.error('');
57
- console.error('To see draft versions: guild agent code <agent-id> --draft');
58
- console.error('To publish a version: cd <agent-directory> && guild agent save --message "..." --publish');
47
+ output.error('No code found for this agent.', 'This agent has no published versions.\n\nTo see draft versions: guild agent code <agent-id> --draft\nTo publish a version: cd <agent-directory> && guild agent save --message "..." --publish');
59
48
  }
60
49
  process.exit(1);
61
50
  }
@@ -71,32 +60,23 @@ export function createAgentCodeCommand() {
71
60
  // Write file
72
61
  await fs.writeFile(filePath, file.content, 'utf-8');
73
62
  }
74
- console.log(JSON.stringify({
63
+ output.data({
75
64
  success: 'Code written to directory',
76
65
  directory: options.output,
77
66
  files_written: files.length,
78
67
  files: files.map((f) => f.path),
79
- }, null, 2));
68
+ });
80
69
  process.exit(0);
81
70
  }
82
71
  catch (error) {
83
72
  const err = error;
84
- console.error(`Could not write files to directory: ${options.output}`);
85
- console.error('');
86
- console.error(`Error: ${err.message || 'Unknown error'}`);
87
- console.error('');
88
- console.error('Check that:');
89
- console.error(' • Directory is writable');
90
- console.error(' • You have permission to create files');
91
- console.error(' • Disk space is available');
73
+ output.error(`Could not write files to directory: ${options.output}`, `Error: ${err.message || 'Unknown error'}\n\nCheck that:\n • Directory is writable\n • You have permission to create files\n • Disk space is available`);
92
74
  process.exit(1);
93
75
  }
94
76
  }
95
77
  else {
96
78
  // Just print JSON
97
- console.log(JSON.stringify({
98
- files,
99
- }, null, 2));
79
+ output.data({ files });
100
80
  process.exit(0);
101
81
  }
102
82
  });
@@ -5,6 +5,7 @@ import { GuildAPIClient } from '../../lib/api-client.js';
5
5
  import { getGuildcoreUrl } from '../../lib/config.js';
6
6
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
7
7
  import { pollAgentStatus } from '../../lib/polling.js';
8
+ import { createOutputWriter } from '../../lib/output.js';
8
9
  const TEMPLATE_CHOICES = [
9
10
  {
10
11
  name: 'LLM - Simple language model agent (recommended)',
@@ -47,6 +48,7 @@ export function createAgentCreateCommand() {
47
48
  .option('--template <template>', 'Agent template (LLM, AUTO_MANAGED_STATE, BLANK)')
48
49
  .option('--no-wait', 'Return immediately without waiting for initialization')
49
50
  .action(async (name, options) => {
51
+ const output = createOutputWriter();
50
52
  const baseUrl = getGuildcoreUrl();
51
53
  const client = new GuildAPIClient({ baseUrl });
52
54
  try {
@@ -63,18 +65,12 @@ export function createAgentCreateCommand() {
63
65
  template = await promptForTemplate();
64
66
  }
65
67
  else {
66
- console.error('Error: --template is required in non-interactive mode');
67
- console.error('');
68
- console.error('Provide a template:');
69
- console.error(` guild agent create ${name} --template LLM`);
70
- console.error('');
71
- console.error('Available templates:');
72
- console.error(' • LLM - Simple language model agent (recommended)');
73
- console.error(' • AUTO_MANAGED_STATE - Agent with automatic state management');
74
- console.error(' • BLANK - Start from scratch');
68
+ output.error('Error: --template is required in non-interactive mode', `Provide a template:\n guild agent create ${name} --template LLM\n\nAvailable templates:\n • LLM - Simple language model agent (recommended)\n • AUTO_MANAGED_STATE - Agent with automatic state management\n • BLANK - Start from scratch`);
75
69
  process.exit(1);
76
70
  }
77
71
  }
72
+ // Normalize template to uppercase for case-insensitive matching
73
+ template = template.toUpperCase();
78
74
  // Validate template
79
75
  const validTemplates = [
80
76
  'LLM',
@@ -82,12 +78,7 @@ export function createAgentCreateCommand() {
82
78
  'BLANK',
83
79
  ];
84
80
  if (!validTemplates.includes(template)) {
85
- console.error(`Error: Invalid template '${template}'`);
86
- console.error('');
87
- console.error('Valid templates:');
88
- console.error(' • LLM');
89
- console.error(' • AUTO_MANAGED_STATE');
90
- console.error(' • BLANK');
81
+ output.error(`Error: Invalid template '${template}'`, 'Valid templates:\n • LLM\n • AUTO_MANAGED_STATE\n • BLANK');
91
82
  process.exit(1);
92
83
  }
93
84
  const response = await client.post('/agents', {
@@ -101,43 +92,36 @@ export function createAgentCreateCommand() {
101
92
  const pollResult = await pollAgentStatus(response.id, 'READY');
102
93
  if (pollResult.success && pollResult.response) {
103
94
  // Display the updated response with READY status
104
- console.log(JSON.stringify(pollResult.response, null, 2));
95
+ output.data(pollResult.response);
105
96
  }
106
97
  else {
107
98
  // Polling failed or timed out - this is an error
108
- console.error('Agent initialization failed or timed out');
109
- console.error(`Check status: guild agent get ${response.id}`);
110
- console.log(JSON.stringify(response, null, 2));
99
+ output.error('Agent initialization failed or timed out', `Check status: guild agent get ${response.id}`);
100
+ output.data(response);
111
101
  process.exit(1);
112
102
  }
113
103
  }
114
104
  else {
115
105
  // --no-wait: return immediately without polling
116
- console.log(JSON.stringify(response, null, 2));
106
+ output.data(response);
117
107
  }
118
108
  }
119
109
  catch (error) {
120
110
  const formattedError = handleAxiosError(error);
121
111
  // Provide specific error messages based on error code
122
112
  if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
123
- console.error('Not logged in.');
124
- console.error('');
125
- console.error('Please authenticate first:');
126
- console.error(' guild auth login');
113
+ output.error('Not logged in.', 'Please authenticate first:\n guild auth login');
127
114
  }
128
115
  else if (formattedError.code === ErrorCodes.CONN_REFUSED) {
129
- console.error('Cannot connect to Guild servers');
116
+ output.error('Cannot connect to Guild servers');
130
117
  }
131
118
  else if (formattedError.code === ErrorCodes.API_ERROR &&
132
119
  formattedError.details.includes('already exists')) {
133
- console.error(`Agent name '${name}' already exists.`);
134
- console.error('');
135
- console.error('Try a different name:');
136
- console.error(` guild agent create ${name}-v2`);
120
+ output.error(`Agent name '${name}' already exists.`, `Try a different name:\n guild agent create ${name}-v2`);
137
121
  }
138
122
  else {
139
123
  // Generic error with details from server
140
- console.error(`Failed to create agent: ${formattedError.details}`);
124
+ output.error(`Failed to create agent: ${formattedError.details}`);
141
125
  }
142
126
  process.exit(1);
143
127
  }
@@ -5,6 +5,7 @@ import { handleAxiosError, ErrorCodes, debug } from '../../lib/errors.js';
5
5
  import * as fs from 'fs/promises';
6
6
  import * as readline from 'readline';
7
7
  import { runGit, GitError, formatGitError } from '../../lib/git.js';
8
+ import { createOutputWriter } from '../../lib/output.js';
8
9
  import { resolveOwnerId } from '../../lib/owner-helpers.js';
9
10
  async function promptForName() {
10
11
  const rl = readline.createInterface({
@@ -53,17 +54,12 @@ export function createAgentForkCommand() {
53
54
  .option('--directory <path>', 'Target directory for clone')
54
55
  .option('--owner <owner-id>', 'Owner account (user or organization ID)')
55
56
  .action(async (sourceArg, options) => {
57
+ const output = createOutputWriter();
56
58
  try {
57
59
  // Parse agent-id:version-id format
58
60
  const parts = sourceArg.split(':');
59
61
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
60
- console.error('Error: Invalid argument format');
61
- console.error('');
62
- console.error('Expected: <agent-id>:<version-id>');
63
- console.error('Example: guild agent fork agent_abc123:version_xyz789');
64
- console.error('');
65
- console.error('To find versions:');
66
- console.error(` guild agent versions ${parts[0] || '<agent-id>'}`);
62
+ output.error('Error: Invalid argument format', `Expected: <agent-id>:<version-id>\nExample: guild agent fork agent_abc123:version_xyz789\n\nTo find versions:\n guild agent versions ${parts[0] || '<agent-id>'}`);
67
63
  process.exit(1);
68
64
  }
69
65
  const [sourceAgentId, sourceVersionId] = parts;
@@ -73,29 +69,18 @@ export function createAgentForkCommand() {
73
69
  if (isInteractive()) {
74
70
  agentName = await promptForName();
75
71
  if (!agentName) {
76
- console.error('Error: Agent name is required');
72
+ output.error('Error: Agent name is required');
77
73
  process.exit(1);
78
74
  }
79
75
  }
80
76
  else {
81
- console.error('Error: Agent name required in non-interactive mode');
82
- console.error('');
83
- console.error('Provide a name:');
84
- console.error(` guild agent fork ${sourceArg} --name my-forked-agent`);
85
- console.error('');
86
- console.error('Or run interactively to be prompted.');
77
+ output.error('Error: Agent name required in non-interactive mode', `Provide a name:\n guild agent fork ${sourceArg} --name my-forked-agent\n\nOr run interactively to be prompted.`);
87
78
  process.exit(1);
88
79
  }
89
80
  }
90
81
  // Validate name format (before API call)
91
82
  if (!/^[a-z0-9_-]{5,100}$/.test(agentName)) {
92
- console.error('Error: Invalid agent name');
93
- console.error('');
94
- console.error('Name must:');
95
- console.error(' • Be between 5 and 100 characters');
96
- console.error(' • Only contain lowercase letters, numbers, hyphens, and underscores');
97
- console.error('');
98
- console.error('Examples: my-agent, weather_bot, agent-007');
83
+ output.error('Error: Invalid agent name', 'Name must:\n • Be between 5 and 100 characters\n • Only contain lowercase letters, numbers, hyphens, and underscores\n\nExamples: my-agent, weather_bot, agent-007');
99
84
  process.exit(1);
100
85
  }
101
86
  // Determine directory (default to agent name)
@@ -108,19 +93,14 @@ export function createAgentForkCommand() {
108
93
  if (dirExists) {
109
94
  const isEmpty = await isDirectoryEmpty(targetDir);
110
95
  if (!isEmpty) {
111
- console.error(`Error: Directory '${targetDir}' already exists and is not empty`);
112
- console.error('');
113
- console.error('Choose a different directory:');
114
- console.error(` guild agent fork ${sourceArg} --directory ./different-path`);
115
- console.error('');
116
- console.error('Or remove the existing directory first.');
96
+ output.error(`Error: Directory '${targetDir}' already exists and is not empty`, `Choose a different directory:\n guild agent fork ${sourceArg} --directory ./different-path\n\nOr remove the existing directory first.`);
117
97
  process.exit(1);
118
98
  }
119
99
  }
120
100
  // Now fetch source version from API (after local validations)
121
101
  const client = new GuildAPIClient();
122
102
  const sourceVersion = await client.get(`/agents/${sourceAgentId}/versions/${sourceVersionId}`);
123
- console.log(`✓ Fetched source version from '${sourceVersion.agent.name}' (${sourceVersionId.substring(0, 12)})`);
103
+ output.progress(`✓ Fetched source version from '${sourceVersion.agent.name}' (${sourceVersionId.substring(0, 12)})`);
124
104
  // Determine description
125
105
  let description = options.description;
126
106
  if (!description) {
@@ -139,16 +119,16 @@ export function createAgentForkCommand() {
139
119
  interactive: isInteractive(),
140
120
  });
141
121
  // Create forked agent
142
- console.log(`✓ Forking agent '${agentName}'...`);
122
+ output.progress(`✓ Forking agent '${agentName}'...`);
143
123
  const newAgent = await client.post('/agents', {
144
124
  name: agentName,
145
125
  description,
146
126
  forked_from_version: sourceVersionId,
147
127
  owner_id: owner.id,
148
128
  });
149
- console.log(`✓ Agent created: ${newAgent.name} (${newAgent.id})`);
129
+ output.progress(`✓ Agent created: ${newAgent.name} (${newAgent.id})`);
150
130
  // Poll until repository is ready
151
- console.log('✓ Waiting for repository initialization...');
131
+ output.progress('✓ Waiting for repository initialization...');
152
132
  let attempts = 0;
153
133
  const maxAttempts = 60; // 2 minutes
154
134
  let agent = newAgent;
@@ -161,75 +141,49 @@ export function createAgentForkCommand() {
161
141
  debug(`Agent status: ${agent.status} (attempt ${attempts}/${maxAttempts})`);
162
142
  }
163
143
  if (agent.status === 'FAILED') {
164
- console.error('Error: Repository initialization failed');
165
- console.error('');
166
- console.error('The agent was created but the repository could not be initialized.');
167
- console.error('Check the agent status:');
168
- console.error(` guild agent get ${newAgent.id}`);
144
+ output.error('Error: Repository initialization failed', `The agent was created but the repository could not be initialized.\nCheck the agent status:\n guild agent get ${newAgent.id}`);
169
145
  process.exit(1);
170
146
  }
171
147
  if (agent.status !== 'READY') {
172
- console.error('Error: Timed out waiting for repository initialization');
173
- console.error('');
174
- console.error('The agent was created but the repository is not ready yet.');
175
- console.error('Check the status with:');
176
- console.error(` guild agent get ${newAgent.id}`);
177
- console.error('');
178
- console.error('Once ready, clone manually:');
179
- console.error(` guild agent clone ${newAgent.id} --directory ${targetDir}`);
148
+ output.error('Error: Timed out waiting for repository initialization', `The agent was created but the repository is not ready yet.\nCheck the status with:\n guild agent get ${newAgent.id}\n\nOnce ready, clone manually:\n guild agent clone ${newAgent.id} --directory ${targetDir}`);
180
149
  process.exit(1);
181
150
  }
182
- console.log('✓ Repository ready!');
151
+ output.progress('✓ Repository ready!');
183
152
  if (!agent.git_url) {
184
- console.error('Error: Agent repository URL not available');
185
- console.error('');
186
- console.error('The agent was created but git_url is missing.');
187
- console.error('Check the agent details:');
188
- console.error(` guild agent get ${newAgent.id}`);
153
+ output.error('Error: Agent repository URL not available', `The agent was created but git_url is missing.\nCheck the agent details:\n guild agent get ${newAgent.id}`);
189
154
  process.exit(1);
190
155
  }
191
156
  // Clone the new repository
192
157
  await runGit(['clone', agent.git_url, targetDir]);
193
- console.log(`✓ Cloned repository to ${targetDir}`);
158
+ output.progress(`✓ Cloned repository to ${targetDir}`);
194
159
  // Display next steps
195
- console.log('');
196
- console.log('Next steps:');
197
- console.log(` 1. cd ${targetDir}`);
198
- console.log(' 2. Make your changes to the code');
199
- console.log(` 3. Run 'guild agent save --message "your changes"'`);
200
- console.log(` 4. Run 'guild agent test' to test your changes`);
160
+ output.progress('');
161
+ output.progress('Next steps:');
162
+ output.progress(` 1. cd ${targetDir}`);
163
+ output.progress(' 2. Make your changes to the code');
164
+ output.progress(` 3. Run 'guild agent save --message "your changes"'`);
165
+ output.progress(` 4. Run 'guild agent test' to test your changes`);
201
166
  }
202
167
  catch (error) {
203
168
  if (error instanceof GitError) {
204
- console.error('Error: Failed to clone repository');
205
- console.error('');
206
- console.error(formatGitError(error));
207
- console.error('');
208
- console.error('The agent was created successfully, but cloning failed.');
209
- console.error('Try cloning manually:');
210
- console.error(` guild agent clone <agent-id>`);
169
+ output.error('Error: Failed to clone repository', `${formatGitError(error)}\n\nThe agent was created successfully, but cloning failed.\nTry cloning manually:\n guild agent clone <agent-id>`);
211
170
  process.exit(1);
212
171
  }
213
172
  const formattedError = handleAxiosError(error);
214
173
  if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
215
- console.error('Not authenticated. Please log in first.');
216
- console.error('');
217
- console.error('Run: guild auth login');
174
+ output.error('Not authenticated. Please log in first.', 'Run: guild auth login');
218
175
  process.exit(1);
219
176
  }
220
177
  if (formattedError.code === ErrorCodes.NOT_FOUND) {
221
178
  const [sourceAgentId] = sourceArg.split(':');
222
- console.error(`Error: Version not found: ${sourceArg}`);
223
- console.error('');
224
- console.error('Check available versions:');
225
- console.error(` guild agent versions ${sourceAgentId}`);
179
+ output.error(`Error: Version not found: ${sourceArg}`, `Check available versions:\n guild agent versions ${sourceAgentId}`);
226
180
  process.exit(1);
227
181
  }
228
182
  if (formattedError.code === ErrorCodes.CONN_REFUSED) {
229
- console.error('Cannot connect to Guild servers');
183
+ output.error('Cannot connect to Guild servers');
230
184
  process.exit(1);
231
185
  }
232
- console.error(`Failed to fork agent: ${formattedError.details}`);
186
+ output.error(`Failed to fork agent: ${formattedError.details}`);
233
187
  process.exit(1);
234
188
  }
235
189
  });
@@ -4,28 +4,29 @@ import { GuildAPIClient } from '../../lib/api-client.js';
4
4
  import { getAuthToken } from '../../lib/auth.js';
5
5
  import { getAgentId } from '../../lib/agent-helpers.js';
6
6
  import { handleAxiosError } from '../../lib/errors.js';
7
+ import { createOutputWriter } from '../../lib/output.js';
7
8
  export function createAgentGetCommand() {
8
9
  const cmd = new Command('get');
9
10
  cmd
10
11
  .description('Get agent details')
11
12
  .argument('[identifier]', 'Agent ID or full name (e.g., owner/agent-name)')
12
13
  .action(async (idArg) => {
14
+ const output = createOutputWriter();
13
15
  try {
14
16
  const token = await getAuthToken();
15
17
  if (!token) {
16
- console.error('Not authenticated. Please log in first.');
17
- console.error('Run: guild auth login');
18
+ output.error('Not authenticated. Please log in first.', 'Run: guild auth login');
18
19
  process.exit(1);
19
20
  }
20
21
  // Get agent ID from argument or guild.json
21
22
  const { agentId } = await getAgentId(idArg);
22
23
  const client = new GuildAPIClient();
23
24
  const response = await client.get(`/agents/${agentId}`);
24
- console.log(JSON.stringify(response, null, 2));
25
+ output.data(response);
25
26
  }
26
27
  catch (error) {
27
28
  const formattedError = handleAxiosError(error);
28
- console.error(`Failed to get agent: ${formattedError.details}`);
29
+ output.error(`Failed to get agent: ${formattedError.details}`);
29
30
  process.exit(1);
30
31
  }
31
32
  });
@@ -15,15 +15,15 @@ export function createAgentGrepCommand() {
15
15
  }
16
16
  const BATCH_SIZE = 20;
17
17
  async function grep(patternArg, options) {
18
+ const output = createOutputWriter();
18
19
  try {
19
20
  const token = await getAuthToken();
20
21
  if (!token) {
21
- console.error('Not authenticated. Please log in first.');
22
- console.error('Run: guild auth login');
22
+ output.error('Not authenticated. Please log in first.', 'Run: guild auth login');
23
23
  process.exit(1);
24
24
  }
25
25
  if (patternArg === undefined) {
26
- console.error('Please specify a pattern to match');
26
+ output.error('Please specify a pattern to match');
27
27
  process.exit(1);
28
28
  }
29
29
  let patternRE;
@@ -31,7 +31,7 @@ async function grep(patternArg, options) {
31
31
  patternRE = new RegExp(patternArg);
32
32
  }
33
33
  catch {
34
- console.error(`Invalid regex pattern: ${patternArg}`);
34
+ output.error(`Invalid regex pattern: ${patternArg}`);
35
35
  process.exit(1);
36
36
  }
37
37
  const client = new GuildAPIClient();
@@ -50,15 +50,14 @@ async function grep(patternArg, options) {
50
50
  files = await client.get(`/agents/${agent.id}/code`);
51
51
  }
52
52
  catch (ex) {
53
- const out = createOutputWriter();
54
53
  const formattedError = handleAxiosError(ex);
55
- out.error(`${agent.owner?.name}/${agent.name}: ${formattedError.details}`);
54
+ output.error(`${agent.owner?.name}/${agent.name}: ${formattedError.details}`);
56
55
  return;
57
56
  }
58
57
  for (const { path, content } of files) {
59
58
  content.split('\n').forEach((line, lineNumber) => {
60
59
  if (patternRE.test(line)) {
61
- console.log(`${agent.owner?.name}/${agent.name}/${path}:${lineNumber + 1}:${line}`);
60
+ output.progress(`${agent.owner?.name}/${agent.name}/${path}:${lineNumber + 1}:${line}`);
62
61
  }
63
62
  });
64
63
  }
@@ -69,9 +68,8 @@ async function grep(patternArg, options) {
69
68
  }
70
69
  }
71
70
  catch (error) {
72
- const out = createOutputWriter();
73
71
  const formattedError = handleAxiosError(error);
74
- out.error(`Failed to search agents: ${formattedError.details}`);
72
+ output.error(`Failed to search agents: ${formattedError.details}`);
75
73
  process.exit(1);
76
74
  }
77
75
  }
@@ -130,6 +130,8 @@ export function createAgentInitCommand() {
130
130
  process.exit(1);
131
131
  }
132
132
  }
133
+ // Normalize template to uppercase for case-insensitive matching
134
+ template = template.toUpperCase();
133
135
  // Validate template
134
136
  const validTemplates = [
135
137
  'LLM',