@guildai/cli 0.3.16 → 0.3.18

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 (69) hide show
  1. package/dist/commands/agent/clone.js +22 -67
  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 -6
  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 +30 -76
  13. package/dist/commands/agent/search.js +5 -6
  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 +13 -11
  21. package/dist/commands/agent/workspaces.d.ts +3 -0
  22. package/dist/commands/agent/workspaces.js +51 -0
  23. package/dist/commands/auth/login.js +4 -2
  24. package/dist/commands/auth/logout.js +3 -1
  25. package/dist/commands/auth/status.js +4 -3
  26. package/dist/commands/auth/token.js +3 -2
  27. package/dist/commands/config/get.js +7 -9
  28. package/dist/commands/config/list.js +13 -11
  29. package/dist/commands/config/path.js +6 -4
  30. package/dist/commands/config/set.js +17 -22
  31. package/dist/commands/doctor.js +9 -7
  32. package/dist/commands/session/create.js +7 -5
  33. package/dist/commands/session/events.js +5 -3
  34. package/dist/commands/session/get.js +5 -3
  35. package/dist/commands/session/list.js +5 -4
  36. package/dist/commands/session/send.js +7 -5
  37. package/dist/commands/session/tasks.js +5 -3
  38. package/dist/commands/setup.js +15 -14
  39. package/dist/commands/trigger/activate.js +7 -6
  40. package/dist/commands/trigger/create.js +16 -15
  41. package/dist/commands/trigger/deactivate.js +7 -6
  42. package/dist/commands/trigger/get.js +5 -4
  43. package/dist/commands/trigger/list.js +5 -5
  44. package/dist/commands/trigger/sessions.js +7 -6
  45. package/dist/commands/trigger/update.js +11 -10
  46. package/dist/commands/version.js +7 -5
  47. package/dist/commands/workspace/agent/add.js +16 -22
  48. package/dist/commands/workspace/agent/list.js +12 -32
  49. package/dist/commands/workspace/agent/remove.js +9 -15
  50. package/dist/commands/workspace/context/edit.js +13 -27
  51. package/dist/commands/workspace/context/get.js +8 -14
  52. package/dist/commands/workspace/context/list.js +12 -37
  53. package/dist/commands/workspace/context/publish.js +7 -11
  54. package/dist/commands/workspace/create.js +7 -11
  55. package/dist/commands/workspace/current.js +19 -31
  56. package/dist/commands/workspace/get.js +7 -11
  57. package/dist/commands/workspace/list.js +5 -8
  58. package/dist/commands/workspace/select.js +17 -22
  59. package/dist/index.js +2 -0
  60. package/dist/lib/agent-helpers.js +2 -2
  61. package/dist/lib/generated-types.d.ts +1 -1
  62. package/dist/lib/generated-types.js +1 -0
  63. package/dist/lib/npmrc.js +9 -1
  64. package/dist/lib/output.d.ts +18 -4
  65. package/dist/lib/output.js +112 -19
  66. package/docs/getting-started.md +1 -1
  67. package/docs/output-format.md +1 -1
  68. package/docs/skills/agent-dev.md +18 -17
  69. package/package.json +1 -1
@@ -4,24 +4,9 @@ import { GuildAPIClient } from '../../lib/api-client.js';
4
4
  import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
5
5
  import * as fs from 'fs/promises';
6
6
  import * as path from 'path';
7
- import * as readline from 'readline';
8
7
  import { getAuthenticatedUrl } from '../../lib/auth.js';
9
8
  import { runGit, GitError, formatGitError } from '../../lib/git.js';
