@guildai/cli 0.5.11 → 0.5.13
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/fork.js +1 -3
- package/dist/commands/agent/init.js +1 -3
- package/dist/commands/auth/login.js +3 -1
- package/dist/commands/workspace/select.js +29 -33
- package/dist/index.js +19 -16
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +29 -23
- package/dist/lib/config.js +0 -4
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +1 -0
- package/dist/lib/gk.d.ts +15 -0
- package/dist/lib/gk.js +91 -0
- package/dist/lib/stdin.d.ts +4 -0
- package/dist/lib/stdin.js +6 -0
- package/docs/CLI_WORKFLOW.md +9 -9
- package/docs/skills/agent-dev.md +71 -48
- package/package.json +2 -1
- package/dist/commands/agent/create.d.ts +0 -3
- package/dist/commands/agent/create.js +0 -132
|
@@ -8,6 +8,7 @@ import * as readline from 'readline';
|
|
|
8
8
|
import { runGit, GitError, formatGitError } from '../../lib/git.js';
|
|
9
9
|
import { createOutputWriter } from '../../lib/output.js';
|
|
10
10
|
import { resolveOwnerId } from '../../lib/owner-helpers.js';
|
|
11
|
+
import { isInteractive } from '../../lib/stdin.js';
|
|
11
12
|
async function promptForName() {
|
|
12
13
|
const rl = readline.createInterface({
|
|
13
14
|
input: process.stdin,
|
|
@@ -32,9 +33,6 @@ async function promptForDescription(defaultDescription) {
|
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
function isInteractive() {
|
|
36
|
-
return process.stdin.isTTY === true;
|
|
37
|
-
}
|
|
38
36
|
async function isDirectoryEmpty(dirPath) {
|
|
39
37
|
try {
|
|
40
38
|
const files = await fs.readdir(dirPath);
|
|
@@ -11,6 +11,7 @@ import * as readline from 'readline';
|
|
|
11
11
|
import { getAuthenticatedUrl } from '../../lib/auth.js';
|
|
12
12
|
import { runGit, GitError, formatGitError, installPrePushHook } from '../../lib/git.js';
|
|
13
13
|
import { resolveOwnerId } from '../../lib/owner-helpers.js';
|
|
14
|
+
import { isInteractive } from '../../lib/stdin.js';
|
|
14
15
|
const TEMPLATE_CHOICES = [
|
|
15
16
|
{
|
|
16
17
|
name: 'LLM - Simple language model agent (recommended)',
|
|
@@ -71,9 +72,6 @@ async function promptForTemplate() {
|
|
|
71
72
|
]);
|
|
72
73
|
return template;
|
|
73
74
|
}
|
|
74
|
-
function isInteractive() {
|
|
75
|
-
return process.stdin.isTTY === true;
|
|
76
|
-
}
|
|
77
75
|
export function createAgentInitCommand() {
|
|
78
76
|
const cmd = new Command('init');
|
|
79
77
|
cmd
|
|
@@ -11,9 +11,11 @@ export function createAuthLoginCommand() {
|
|
|
11
11
|
.description('Login to Guild.ai')
|
|
12
12
|
.option('--return-url <url>', 'Custom URL to redirect to after authentication')
|
|
13
13
|
.option('--return-label <text>', 'Friendly label for return button (e.g., "VSCode")')
|
|
14
|
+
.option('--no-browser', 'Skip opening the browser (print URL only)')
|
|
14
15
|
.action(async (options) => {
|
|
15
16
|
const output = createOutputWriter();
|
|
16
|
-
const
|
|
17
|
+
const noBrowser = options.browser === false;
|
|
18
|
+
const success = await login(options.returnUrl, options.returnLabel, noBrowser);
|
|
17
19
|
if (success) {
|
|
18
20
|
try {
|
|
19
21
|
await configureNpmrc();
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import * as fs from 'fs/promises';
|
|
5
|
-
import
|
|
5
|
+
import search from '@inquirer/search';
|
|
6
6
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
7
7
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
8
8
|
import { createOutputWriter } from '../../lib/output.js';
|
|
9
|
+
import { isInteractive } from '../../lib/stdin.js';
|
|
9
10
|
import { isAgentDirectory, loadLocalConfig, getLocalConfigPath, getWorkspaceId, saveGlobalConfig, } from '../../lib/guild-config.js';
|
|
10
11
|
/**
|
|
11
12
|
* Format workspace for display with owner name.
|
|
@@ -68,44 +69,39 @@ export function createWorkspaceSelectCommand() {
|
|
|
68
69
|
}
|
|
69
70
|
return;
|
|
70
71
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
if (!isInteractive()) {
|
|
73
|
+
output.error('Interactive mode requires a terminal.', 'Provide a workspace argument:\n guild workspace select <name|id>');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// Interactive mode: fetch all workspaces across all pages
|
|
77
|
+
const workspaces = await client.fetchAll('/me/workspaces?filter=all');
|
|
78
|
+
if (workspaces.length === 0) {
|
|
74
79
|
output.error('No workspaces found.', 'Create a workspace first:\n guild workspace create <name>');
|
|
75
80
|
process.exit(1);
|
|
76
81
|
}
|
|
77
82
|
// Resolve the currently selected workspace (if any)
|
|
78
83
|
const current = await getWorkspaceId();
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
84
|
+
const currentId = current?.workspaceId;
|
|
85
|
+
// Interactive searchable selection
|
|
86
|
+
const selectedId = await search({
|
|
87
|
+
message: 'Select a workspace (type to filter)',
|
|
88
|
+
pageSize: 15,
|
|
89
|
+
default: currentId,
|
|
90
|
+
source: (input) => {
|
|
91
|
+
const term = input?.toLowerCase() ?? '';
|
|
92
|
+
const filtered = term
|
|
93
|
+
? workspaces.filter((w) => w.name.toLowerCase().includes(term) ||
|
|
94
|
+
w.full_name?.toLowerCase().includes(term) ||
|
|
95
|
+
w.owner?.name.toLowerCase().includes(term))
|
|
96
|
+
: workspaces;
|
|
97
|
+
return filtered.map((w) => ({
|
|
98
|
+
name: formatWorkspaceDisplay(w) + (w.id === currentId ? ' (current)' : ''),
|
|
99
|
+
value: w.id,
|
|
100
|
+
short: w.name,
|
|
101
|
+
}));
|
|
102
|
+
},
|
|
96
103
|
});
|
|
97
|
-
|
|
98
|
-
// Empty input keeps current selection
|
|
99
|
-
if (answer.trim() === '' && currentIndex >= 0) {
|
|
100
|
-
console.log('Workspace unchanged.');
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
const selection = parseInt(answer.trim(), 10);
|
|
104
|
-
if (isNaN(selection) || selection < 1 || selection > workspaces.items.length) {
|
|
105
|
-
output.error('Invalid selection');
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
const selectedWorkspace = workspaces.items[selection - 1];
|
|
104
|
+
const selectedWorkspace = workspaces.find((w) => w.id === selectedId);
|
|
109
105
|
const target = await saveWorkspaceConfig(selectedWorkspace.id, selectedWorkspace.name);
|
|
110
106
|
if (target === 'local') {
|
|
111
107
|
output.success(`Workspace set for this agent: ${formatWorkspaceDisplay(selectedWorkspace)}`);
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,6 @@ import { createAuthLogoutCommand } from './commands/auth/logout.js';
|
|
|
11
11
|
import { createAuthStatusCommand } from './commands/auth/status.js';
|
|
12
12
|
import { createAuthTokenCommand } from './commands/auth/token.js';
|
|
13
13
|
import { createAgentListCommand } from './commands/agent/list.js';
|
|
14
|
-
import { createAgentCreateCommand } from './commands/agent/create.js';
|
|
15
14
|
import { createAgentGetCommand } from './commands/agent/get.js';
|
|
16
15
|
import { createAgentUpdateCommand } from './commands/agent/update.js';
|
|
17
16
|
import { createAgentVersionsCommand } from './commands/agent/versions.js';
|
|
@@ -80,6 +79,7 @@ import { createMcpCommand } from './commands/mcp.js';
|
|
|
80
79
|
import { showSplashScreen, shouldShowSplash } from './lib/splash.js';
|
|
81
80
|
import { checkForUpdate } from './lib/update-check.js';
|
|
82
81
|
import { setupUnknownCommandSuggestions } from './lib/did-you-mean.js';
|
|
82
|
+
import { getEnabledGKs, isGKEnabled } from './lib/gk.js';
|
|
83
83
|
import chalk from 'chalk';
|
|
84
84
|
// ESM equivalent of __dirname
|
|
85
85
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -132,7 +132,6 @@ authCmd.addCommand(createAuthTokenCommand());
|
|
|
132
132
|
// Agent command group
|
|
133
133
|
const agentCmd = program.command('agent').description('Agent management');
|
|
134
134
|
agentCmd.addCommand(createAgentListCommand());
|
|
135
|
-
agentCmd.addCommand(createAgentCreateCommand());
|
|
136
135
|
agentCmd.addCommand(createAgentGetCommand());
|
|
137
136
|
agentCmd.addCommand(createAgentUpdateCommand());
|
|
138
137
|
agentCmd.addCommand(createAgentVersionsCommand());
|
|
@@ -218,20 +217,24 @@ triggerCmd.addCommand(createTriggerUpdateCommand());
|
|
|
218
217
|
triggerCmd.addCommand(createTriggerActivateCommand());
|
|
219
218
|
triggerCmd.addCommand(createTriggerDeactivateCommand());
|
|
220
219
|
triggerCmd.addCommand(createTriggerSessionsCommand());
|
|
221
|
-
//
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
containerCmd.
|
|
226
|
-
containerCmd.addCommand(
|
|
227
|
-
containerCmd.addCommand(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
containerImageCmd
|
|
233
|
-
|
|
234
|
-
|
|
220
|
+
// GK-gated commands: fetch enabled GKs and conditionally register command groups
|
|
221
|
+
const enabledGKs = await getEnabledGKs();
|
|
222
|
+
if (isGKEnabled('CONTAINERS', enabledGKs)) {
|
|
223
|
+
// Container command group
|
|
224
|
+
const containerCmd = program.command('container').description('Container management');
|
|
225
|
+
containerCmd.addCommand(createContainerListCommand());
|
|
226
|
+
containerCmd.addCommand(createContainerGetCommand());
|
|
227
|
+
containerCmd.addCommand(createContainerEventsCommand());
|
|
228
|
+
containerCmd.addCommand(createContainerExecCommand());
|
|
229
|
+
containerCmd.addCommand(createContainerDestroyCommand());
|
|
230
|
+
// Container image command group
|
|
231
|
+
const containerImageCmd = program
|
|
232
|
+
.command('container-image')
|
|
233
|
+
.description('Container image management');
|
|
234
|
+
containerImageCmd.addCommand(createContainerImageCreateCommand());
|
|
235
|
+
containerImageCmd.addCommand(createContainerImageListCommand());
|
|
236
|
+
containerImageCmd.addCommand(createContainerImageGetCommand());
|
|
237
|
+
}
|
|
235
238
|
// Session command group
|
|
236
239
|
const sessionCmd = program.command('session').description('Session management');
|
|
237
240
|
sessionCmd.addCommand(createSessionListCommand());
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare function showBetaGuidance(): void;
|
|
|
50
50
|
* @param returnLabel - Optional friendly label for return button (e.g., "VSCode")
|
|
51
51
|
* @returns true if successful, false otherwise
|
|
52
52
|
*/
|
|
53
|
-
export declare function login(returnUrl?: string, returnLabel?: string): Promise<boolean>;
|
|
53
|
+
export declare function login(returnUrl?: string, returnLabel?: string, noBrowser?: boolean): Promise<boolean>;
|
|
54
54
|
/**
|
|
55
55
|
* Perform logout
|
|
56
56
|
*/
|
package/dist/lib/auth.js
CHANGED
|
@@ -196,36 +196,42 @@ export function showBetaGuidance() {
|
|
|
196
196
|
* @param returnLabel - Optional friendly label for return button (e.g., "VSCode")
|
|
197
197
|
* @returns true if successful, false otherwise
|
|
198
198
|
*/
|
|
199
|
-
export async function login(returnUrl, returnLabel) {
|
|
199
|
+
export async function login(returnUrl, returnLabel, noBrowser) {
|
|
200
200
|
const authUrl = getGuildcoreUrl();
|
|
201
201
|
try {
|
|
202
202
|
console.log(chalk.bold('\nGuild.ai Authentication'));
|
|
203
203
|
const deviceCode = await startDeviceFlow(authUrl, returnUrl, returnLabel);
|
|
204
204
|
// Display styled verification code box
|
|
205
205
|
displayVerificationCode(deviceCode.user_code, deviceCode.verification_uri_complete);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const { openBrowser } = await inquirer.prompt([
|
|
210
|
-
{
|
|
211
|
-
type: 'confirm',
|
|
212
|
-
name: 'openBrowser',
|
|
213
|
-
message: 'Press Enter to open browser in your default web browser...',
|
|
214
|
-
default: true,
|
|
215
|
-
},
|
|
216
|
-
]);
|
|
217
|
-
if (!openBrowser) {
|
|
218
|
-
console.error(chalk.red('✗ Authentication cancelled by user'));
|
|
219
|
-
return false;
|
|
206
|
+
const skipBrowser = noBrowser || process.stdin.isTTY !== true;
|
|
207
|
+
if (skipBrowser) {
|
|
208
|
+
console.log(chalk.dim('Open the URL above in your browser to complete authentication.'));
|
|
220
209
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
210
|
+
else {
|
|
211
|
+
// Wait for user to press Enter before opening browser
|
|
212
|
+
// This gives them time to see and copy the code before browser opens
|
|
213
|
+
// Critical for VSCode integration where browser may open full screen
|
|
214
|
+
const { openBrowser } = await inquirer.prompt([
|
|
215
|
+
{
|
|
216
|
+
type: 'confirm',
|
|
217
|
+
name: 'openBrowser',
|
|
218
|
+
message: 'Press Enter to open browser in your default web browser...',
|
|
219
|
+
default: true,
|
|
220
|
+
},
|
|
221
|
+
]);
|
|
222
|
+
if (!openBrowser) {
|
|
223
|
+
console.error(chalk.red('✗ Authentication cancelled by user'));
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
// Open URL in default browser after user confirmation
|
|
227
|
+
try {
|
|
228
|
+
const open = (await import('open')).default;
|
|
229
|
+
await open(deviceCode.verification_uri_complete);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Fallback if open fails - user can manually open URL
|
|
233
|
+
console.log(chalk.red('✗ Could not open browser automatically. Please visit the URL manually.\n'));
|
|
234
|
+
}
|
|
229
235
|
}
|
|
230
236
|
const token = await pollForToken(authUrl, deviceCode.device_code, deviceCode.interval);
|
|
231
237
|
if (token) {
|
package/dist/lib/config.js
CHANGED
|
@@ -46,10 +46,6 @@ export const IAP_CLIENT_ID = SHARED_IAP_CONFIG.clientId;
|
|
|
46
46
|
export const IAP_SERVICE_ACCOUNT = SHARED_IAP_CONFIG.serviceAccount;
|
|
47
47
|
/**
|
|
48
48
|
* GuildCore URL constants
|
|
49
|
-
*
|
|
50
|
-
* TODO: Consider implementing proper feature flags (GK) for the CLI instead of
|
|
51
|
-
* these hardcoded shortcuts. For now, --shared and --local are undocumented
|
|
52
|
-
* developer conveniences.
|
|
53
49
|
*/
|
|
54
50
|
const PROD_GUILDCORE_URL = 'https://app.guild.ai/api';
|
|
55
51
|
const SHARED_GUILDCORE_URL = 'https://shared.guildai.dev/api';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const WEBHOOK_SERVICES: readonly ["AZURE_DEVOPS", "BITBUCKET", "CYPRESS", "GITHUB", "GOOGLE_DOCS", "GOOGLE_COMPUTE", "GOOGLE_LOGGING", "JIRA", "LINEAR", "NEWRELIC", "NOTION", "SLACK", "TESTRAIL", "ZENDESK"];
|
|
1
|
+
export declare const WEBHOOK_SERVICES: readonly ["AZURE_DEVOPS", "BITBUCKET", "CURL", "CYPRESS", "GITHUB", "GOOGLE_DOCS", "GOOGLE_COMPUTE", "GOOGLE_LOGGING", "JIRA", "LINEAR", "NEWRELIC", "NOTION", "SLACK", "TESTRAIL", "ZENDESK"];
|
|
2
2
|
export type WebhookService = (typeof WEBHOOK_SERVICES)[number];
|
|
3
3
|
export declare const TIME_TRIGGER_FREQUENCIES: readonly ["HOURLY", "DAILY", "WEEKLY", "MONTHLY", "CRON"];
|
|
4
4
|
export type TimeTriggerFrequency = (typeof TIME_TRIGGER_FREQUENCIES)[number];
|
package/dist/lib/gk.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the set of GK names enabled for the current user.
|
|
3
|
+
*
|
|
4
|
+
* Returns null when the enabled set cannot be determined (not authenticated,
|
|
5
|
+
* network error, etc.). Callers should treat null as "show everything" (fail-open).
|
|
6
|
+
*/
|
|
7
|
+
export declare function getEnabledGKs(): Promise<Set<string> | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Check whether a specific GK is enabled for the current user.
|
|
10
|
+
*
|
|
11
|
+
* When enabledGKs is null (could not be determined), returns true (fail-open)
|
|
12
|
+
* so that commands are not hidden due to transient errors.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isGKEnabled(gk: string, enabledGKs: Set<string> | null): boolean;
|
|
15
|
+
//# sourceMappingURL=gk.d.ts.map
|
package/dist/lib/gk.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import { getGlobalConfigDir } from './guild-config.js';
|
|
7
|
+
import { getGuildcoreUrl } from './config.js';
|
|
8
|
+
import { getAuthToken } from './auth.js';
|
|
9
|
+
import { getIapHeaders } from './iap.js';
|
|
10
|
+
import { debug } from './errors.js';
|
|
11
|
+
const GK_CACHE_FILE = 'gk-cache.json';
|
|
12
|
+
const GK_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
13
|
+
function getCachePath() {
|
|
14
|
+
return path.join(getGlobalConfigDir(), GK_CACHE_FILE);
|
|
15
|
+
}
|
|
16
|
+
async function readCache(host) {
|
|
17
|
+
try {
|
|
18
|
+
const content = await fs.readFile(getCachePath(), 'utf-8');
|
|
19
|
+
const entry = JSON.parse(content);
|
|
20
|
+
if (entry.host === host && Date.now() - entry.ts < GK_CACHE_TTL_MS) {
|
|
21
|
+
debug('GK cache hit for %s', host);
|
|
22
|
+
return entry.gks;
|
|
23
|
+
}
|
|
24
|
+
debug('GK cache stale or host mismatch');
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
debug('GK cache not found or unreadable');
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
async function writeCache(host, gks) {
|
|
32
|
+
const configDir = getGlobalConfigDir();
|
|
33
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
34
|
+
await fs.writeFile(getCachePath(), JSON.stringify({ gks, ts: Date.now(), host }));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fetch the set of GK names enabled for the current user.
|
|
38
|
+
*
|
|
39
|
+
* Returns null when the enabled set cannot be determined (not authenticated,
|
|
40
|
+
* network error, etc.). Callers should treat null as "show everything" (fail-open).
|
|
41
|
+
*/
|
|
42
|
+
export async function getEnabledGKs() {
|
|
43
|
+
const token = await getAuthToken();
|
|
44
|
+
if (!token) {
|
|
45
|
+
debug('Not authenticated, skipping GK fetch');
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const baseUrl = getGuildcoreUrl();
|
|
49
|
+
let host;
|
|
50
|
+
try {
|
|
51
|
+
host = new URL(baseUrl).host;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const cached = await readCache(host);
|
|
57
|
+
if (cached) {
|
|
58
|
+
return new Set(cached);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const iapHeaders = await getIapHeaders(baseUrl);
|
|
62
|
+
const response = await axios.get(`${baseUrl}/gks/enabled`, {
|
|
63
|
+
headers: {
|
|
64
|
+
...iapHeaders,
|
|
65
|
+
Authorization: `Bearer ${token}`,
|
|
66
|
+
Accept: 'application/json',
|
|
67
|
+
},
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
});
|
|
70
|
+
const gkNames = response.data.gks.map((g) => g.name);
|
|
71
|
+
await writeCache(host, gkNames);
|
|
72
|
+
debug('Fetched %d enabled GKs', gkNames.length);
|
|
73
|
+
return new Set(gkNames);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
debug('Failed to fetch GKs: %s', error);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check whether a specific GK is enabled for the current user.
|
|
82
|
+
*
|
|
83
|
+
* When enabledGKs is null (could not be determined), returns true (fail-open)
|
|
84
|
+
* so that commands are not hidden due to transient errors.
|
|
85
|
+
*/
|
|
86
|
+
export function isGKEnabled(gk, enabledGKs) {
|
|
87
|
+
if (!enabledGKs)
|
|
88
|
+
return true;
|
|
89
|
+
return enabledGKs.has(gk);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=gk.js.map
|
package/dist/lib/stdin.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if stdin is connected to an interactive terminal (TTY).
|
|
3
|
+
*/
|
|
4
|
+
export declare function isInteractive(): boolean;
|
|
1
5
|
/**
|
|
2
6
|
* Exit with a helpful error when stdin is piped but no --mode flag was given.
|
|
3
7
|
* Call this before rendering an interactive UI (Ink render()) so users who
|
package/dist/lib/stdin.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// Copyright 2026 Guild.ai
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Check if stdin is connected to an interactive terminal (TTY).
|
|
5
|
+
*/
|
|
6
|
+
export function isInteractive() {
|
|
7
|
+
return process.stdin.isTTY === true;
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
10
|
* Exit with a helpful error when stdin is piped but no --mode flag was given.
|
|
5
11
|
* Call this before rendering an interactive UI (Ink render()) so users who
|
package/docs/CLI_WORKFLOW.md
CHANGED
|
@@ -11,7 +11,7 @@ For local agent development using the Guild CLI. This workflow manages agent cod
|
|
|
11
11
|
|
|
12
12
|
If Guild MCP tools are available (check for tools prefixed with `guild_`), use them for **read operations**: searching agents, listing workspaces, reading contexts, checking sessions, viewing credentials. MCP tools are faster and don't require shell execution.
|
|
13
13
|
|
|
14
|
-
Use the **CLI** (via Bash) for **local development operations**: `guild agent
|
|
14
|
+
Use the **CLI** (via Bash) for **local development operations**: `guild agent init`, `guild agent save`, `guild agent test`, `guild agent pull`, `guild agent clone`. These involve the local filesystem and git, which MCP can't do.
|
|
15
15
|
|
|
16
16
|
## CRITICAL: Always Use the Guild CLI
|
|
17
17
|
|
|
@@ -19,7 +19,7 @@ Use the **CLI** (via Bash) for **local development operations**: `guild agent cr
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
# Create a new agent
|
|
22
|
-
guild agent
|
|
22
|
+
guild agent init --name my-agent --template LLM
|
|
23
23
|
|
|
24
24
|
# Clone an existing agent
|
|
25
25
|
guild agent clone guildai/dev-assistant
|
|
@@ -66,11 +66,11 @@ guild setup --claude-md
|
|
|
66
66
|
### Creating Agents
|
|
67
67
|
|
|
68
68
|
```bash
|
|
69
|
-
# Create
|
|
70
|
-
guild agent
|
|
71
|
-
guild agent
|
|
72
|
-
guild agent
|
|
73
|
-
guild agent
|
|
69
|
+
# Create and initialize a new agent (interactive — prompts for name and template)
|
|
70
|
+
guild agent init
|
|
71
|
+
guild agent init --name my-agent --template LLM
|
|
72
|
+
guild agent init --name my-agent --template AUTO_MANAGED_STATE
|
|
73
|
+
guild agent init --name my-agent --template BLANK
|
|
74
74
|
|
|
75
75
|
# Fork an existing agent
|
|
76
76
|
guild agent init --fork owner/agent-name
|
|
@@ -145,7 +145,7 @@ guild agent chat "Hello, can you help me?"
|
|
|
145
145
|
|
|
146
146
|
## File Structure
|
|
147
147
|
|
|
148
|
-
After `guild agent
|
|
148
|
+
After `guild agent init`, you get:
|
|
149
149
|
|
|
150
150
|
```
|
|
151
151
|
my-agent/
|
|
@@ -175,7 +175,7 @@ Working tree is clean and there are no unpushed commits. Make a code change, com
|
|
|
175
175
|
You're not in an agent directory. Either:
|
|
176
176
|
|
|
177
177
|
- `cd` into the agent directory
|
|
178
|
-
- Run `guild agent
|
|
178
|
+
- Run `guild agent init` to create one
|
|
179
179
|
|
|
180
180
|
### Validation Failed
|
|
181
181
|
|
package/docs/skills/agent-dev.md
CHANGED
|
@@ -11,7 +11,7 @@ Build agents for Guild using the CLI. **Always use the Guild CLI for agent opera
|
|
|
11
11
|
|
|
12
12
|
If Guild MCP tools are available (check for tools prefixed with `guild_`), use them for **read operations**: searching agents, listing workspaces, reading contexts, checking sessions, viewing credentials. MCP tools are faster and don't require shell execution.
|
|
13
13
|
|
|
14
|
-
Use the **CLI** (via Bash) for **local development operations**: `guild agent
|
|
14
|
+
Use the **CLI** (via Bash) for **local development operations**: `guild agent init`, `guild agent save`, `guild agent test`, `guild agent pull`, `guild agent clone`. These involve the local filesystem and git, which MCP can't do.
|
|
15
15
|
|
|
16
16
|
## When to Use This
|
|
17
17
|
|
|
@@ -39,17 +39,14 @@ guild setup --claude-md
|
|
|
39
39
|
### Creating Agents
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
# Create
|
|
43
|
-
guild agent create my-agent
|
|
44
|
-
|
|
45
|
-
# Create with specific template
|
|
46
|
-
guild agent create my-agent --template LLM
|
|
47
|
-
guild agent create my-agent --template AUTO_MANAGED_STATE
|
|
48
|
-
guild agent create my-agent --template BLANK
|
|
49
|
-
|
|
50
|
-
# Initialize local workspace for existing agent
|
|
42
|
+
# Create and initialize a new agent (interactive - prompts for name and template)
|
|
51
43
|
guild agent init
|
|
52
44
|
|
|
45
|
+
# Create with specific name and template
|
|
46
|
+
guild agent init --name my-agent --template LLM
|
|
47
|
+
guild agent init --name my-agent --template AUTO_MANAGED_STATE
|
|
48
|
+
guild agent init --name my-agent --template BLANK
|
|
49
|
+
|
|
53
50
|
# Initialize with fork of existing agent
|
|
54
51
|
guild agent init --fork owner/agent-name
|
|
55
52
|
|
|
@@ -98,7 +95,7 @@ guild agent chat "Hello, can you help me?"
|
|
|
98
95
|
|
|
99
96
|
### For Creating and Modifying
|
|
100
97
|
|
|
101
|
-
- ✅ `guild agent
|
|
98
|
+
- ✅ `guild agent init`, `guild agent clone`
|
|
102
99
|
- ✅ `git add`, `git commit` (manage your own working tree)
|
|
103
100
|
- ✅ `guild agent save` (push commits and create a version)
|
|
104
101
|
- ✅ `guild agent save -A --message "desc"` (stage+commit+push in one step)
|
|
@@ -154,16 +151,22 @@ import { ask, output, callTools } from '@guildai/agents-sdk';
|
|
|
154
151
|
import { guildTools, userInterfaceTools } from '@guildai/agents-sdk';
|
|
155
152
|
|
|
156
153
|
// Service tools (from separate packages - NOT from SDK)
|
|
154
|
+
import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
|
|
155
|
+
import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
|
|
156
|
+
import { confluenceTools } from '@guildai-services/guildai~confluence';
|
|
157
|
+
import { cypressTools } from '@guildai-services/guildai~cypress';
|
|
158
|
+
import { figmaTools } from '@guildai-services/guildai~figma';
|
|
157
159
|
import { gitHubTools } from '@guildai-services/guildai~github';
|
|
158
|
-
import {
|
|
160
|
+
import { googleComputeTools } from '@guildai-services/guildai~google-compute';
|
|
161
|
+
import { googleLoggingTools } from '@guildai-services/guildai~google-logging';
|
|
159
162
|
import { jiraTools } from '@guildai-services/guildai~jira';
|
|
160
|
-
import { bitbucketTools } from '@guildai-services/guildai~bitbucket';
|
|
161
|
-
import { azureDevOpsTools } from '@guildai-services/guildai~azure-devops';
|
|
162
163
|
import { pipedreamTools } from '@guildai-services/guildai~pipedream';
|
|
163
|
-
import {
|
|
164
|
+
import { slackTools } from '@guildai-services/guildai~slack';
|
|
165
|
+
import { testrailTools } from '@guildai-services/guildai~testrail';
|
|
166
|
+
import { zendeskTools } from '@guildai-services/guildai~zendesk';
|
|
164
167
|
|
|
165
168
|
// Utilities
|
|
166
|
-
import { pick, progressLogNotifyEvent } from '@guildai/agents-sdk';
|
|
169
|
+
import { pick, omit, progressLogNotifyEvent } from '@guildai/agents-sdk';
|
|
167
170
|
|
|
168
171
|
// Advanced (for compiled agents with LLM tool loops)
|
|
169
172
|
import { delegatedCallsOf, asToolResultContent } from '@guildai/agents-sdk';
|
|
@@ -176,19 +179,25 @@ import { z } from 'zod';
|
|
|
176
179
|
|
|
177
180
|
Service tools are in separate `@guildai-services/*` packages. The runtime resolves them automatically.
|
|
178
181
|
|
|
179
|
-
| Service | Package
|
|
180
|
-
| -------------- |
|
|
181
|
-
| Azure DevOps | `@guildai-services/guildai~azure-devops`
|
|
182
|
-
| Bitbucket | `@guildai-services/guildai~bitbucket`
|
|
183
|
-
|
|
|
184
|
-
|
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
182
|
+
| Service | Package | Export | Tool Name Prefix |
|
|
183
|
+
| -------------- | ------------------------------------------ | -------------------- | ----------------- |
|
|
184
|
+
| Azure DevOps | `@guildai-services/guildai~azure-devops` | `azureDevOpsTools` | `azure_devops_` |
|
|
185
|
+
| Bitbucket | `@guildai-services/guildai~bitbucket` | `bitbucketTools` | `bitbucket_` |
|
|
186
|
+
| Confluence | `@guildai-services/guildai~confluence` | `confluenceTools` | `confluence_` |
|
|
187
|
+
| Cypress | `@guildai-services/guildai~cypress` | `cypressTools` | `cypress_` |
|
|
188
|
+
| Figma | `@guildai-services/guildai~figma` | `figmaTools` | `figma_` |
|
|
189
|
+
| GitHub | `@guildai-services/guildai~github` | `gitHubTools` | `github_` |
|
|
190
|
+
| Google Compute | `@guildai-services/guildai~google-compute` | `googleComputeTools` | `google_compute_` |
|
|
191
|
+
| Google Logging | `@guildai-services/guildai~google-logging` | `googleLoggingTools` | `google_logging_` |
|
|
192
|
+
| Guild | `@guildai/agents-sdk` | `guildTools` | `guild_` |
|
|
193
|
+
| Jira | `@guildai-services/guildai~jira` | `jiraTools` | `jira_` |
|
|
194
|
+
| Linear | `@guildai-services/guildai~linear` | `linearTools` | `linear_` |
|
|
195
|
+
| NewRelic | `@guildai-services/guildai~newrelic` | `newrelicTools` | `newrelic_` |
|
|
196
|
+
| Pipedream | `@guildai-services/guildai~pipedream` | `pipedreamTools` | `pipedream_` |
|
|
197
|
+
| Slack | `@guildai-services/guildai~slack` | `slackTools` | `slack_` |
|
|
198
|
+
| TestRail | `@guildai-services/guildai~testrail` | `testrailTools` | `testrail_` |
|
|
199
|
+
| User Interface | `@guildai/agents-sdk` | `userInterfaceTools` | `ui_` |
|
|
200
|
+
| Zendesk | `@guildai-services/guildai~zendesk` | `zendeskTools` | `zendesk_` |
|
|
192
201
|
|
|
193
202
|
### Tool Access via `task.tools.*`
|
|
194
203
|
|
|
@@ -223,12 +232,16 @@ await task.tools.guild_credentials_request({ service: 'GITHUB' });
|
|
|
223
232
|
|
|
224
233
|
### Task Properties
|
|
225
234
|
|
|
226
|
-
| Property | Description
|
|
227
|
-
| ---------------- |
|
|
228
|
-
| `task.sessionId` | Session ID for correlating operations
|
|
229
|
-
| `task.
|
|
230
|
-
| `task.
|
|
231
|
-
| `task.
|
|
235
|
+
| Property | Description |
|
|
236
|
+
| ---------------- | ------------------------------------------------------------------------------------------- |
|
|
237
|
+
| `task.sessionId` | Session ID for correlating operations |
|
|
238
|
+
| `task.tools` | Primary API for calling all tools |
|
|
239
|
+
| `task.llm` | LLM service — call `task.llm.generateText({ messages, system, tools })` for AI model access |
|
|
240
|
+
| `task.console` | Debug logging (`task.console.debug(...)`, `.info(...)`, `.warn(...)`, `.error(...)`) |
|
|
241
|
+
| `task.save()` | Persist agent state (self-managed state agents only) |
|
|
242
|
+
| `task.restore()` | Retrieve previously saved state (self-managed state agents only) |
|
|
243
|
+
| `task.guild` | **Deprecated** — use `task.tools.guild_*` instead |
|
|
244
|
+
| `task.ui` | **Deprecated** — use `task.tools.ui_*` instead |
|
|
232
245
|
|
|
233
246
|
---
|
|
234
247
|
|
|
@@ -343,7 +356,7 @@ export default agent({
|
|
|
343
356
|
|
|
344
357
|
- `run()` returns the OUTPUT directly (not wrapped in `{ type: "output", output: ... }`)
|
|
345
358
|
- The runtime handles continuations — you can `await` tool calls inline
|
|
346
|
-
-
|
|
359
|
+
- `"use agent"` directive at top of file is optional (the Babel compiler recognizes it but strips it)
|
|
347
360
|
- No `identifier` field needed
|
|
348
361
|
|
|
349
362
|
### 3. Self-Managed State Agent (`start()`/`onToolResults()`)
|
|
@@ -477,7 +490,7 @@ import {
|
|
|
477
490
|
} from '@guildai/agents-sdk';
|
|
478
491
|
import { gitHubTools } from '@guildai-services/guildai~github';
|
|
479
492
|
import { slackTools } from '@guildai-services/guildai~slack';
|
|
480
|
-
import {
|
|
493
|
+
import type { ModelMessage } from 'ai';
|
|
481
494
|
import { z } from 'zod';
|
|
482
495
|
|
|
483
496
|
const tools = { ...gitHubTools, ...slackTools, ...userInterfaceTools };
|
|
@@ -488,15 +501,12 @@ const llmTools = { ...gitHubTools }; // LLM gets execute access to these
|
|
|
488
501
|
const agentTools = { ...slackTools, ...userInterfaceTools }; // These get delegated
|
|
489
502
|
|
|
490
503
|
async function start(input, task: Task<Tools>) {
|
|
491
|
-
const model = await task.llm.model();
|
|
492
504
|
const messages: ModelMessage[] = [{ role: 'user', content: input.text }];
|
|
493
505
|
|
|
494
|
-
const result = await generateText({
|
|
495
|
-
model,
|
|
506
|
+
const result = await task.llm.generateText({
|
|
496
507
|
system: 'You are a helpful assistant.',
|
|
497
508
|
messages,
|
|
498
509
|
tools: llmTools, // Only give LLM the tools it can execute
|
|
499
|
-
maxSteps: 10,
|
|
500
510
|
});
|
|
501
511
|
|
|
502
512
|
// Check for delegated (unexecuted) tool calls
|
|
@@ -528,6 +538,7 @@ async function onToolResults(
|
|
|
528
538
|
|
|
529
539
|
**Key utilities:**
|
|
530
540
|
|
|
541
|
+
- `task.llm.generateText({ messages, system, tools })` — call the LLM with automatic authentication and provider selection. The runtime handles model selection and credential injection.
|
|
531
542
|
- `delegatedCallsOf<Tools>(content)` — extracts unexecuted tool calls from `generateText` results that need runtime delegation
|
|
532
543
|
- `asToolResultContent(results)` — converts `TypedToolResult[]` into LLM message format for conversation history
|
|
533
544
|
|
|
@@ -575,13 +586,20 @@ await task.tools.slack_chat_post_message({
|
|
|
575
586
|
|
|
576
587
|
**CRITICAL: Only use tool names listed below.** If a tool isn't listed here, it doesn't exist. Do not guess tool names based on API patterns.
|
|
577
588
|
|
|
578
|
-
Use `pick()` to select specific tools:
|
|
589
|
+
Use `pick()` to select specific tools, or `omit()` to exclude specific tools:
|
|
579
590
|
|
|
580
591
|
```typescript
|
|
592
|
+
// Include only specific tools
|
|
581
593
|
const tools = {
|
|
582
594
|
...pick(gitHubTools, ['github_repos_get', 'github_pulls_list']),
|
|
583
595
|
...guildTools,
|
|
584
596
|
};
|
|
597
|
+
|
|
598
|
+
// Include all tools except specific ones
|
|
599
|
+
const tools = {
|
|
600
|
+
...omit(gitHubTools, ['github_repos_delete', 'github_repos_update']),
|
|
601
|
+
...guildTools,
|
|
602
|
+
};
|
|
585
603
|
```
|
|
586
604
|
|
|
587
605
|
#### Azure DevOps (`azure_devops_` prefix, 122 tools)
|
|
@@ -1676,12 +1694,12 @@ export default agent({ run: async (input, task) => { ... } })
|
|
|
1676
1694
|
## Versioning
|
|
1677
1695
|
|
|
1678
1696
|
- Use semver: `1.0.0` → `1.0.1` (patch), `1.1.0` (minor), `2.0.0` (breaking)
|
|
1679
|
-
-
|
|
1680
|
-
-
|
|
1697
|
+
- Use `--bump [patch|minor|major]` with `guild agent save` to auto-bump `package.json` version
|
|
1698
|
+
- Or bump manually in `package.json` before saving
|
|
1681
1699
|
|
|
1682
1700
|
## File Structure
|
|
1683
1701
|
|
|
1684
|
-
After `guild agent
|
|
1702
|
+
After `guild agent init`:
|
|
1685
1703
|
|
|
1686
1704
|
```
|
|
1687
1705
|
my-agent/
|
|
@@ -1705,23 +1723,28 @@ my-agent/
|
|
|
1705
1723
|
```bash
|
|
1706
1724
|
guild setup # Install coding assistant skills
|
|
1707
1725
|
guild setup --claude-md # Also create CLAUDE.md template
|
|
1708
|
-
guild agent
|
|
1709
|
-
guild agent
|
|
1710
|
-
guild agent init # Initialize local workspace
|
|
1726
|
+
guild agent init # Create and initialize a new agent
|
|
1727
|
+
guild agent init --name <name> --template LLM # Create with specific name and template
|
|
1711
1728
|
guild agent init --fork <agent-id> # Fork existing agent
|
|
1712
1729
|
guild agent pull # Pull remote changes
|
|
1713
1730
|
guild agent save # Push commits and create a draft version
|
|
1714
1731
|
guild agent save -A --message "description" # Stage+commit+push in one step
|
|
1715
1732
|
guild agent save --message "v1.0" --wait --publish # Save + validate + publish
|
|
1733
|
+
guild agent save --bump minor --message "v1.1" # Auto-bump version before saving
|
|
1716
1734
|
guild agent test # Interactive test
|
|
1717
1735
|
guild agent test --ephemeral # Ephemeral test
|
|
1718
1736
|
guild agent chat "Hello" # Test with input
|
|
1719
1737
|
guild agent get [agent-id] # View agent info
|
|
1738
|
+
guild agent list # List agents
|
|
1739
|
+
guild agent list --search "github" --published # Search published agents
|
|
1740
|
+
guild agent search <query> # Search published agents
|
|
1720
1741
|
guild agent versions [agent-id] # Version history
|
|
1721
1742
|
guild agent clone <agent-id> # Clone existing agent
|
|
1743
|
+
guild agent fork <agent-id>:<version-id> # Fork a specific version as a new agent
|
|
1722
1744
|
guild agent publish # Publish a version
|
|
1723
1745
|
guild agent unpublish # Remove from catalog
|
|
1724
1746
|
guild agent update # Update agent metadata
|
|
1747
|
+
guild agent workspaces [agent-id] # List workspaces using an agent
|
|
1725
1748
|
guild agent tags list|add|remove|set # Manage agent tags
|
|
1726
1749
|
guild agent revalidate # Re-run validation
|
|
1727
1750
|
guild agent code [agent-id] # View agent source
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guildai/cli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.13",
|
|
4
4
|
"description": "Guild.ai CLI - Build, test, and deploy AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"url": "https://docs.guild.ai/support"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
+
"@inquirer/search": "^4.1.7",
|
|
58
59
|
"@napi-rs/canvas": "^0.1.85",
|
|
59
60
|
"@napi-rs/keyring": "^1.2.0",
|
|
60
61
|
"axios": "^1.13.2",
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
// Copyright 2026 Guild.ai
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import { Command } from 'commander';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
|
-
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
6
|
-
import { getGuildcoreUrl } from '../../lib/config.js';
|
|
7
|
-
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
8
|
-
import { pollAgentStatus } from '../../lib/polling.js';
|
|
9
|
-
import { createOutputWriter } from '../../lib/output.js';
|
|
10
|
-
const TEMPLATE_CHOICES = [
|
|
11
|
-
{
|
|
12
|
-
name: 'LLM - Simple language model agent (recommended)',
|
|
13
|
-
value: 'LLM',
|
|
14
|
-
short: 'LLM',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
name: 'Auto-managed state - Agent with automatic state management',
|
|
18
|
-
value: 'AUTO_MANAGED_STATE',
|
|
19
|
-
short: 'Auto-managed state',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'Blank - Start from scratch',
|
|
23
|
-
value: 'BLANK',
|
|
24
|
-
short: 'Blank',
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
function isInteractive() {
|
|
28
|
-
return process.stdin.isTTY === true;
|
|
29
|
-
}
|
|
30
|
-
async function promptForTemplate() {
|
|
31
|
-
const { template } = await inquirer.prompt([
|
|
32
|
-
{
|
|
33
|
-
type: 'list',
|
|
34
|
-
name: 'template',
|
|
35
|
-
message: 'Select agent template:',
|
|
36
|
-
choices: TEMPLATE_CHOICES,
|
|
37
|
-
default: 'LLM',
|
|
38
|
-
},
|
|
39
|
-
]);
|
|
40
|
-
return template;
|
|
41
|
-
}
|
|
42
|
-
export function createAgentCreateCommand() {
|
|
43
|
-
const cmd = new Command('create');
|
|
44
|
-
cmd
|
|
45
|
-
.description('Create a new agent')
|
|
46
|
-
.argument('<name>', 'Agent name')
|
|
47
|
-
.option('--description <text>', 'Agent description')
|
|
48
|
-
.option('--owner-id <id>', 'Owner ID (user or organization, defaults to current user)')
|
|
49
|
-
.option('--template <template>', 'Agent template (LLM, AUTO_MANAGED_STATE, BLANK)')
|
|
50
|
-
.option('--no-wait', 'Return immediately without waiting for initialization')
|
|
51
|
-
.action(async (name, options) => {
|
|
52
|
-
const output = createOutputWriter();
|
|
53
|
-
const baseUrl = getGuildcoreUrl();
|
|
54
|
-
const client = new GuildAPIClient({ baseUrl });
|
|
55
|
-
try {
|
|
56
|
-
// Get owner_id: use --owner-id option or default to current user
|
|
57
|
-
let ownerId = options.ownerId;
|
|
58
|
-
if (!ownerId) {
|
|
59
|
-
const me = (await client.get('/me'));
|
|
60
|
-
ownerId = me.id;
|
|
61
|
-
}
|
|
62
|
-
// Determine template: use --template option, prompt if interactive, or error
|
|
63
|
-
let template = options.template;
|
|
64
|
-
if (!template) {
|
|
65
|
-
if (isInteractive()) {
|
|
66
|
-
template = await promptForTemplate();
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
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`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// Normalize template to uppercase for case-insensitive matching
|
|
74
|
-
template = template.toUpperCase();
|
|
75
|
-
// Validate template
|
|
76
|
-
const validTemplates = [
|
|
77
|
-
'LLM',
|
|
78
|
-
'AUTO_MANAGED_STATE',
|
|
79
|
-
'BLANK',
|
|
80
|
-
];
|
|
81
|
-
if (!validTemplates.includes(template)) {
|
|
82
|
-
output.error(`Error: Invalid template '${template}'`, 'Valid templates:\n • LLM\n • AUTO_MANAGED_STATE\n • BLANK');
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
const response = await client.post('/agents', {
|
|
86
|
-
name,
|
|
87
|
-
owner_id: ownerId,
|
|
88
|
-
description: options.description || 'No description provided',
|
|
89
|
-
template,
|
|
90
|
-
});
|
|
91
|
-
// By default, poll until agent is ready (unless --no-wait is passed)
|
|
92
|
-
if (options.wait !== false) {
|
|
93
|
-
const pollResult = await pollAgentStatus(response.id, 'READY');
|
|
94
|
-
if (pollResult.success && pollResult.response) {
|
|
95
|
-
// Display the updated response with READY status
|
|
96
|
-
output.data(pollResult.response);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// Polling failed or timed out - this is an error
|
|
100
|
-
output.error('Agent initialization failed or timed out', `Check status: guild agent get ${response.id}`);
|
|
101
|
-
output.data(response);
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
// --no-wait: return immediately without polling
|
|
107
|
-
output.data(response);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
const formattedError = handleAxiosError(error);
|
|
112
|
-
// Provide specific error messages based on error code
|
|
113
|
-
if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
|
|
114
|
-
output.error('Not logged in.', 'Please authenticate first:\n guild auth login');
|
|
115
|
-
}
|
|
116
|
-
else if (formattedError.code === ErrorCodes.CONN_REFUSED) {
|
|
117
|
-
output.error('Cannot connect to Guild servers');
|
|
118
|
-
}
|
|
119
|
-
else if (formattedError.code === ErrorCodes.API_ERROR &&
|
|
120
|
-
formattedError.details.includes('already exists')) {
|
|
121
|
-
output.error(`Agent name '${name}' already exists.`, `Try a different name:\n guild agent create ${name}-v2`);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
// Generic error with details from server
|
|
125
|
-
output.error(`Failed to create agent: ${formattedError.details}`);
|
|
126
|
-
}
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
return cmd;
|
|
131
|
-
}
|
|
132
|
-
//# sourceMappingURL=create.js.map
|