@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.
- package/dist/commands/agent/clone.js +22 -67
- package/dist/commands/agent/code.js +12 -32
- package/dist/commands/agent/create.js +14 -30
- package/dist/commands/agent/fork.js +27 -73
- package/dist/commands/agent/get.js +5 -4
- package/dist/commands/agent/grep.js +7 -9
- package/dist/commands/agent/init.js +2 -0
- package/dist/commands/agent/list.js +5 -6
- package/dist/commands/agent/publish.js +27 -44
- package/dist/commands/agent/pull.js +8 -35
- package/dist/commands/agent/revalidate.js +8 -16
- package/dist/commands/agent/save.js +30 -76
- package/dist/commands/agent/search.js +5 -6
- package/dist/commands/agent/tags/add.js +14 -24
- package/dist/commands/agent/tags/list.js +12 -23
- package/dist/commands/agent/tags/remove.js +16 -27
- package/dist/commands/agent/tags/set.js +14 -19
- package/dist/commands/agent/unpublish.js +12 -17
- package/dist/commands/agent/update.js +12 -29
- package/dist/commands/agent/versions.js +13 -11
- package/dist/commands/agent/workspaces.d.ts +3 -0
- package/dist/commands/agent/workspaces.js +51 -0
- package/dist/commands/auth/login.js +4 -2
- package/dist/commands/auth/logout.js +3 -1
- package/dist/commands/auth/status.js +4 -3
- package/dist/commands/auth/token.js +3 -2
- package/dist/commands/config/get.js +7 -9
- package/dist/commands/config/list.js +13 -11
- package/dist/commands/config/path.js +6 -4
- package/dist/commands/config/set.js +17 -22
- package/dist/commands/doctor.js +9 -7
- package/dist/commands/session/create.js +7 -5
- package/dist/commands/session/events.js +5 -3
- package/dist/commands/session/get.js +5 -3
- package/dist/commands/session/list.js +5 -4
- package/dist/commands/session/send.js +7 -5
- package/dist/commands/session/tasks.js +5 -3
- package/dist/commands/setup.js +15 -14
- package/dist/commands/trigger/activate.js +7 -6
- package/dist/commands/trigger/create.js +16 -15
- package/dist/commands/trigger/deactivate.js +7 -6
- package/dist/commands/trigger/get.js +5 -4
- package/dist/commands/trigger/list.js +5 -5
- package/dist/commands/trigger/sessions.js +7 -6
- package/dist/commands/trigger/update.js +11 -10
- package/dist/commands/version.js +7 -5
- package/dist/commands/workspace/agent/add.js +16 -22
- package/dist/commands/workspace/agent/list.js +12 -32
- package/dist/commands/workspace/agent/remove.js +9 -15
- package/dist/commands/workspace/context/edit.js +13 -27
- package/dist/commands/workspace/context/get.js +8 -14
- package/dist/commands/workspace/context/list.js +12 -37
- package/dist/commands/workspace/context/publish.js +7 -11
- package/dist/commands/workspace/create.js +7 -11
- package/dist/commands/workspace/current.js +19 -31
- package/dist/commands/workspace/get.js +7 -11
- package/dist/commands/workspace/list.js +5 -8
- package/dist/commands/workspace/select.js +17 -22
- package/dist/index.js +2 -0
- package/dist/lib/agent-helpers.js +2 -2
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +1 -0
- package/dist/lib/npmrc.js +9 -1
- package/dist/lib/output.d.ts +18 -4
- package/dist/lib/output.js +112 -19
- package/docs/getting-started.md +1 -1
- package/docs/output-format.md +1 -1
- package/docs/skills/agent-dev.md +18 -17
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
// Determine target directory
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
output.progress('✓ Created guild.json');
|
|
115
77
|
}
|
|
116
78
|
// Display next steps
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
output.error('Cannot connect to Guild servers');
|
|
147
102
|
process.exit(1);
|
|
148
103
|
}
|
|
149
|
-
|
|
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
|
-
|
|
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
|
-
|
|
32
|
+
output.error('Cannot connect to Guild servers');
|
|
34
33
|
}
|
|
35
34
|
else if (formattedError.code === ErrorCodes.NOT_FOUND) {
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
68
|
+
});
|
|
80
69
|
process.exit(0);
|
|
81
70
|
}
|
|
82
71
|
catch (error) {
|
|
83
72
|
const err = error;
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
+
output.data(pollResult.response);
|
|
105
96
|
}
|
|
106
97
|
else {
|
|
107
98
|
// Polling failed or timed out - this is an error
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
+
output.error('Error: Agent name is required');
|
|
77
73
|
process.exit(1);
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
else {
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
129
|
+
output.progress(`✓ Agent created: ${newAgent.name} (${newAgent.id})`);
|
|
150
130
|
// Poll until repository is ready
|
|
151
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
151
|
+
output.progress('✓ Repository ready!');
|
|
183
152
|
if (!agent.git_url) {
|
|
184
|
-
|
|
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
|
-
|
|
158
|
+
output.progress(`✓ Cloned repository to ${targetDir}`);
|
|
194
159
|
// Display next steps
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
+
output.error('Cannot connect to Guild servers');
|
|
230
184
|
process.exit(1);
|
|
231
185
|
}
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
+
output.data(response);
|
|
25
26
|
}
|
|
26
27
|
catch (error) {
|
|
27
28
|
const formattedError = handleAxiosError(error);
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
+
output.error(`Failed to search agents: ${formattedError.details}`);
|
|
75
73
|
process.exit(1);
|
|
76
74
|
}
|
|
77
75
|
}
|