10
- async function promptForDirectory(suggestedDir) {
11
- const rl = readline.createInterface({
12
- input: process.stdin,
13
- output: process.stdout,
14
- });
15
- return new Promise((resolve) => {
16
- rl.question(`Target directory (default: ${suggestedDir}): `, (answer) => {
17
- rl.close();
18
- resolve(answer.trim() || suggestedDir);
19
- });
20
- });
21
- }
22
- function isInteractive() {
23
- return process.stdin.isTTY === true;
24
- }
9
+ import { createOutputWriter } from '../../lib/output.js';
25
10
  async function isDirectoryEmpty(dirPath) {
26
11
  try {
27
12
  const files = await fs.readdir(dirPath);
@@ -40,34 +25,18 @@ export function createAgentCloneCommand() {
40
25
  .option('--directory <path>', 'Target directory for clone')
41
26
  .option('--force', 'Clone even if directory is not empty', false)
42
27
  .action(async (agentId, options) => {
28
+ const output = createOutputWriter();
43
29
  try {
44
30
  // Fetch agent details
45
31
  const client = new GuildAPIClient();
46
32
  const agent = await client.get(`/agents/${agentId}`);
47
33
  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.');
34
+ output.error('Error: Agent does not have a git repository', 'This agent may not have been initialized with git.');
51
35
  process.exit(1);
52
36
  }
53
- console.log(`✓ Fetched agent '${agent.name}' (${agent.id})`);
54
- // Determine target directory
55
- let targetDir = options.directory;
56
- if (!targetDir) {
57
- if (isInteractive()) {
58
- const suggestedDir = `./${agent.name}`;
59
- targetDir = await promptForDirectory(suggestedDir);
60
- }
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.');
68
- process.exit(1);
69
- }
70
- }
37
+ output.progress(`✓ Fetched agent '${agent.name}' (${agent.id})`);
38
+ // Determine target directory (default to agent name, like git clone)
39
+ const targetDir = options.directory || agent.name;
71
40
  // Check if directory exists and is not empty
72
41
  const dirExists = await fs
73
42
  .access(targetDir)
@@ -76,25 +45,18 @@ export function createAgentCloneCommand() {
76
45
  if (dirExists) {
77
46
  const isEmpty = await isDirectoryEmpty(targetDir);
78
47
  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`);
48
+ 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
49
  process.exit(1);
86
50
  }
87
51
  }
88
52
  // Clone repository
89
53
  const cloneUrl = await getAuthenticatedUrl(agent.git_url);
90
54
  if (!cloneUrl) {
91
- console.error('Error: Not authenticated');
92
- console.error('');
93
- console.error('Run: guild auth login');
55
+ output.error('Error: Not authenticated', 'Run: guild auth login');
94
56
  process.exit(1);
95
57
  }
96
58
  await runGit(['clone', cloneUrl, targetDir]);
97
- console.log(`✓ Cloned repository to ${targetDir}`);
59
+ output.progress(`✓ Cloned repository to ${targetDir}`);
98
60
  // Create guild.json if it doesn't exist
99
61
  const guildJsonPath = path.join(targetDir, 'guild.json');
100
62
  const guildJsonExists = await fs
@@ -102,7 +64,7 @@ export function createAgentCloneCommand() {
102
64
  .then(() => true)
103
65
  .catch(() => false);
104
66
  if (guildJsonExists) {
105
- console.log('✓ Verified guild.json');
67
+ output.progress('✓ Verified guild.json');
106
68
  }
107
69
  else {
108
70
  // Create guild.json for local development
@@ -111,42 +73,35 @@ export function createAgentCloneCommand() {
111
73
  name: agent.name,
112
74
  };
113
75
  await fs.writeFile(guildJsonPath, JSON.stringify(guildConfig, null, 2) + '\n');
114
- console.log('✓ Created guild.json');
76
+ output.progress('✓ Created guild.json');
115
77
  }
116
78
  // 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`);
79
+ output.progress('');
80
+ output.progress('Next steps:');
81
+ output.progress(` 1. cd ${targetDir}`);
82
+ output.progress(' 2. Make your changes to the code');
83
+ output.progress(` 3. Run 'guild agent save --message "your changes"'`);
84
+ output.progress(` 4. Run 'guild agent test' to test your changes`);
123
85
  }
124
86
  catch (error) {
125
87
  if (error instanceof GitError) {
126
- console.error('Error: Failed to clone repository');
127
- console.error('');
128
- console.error(formatGitError(error));
88
+ output.error('Error: Failed to clone repository', formatGitError(error));
129
89
  process.exit(1);
130
90
  }
131
91
  const formattedError = handleAxiosError(error);
132
92
  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');
93
+ output.error('Not authenticated. Please log in first.', 'Run: guild auth login');
136
94
  process.exit(1);
137
95
  }
138
96
  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');
97
+ output.error(`Error: Agent not found: ${agentId}`, 'Check the agent ID:\n guild agent list');
143
98
  process.exit(1);
144
99
  }
145
100
  if (formattedError.code === ErrorCodes.CONN_REFUSED) {
146
- console.error('Cannot connect to Guild servers');
101
+ output.error('Cannot connect to Guild servers');
147
102
  process.exit(1);
148
103
  }
149
- console.error(`Failed to clone agent: ${formattedError.details}`);
104
+ output.error(`Failed to clone agent: ${formattedError.details}`);
150
105
  process.exit(1);
151
106
  }
152
107
  });
@@ -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
  }