@guildai/cli 0.3.17 → 0.4.0
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/chat.js +2 -1
- package/dist/commands/agent/clone.js +2 -28
- package/dist/commands/agent/init.js +35 -15
- package/dist/commands/agent/list.js +8 -2
- package/dist/commands/agent/save.js +7 -2
- package/dist/commands/agent/search.js +8 -2
- package/dist/commands/agent/test.js +2 -1
- package/dist/commands/agent/versions.js +8 -2
- package/dist/commands/agent/workspaces.d.ts +3 -0
- package/dist/commands/agent/workspaces.js +51 -0
- package/dist/commands/chat.js +2 -0
- package/dist/commands/workspace/agent/list.js +7 -6
- package/dist/commands/workspace/context/list.js +8 -2
- package/dist/commands/workspace/context/publish.js +1 -1
- package/dist/index.js +5 -0
- package/dist/lib/agent-helpers.js +2 -2
- package/dist/lib/did-you-mean.d.ts +25 -0
- package/dist/lib/did-you-mean.js +143 -0
- package/dist/lib/generated-types.d.ts +1 -1
- package/dist/lib/generated-types.js +2 -0
- package/dist/lib/npmrc.js +9 -1
- package/dist/lib/output.d.ts +18 -4
- package/dist/lib/output.js +108 -15
- package/dist/lib/stdin.d.ts +6 -0
- package/dist/lib/stdin.js +18 -0
- package/docs/CLI_WORKFLOW.md +28 -28
- package/docs/getting-started.md +1 -1
- package/docs/output-format.md +1 -1
- package/docs/skills/agent-dev.md +76 -37
- package/package.json +1 -1
|
@@ -19,7 +19,7 @@ import { runGit } from '../../lib/git.js';
|
|
|
19
19
|
import { suppressScrollbackClear } from '../../lib/alternate-screen.js';
|
|
20
20
|
import * as readline from 'readline';
|
|
21
21
|
import { pollForResponse } from '../../lib/session-polling.js';
|
|
22
|
-
import { readStdinAsJSON } from '../../lib/stdin.js';
|
|
22
|
+
import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
|
|
23
23
|
import { fetchSession, fetchSessionEvents } from '../../lib/session-resume.js';
|
|
24
24
|
// ESM equivalent of __dirname
|
|
25
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -266,6 +266,7 @@ export function createAgentChatCommand() {
|
|
|
266
266
|
else {
|
|
267
267
|
// Interactive mode: use shared ChatApp with full features
|
|
268
268
|
// (splash animation, task panel, status line, etc.)
|
|
269
|
+
ensureInteractiveStdin('guild agent chat');
|
|
269
270
|
const resumeCommand = 'guild agent chat';
|
|
270
271
|
// Handle --resume: fetch existing session + events
|
|
271
272
|
let resumeSession;
|
|
@@ -4,25 +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
9
|
import { createOutputWriter } from '../../lib/output.js';
|
|
11
|
-
async function promptForDirectory(suggestedDir) {
|
|
12
|
-
const rl = readline.createInterface({
|
|
13
|
-
input: process.stdin,
|
|
14
|
-
output: process.stdout,
|
|
15
|
-
});
|
|
16
|
-
return new Promise((resolve) => {
|
|
17
|
-
rl.question(`Target directory (default: ${suggestedDir}): `, (answer) => {
|
|
18
|
-
rl.close();
|
|
19
|
-
resolve(answer.trim() || suggestedDir);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
function isInteractive() {
|
|
24
|
-
return process.stdin.isTTY === true;
|
|
25
|
-
}
|
|
26
10
|
async function isDirectoryEmpty(dirPath) {
|
|
27
11
|
try {
|
|
28
12
|
const files = await fs.readdir(dirPath);
|
|
@@ -51,18 +35,8 @@ export function createAgentCloneCommand() {
|
|
|
51
35
|
process.exit(1);
|
|
52
36
|
}
|
|
53
37
|
output.progress(`✓ Fetched agent '${agent.name}' (${agent.id})`);
|
|
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
|
-
output.error('Error: Target directory required in non-interactive mode', `Provide a directory:\n guild agent clone ${agentId} --directory ./my-agent\n\nOr run interactively to be prompted.`);
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
38
|
+
// Determine target directory (default to agent name, like git clone)
|
|
39
|
+
const targetDir = options.directory || agent.name;
|
|
66
40
|
// Check if directory exists and is not empty
|
|
67
41
|
const dirExists = await fs
|
|
68
42
|
.access(targetDir)
|
|
@@ -64,15 +64,23 @@ function isInteractive() {
|
|
|
64
64
|
export function createAgentInitCommand() {
|
|
65
65
|
const cmd = new Command('init');
|
|
66
66
|
cmd
|
|
67
|
-
.description('Initialize
|
|
67
|
+
.description('Initialize a directory as a Guild agent')
|
|
68
68
|
.option('--name <name>', 'Agent name')
|
|
69
69
|
.option('--template <template>', 'Agent template (LLM, AUTO_MANAGED_STATE, BLANK)')
|
|
70
70
|
.option('--fork <agent-id>', 'Fork from existing agent')
|
|
71
71
|
.option('--owner <owner-id>', 'Owner account (user or organization ID)')
|
|
72
|
+
.option('--directory <path>', 'Directory to initialize (created if needed)')
|
|
72
73
|
.option('--force', 'Overwrite existing guild.json', false)
|
|
73
74
|
.action(async (options) => {
|
|
74
|
-
|
|
75
|
-
const
|
|
75
|
+
// Resolve target directory
|
|
76
|
+
const targetDir = options.directory
|
|
77
|
+
? path.resolve(process.cwd(), options.directory)
|
|
78
|
+
: process.cwd();
|
|
79
|
+
// Create directory if it doesn't exist
|
|
80
|
+
if (options.directory) {
|
|
81
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
const guildJsonPath = path.join(targetDir, 'guild.json');
|
|
76
84
|
try {
|
|
77
85
|
// Check if already initialized
|
|
78
86
|
const guildJsonExists = await fs
|
|
@@ -93,7 +101,7 @@ export function createAgentInitCommand() {
|
|
|
93
101
|
if (!agentName) {
|
|
94
102
|
if (isInteractive()) {
|
|
95
103
|
// Use slugified directory name as default
|
|
96
|
-
const dirName = path.basename(
|
|
104
|
+
const dirName = path.basename(targetDir);
|
|
97
105
|
const defaultName = slugify(dirName);
|
|
98
106
|
agentName = await promptForName(defaultName);
|
|
99
107
|
if (!agentName) {
|
|
@@ -176,13 +184,13 @@ export function createAgentInitCommand() {
|
|
|
176
184
|
format.detail(`Agent: ${agent.name} (${agent.id})`);
|
|
177
185
|
format.detail(`Owner: ${owner.name}`);
|
|
178
186
|
// Step 2: Initialize git repository if needed
|
|
179
|
-
const gitDir = path.join(
|
|
187
|
+
const gitDir = path.join(targetDir, '.git');
|
|
180
188
|
const gitExists = await fs
|
|
181
189
|
.access(gitDir)
|
|
182
190
|
.then(() => true)
|
|
183
191
|
.catch(() => false);
|
|
184
192
|
if (!gitExists) {
|
|
185
|
-
await runGit(['init'], { cwd });
|
|
193
|
+
await runGit(['init'], { cwd: targetDir });
|
|
186
194
|
steps.succeed('Initialize git repository');
|
|
187
195
|
}
|
|
188
196
|
else {
|
|
@@ -192,13 +200,17 @@ export function createAgentInitCommand() {
|
|
|
192
200
|
// Step 3: Configure git remote
|
|
193
201
|
try {
|
|
194
202
|
// Check if remote 'origin' exists
|
|
195
|
-
await runGit(['remote', 'get-url', 'origin'], { cwd });
|
|
203
|
+
await runGit(['remote', 'get-url', 'origin'], { cwd: targetDir });
|
|
196
204
|
// Remote exists, update it
|
|
197
|
-
await runGit(['remote', 'set-url', 'origin', agent.git_url], {
|
|
205
|
+
await runGit(['remote', 'set-url', 'origin', agent.git_url], {
|
|
206
|
+
cwd: targetDir,
|
|
207
|
+
});
|
|
198
208
|
}
|
|
199
209
|
catch {
|
|
200
210
|
// Remote doesn't exist, add it
|
|
201
|
-
await runGit(['remote', 'add', 'origin', agent.git_url], {
|
|
211
|
+
await runGit(['remote', 'add', 'origin', agent.git_url], {
|
|
212
|
+
cwd: targetDir,
|
|
213
|
+
});
|
|
202
214
|
}
|
|
203
215
|
steps.succeed('Configure git remote');
|
|
204
216
|
format.detail(`Remote: ${agent.git_url}`);
|
|
@@ -242,7 +254,7 @@ export function createAgentInitCommand() {
|
|
|
242
254
|
console.error(` guild agent get ${agent.id}`);
|
|
243
255
|
console.error('');
|
|
244
256
|
console.error('Or wait and try pulling the scaffolding manually:');
|
|
245
|
-
console.error(` cd ${
|
|
257
|
+
console.error(` cd ${targetDir}`);
|
|
246
258
|
console.error(' git pull origin main');
|
|
247
259
|
process.exit(1);
|
|
248
260
|
}
|
|
@@ -258,7 +270,7 @@ export function createAgentInitCommand() {
|
|
|
258
270
|
}
|
|
259
271
|
for (let attempt = 1; attempt <= pullMaxAttempts; attempt++) {
|
|
260
272
|
try {
|
|
261
|
-
await runGit(['pull', authenticatedGitUrl, 'main'], { cwd });
|
|
273
|
+
await runGit(['pull', authenticatedGitUrl, 'main'], { cwd: targetDir });
|
|
262
274
|
steps.succeed('Pull scaffolding from remote');
|
|
263
275
|
break;
|
|
264
276
|
}
|
|
@@ -288,7 +300,7 @@ export function createAgentInitCommand() {
|
|
|
288
300
|
await fs.writeFile(guildJsonPath, JSON.stringify(guildConfig, null, 2) + '\n', 'utf-8');
|
|
289
301
|
steps.succeed('Create guild.json');
|
|
290
302
|
// Add guild.json to .gitignore if not already present
|
|
291
|
-
const gitignorePath = path.join(
|
|
303
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
292
304
|
try {
|
|
293
305
|
const gitignoreContent = await fs.readFile(gitignorePath, 'utf-8');
|
|
294
306
|
if (!gitignoreContent.includes('guild.json')) {
|
|
@@ -303,9 +315,17 @@ export function createAgentInitCommand() {
|
|
|
303
315
|
steps.complete('Agent initialized successfully');
|
|
304
316
|
// Display next steps
|
|
305
317
|
format.section('Next steps:');
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
318
|
+
if (options.directory) {
|
|
319
|
+
format.detail(`1. cd ${targetDir}`);
|
|
320
|
+
format.detail('2. Edit agent.ts to implement your agent logic');
|
|
321
|
+
format.detail(`3. Run 'guild agent save --message "Initial implementation"'`);
|
|
322
|
+
format.detail(`4. Run 'guild agent test' to test your agent`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
format.detail('1. Edit agent.ts to implement your agent logic');
|
|
326
|
+
format.detail(`2. Run 'guild agent save --message "Initial implementation"'`);
|
|
327
|
+
format.detail(`3. Run 'guild agent test' to test your agent`);
|
|
328
|
+
}
|
|
309
329
|
}
|
|
310
330
|
catch (error) {
|
|
311
331
|
if (error instanceof GitError) {
|
|
@@ -3,7 +3,8 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
4
4
|
import { getAuthToken } from '../../lib/auth.js';
|
|
5
5
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
6
|
-
import {
|
|
6
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
7
|
+
import { createOutputWriter, formatAgentTable } from '../../lib/output.js';
|
|
7
8
|
const SORT_MAP = {
|
|
8
9
|
updated: 'updated_at',
|
|
9
10
|
newest: 'created_at',
|
|
@@ -42,7 +43,12 @@ export function createAgentListCommand() {
|
|
|
42
43
|
params.append('sort_by', sortField);
|
|
43
44
|
}
|
|
44
45
|
const response = await client.get(`/agents?${params.toString()}`);
|
|
45
|
-
|
|
46
|
+
if (getOutputMode() === 'json') {
|
|
47
|
+
output.data(response);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
formatAgentTable(response.items, response.pagination);
|
|
51
|
+
}
|
|
46
52
|
}
|
|
47
53
|
catch (error) {
|
|
48
54
|
const formattedError = handleAxiosError(error);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
4
4
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
5
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
5
6
|
import { createOutputWriter } from '../../lib/output.js';
|
|
6
7
|
import * as fs from 'fs/promises';
|
|
7
8
|
import * as path from 'path';
|
|
@@ -254,8 +255,12 @@ export function createAgentSaveCommand() {
|
|
|
254
255
|
output.progress('To publish this version:');
|
|
255
256
|
output.progress(' guild agent save --message "..." --publish');
|
|
256
257
|
}
|
|
257
|
-
// Output JSON to stdout
|
|
258
|
-
|
|
258
|
+
// Output JSON to stdout only in --json mode.
|
|
259
|
+
// In interactive mode the progress messages above already
|
|
260
|
+
// show version details; dumping raw JSON is noise.
|
|
261
|
+
if (getOutputMode() === 'json') {
|
|
262
|
+
output.data({ version });
|
|
263
|
+
}
|
|
259
264
|
}
|
|
260
265
|
catch (error) {
|
|
261
266
|
if (error instanceof GitError) {
|
|
@@ -3,7 +3,8 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
4
4
|
import { getAuthToken } from '../../lib/auth.js';
|
|
5
5
|
import { handleAxiosError } from '../../lib/errors.js';
|
|
6
|
-
import {
|
|
6
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
7
|
+
import { createOutputWriter, formatAgentTable } from '../../lib/output.js';
|
|
7
8
|
const SORT_MAP = {
|
|
8
9
|
updated: 'updated_at',
|
|
9
10
|
newest: 'created_at',
|
|
@@ -40,7 +41,12 @@ export function createAgentSearchCommand() {
|
|
|
40
41
|
params.append('sort_by', sortField);
|
|
41
42
|
}
|
|
42
43
|
const response = await client.get(`/agents?${params.toString()}`);
|
|
43
|
-
|
|
44
|
+
if (getOutputMode() === 'json') {
|
|
45
|
+
output.data(response);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
formatAgentTable(response.items, response.pagination);
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
catch (error) {
|
|
46
52
|
const formattedError = handleAxiosError(error);
|
|
@@ -14,7 +14,7 @@ import { showBetaGuidance } from '../../lib/auth.js';
|
|
|
14
14
|
import * as readline from 'readline';
|
|
15
15
|
import { isQuietMode, getOutputMode } from '../../lib/output-mode.js';
|
|
16
16
|
import { pollForResponse } from '../../lib/session-polling.js';
|
|
17
|
-
import { readStdinAsJSON } from '../../lib/stdin.js';
|
|
17
|
+
import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
|
|
18
18
|
import { loadLocalConfig, getWorkspaceId } from '../../lib/guild-config.js';
|
|
19
19
|
import { runGit, GitError, formatGitError } from '../../lib/git.js';
|
|
20
20
|
import { pollUntilComplete } from '../../lib/polling.js';
|
|
@@ -415,6 +415,7 @@ export function createAgentTestCommand() {
|
|
|
415
415
|
process.exit(0);
|
|
416
416
|
}
|
|
417
417
|
// Interactive mode: use shared ChatApp (spinner, progress, prompt)
|
|
418
|
+
ensureInteractiveStdin('guild agent test');
|
|
418
419
|
const resumeCommand = 'guild agent test';
|
|
419
420
|
const isInteractive = getOutputMode() === 'interactive' && !isQuietMode();
|
|
420
421
|
if (isInteractive) {
|
|
@@ -4,7 +4,8 @@ import { GuildAPIClient } from '../../lib/api-client.js';
|
|
|
4
4
|
import { getGuildcoreUrl } from '../../lib/config.js';
|
|
5
5
|
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
6
6
|
import { getAgentId } from '../../lib/agent-helpers.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
8
|
+
import { createOutputWriter, formatVersionTable } from '../../lib/output.js';
|
|
8
9
|
export function createAgentVersionsCommand() {
|
|
9
10
|
const cmd = new Command('versions');
|
|
10
11
|
cmd
|
|
@@ -22,7 +23,12 @@ export function createAgentVersionsCommand() {
|
|
|
22
23
|
const offset = parseInt(options.offset, 10);
|
|
23
24
|
try {
|
|
24
25
|
const result = await client.get(`/agents/${agentId}/versions?limit=${limit}&offset=${offset}`);
|
|
25
|
-
|
|
26
|
+
if (getOutputMode() === 'json') {
|
|
27
|
+
output.data(result);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
formatVersionTable(result.items, result.pagination);
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
catch (error) {
|
|
28
34
|
const formattedError = handleAxiosError(error);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Copyright (c) 2026 Guild.ai All Rights Reserved
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
4
|
+
import { getGuildcoreUrl } from '../../lib/config.js';
|
|
5
|
+
import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
6
|
+
import { getAgentId } from '../../lib/agent-helpers.js';
|
|
7
|
+
import { createOutputWriter, formatWorkspaceTable } from '../../lib/output.js';
|
|
8
|
+
import { getOutputMode } from '../../lib/output-mode.js';
|
|
9
|
+
export function createAgentWorkspacesCommand() {
|
|
10
|
+
const cmd = new Command('workspaces');
|
|
11
|
+
cmd
|
|
12
|
+
.description('List workspaces that use an agent')
|
|
13
|
+
.argument('[identifier]', 'Agent ID or full name (e.g., owner/agent-name)')
|
|
14
|
+
.option('--limit <number>', 'Maximum number of workspaces to return', '20')
|
|
15
|
+
.option('--offset <number>', 'Number of workspaces to skip', '0')
|
|
16
|
+
.action(async (agentIdArg, options) => {
|
|
17
|
+
const output = createOutputWriter();
|
|
18
|
+
const { agentId } = await getAgentId(agentIdArg);
|
|
19
|
+
const baseUrl = getGuildcoreUrl();
|
|
20
|
+
const client = new GuildAPIClient({ baseUrl });
|
|
21
|
+
const limit = parseInt(options.limit, 10);
|
|
22
|
+
const offset = parseInt(options.offset, 10);
|
|
23
|
+
try {
|
|
24
|
+
const result = await client.get(`/agents/${agentId}/workspaces?limit=${limit}&offset=${offset}`);
|
|
25
|
+
if (getOutputMode() === 'json') {
|
|
26
|
+
output.data(result);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
formatWorkspaceTable(result.items, result.pagination);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const formattedError = handleAxiosError(error);
|
|
34
|
+
if (formattedError.code === ErrorCodes.AUTH_REQUIRED) {
|
|
35
|
+
output.error('Not logged in.', 'Please authenticate first:\n guild auth login');
|
|
36
|
+
}
|
|
37
|
+
else if (formattedError.code === ErrorCodes.CONN_REFUSED) {
|
|
38
|
+
output.error('Cannot connect to Guild servers');
|
|
39
|
+
}
|
|
40
|
+
else if (formattedError.code === ErrorCodes.NOT_FOUND) {
|
|
41
|
+
output.error(`Agent not found: ${agentId}`, 'Check the agent ID:\n guild agent list');
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
output.error(`Failed to list workspaces: ${formattedError.details}`);
|
|
45
|
+
}
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return cmd;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=workspaces.js.map
|
package/dist/commands/chat.js
CHANGED
|
@@ -16,6 +16,7 @@ import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayNam
|
|
|
16
16
|
import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
|
|
17
17
|
import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
|
|
18
18
|
import { getWorkspaceId } from '../lib/guild-config.js';
|
|
19
|
+
import { ensureInteractiveStdin } from '../lib/stdin.js';
|
|
19
20
|
import { brand, BRAND_COLOR, code as codeColor } from '../lib/colors.js';
|
|
20
21
|
import { SplashAnimation } from '../components/SplashAnimation.js';
|
|
21
22
|
import { LOADING_TIMINGS } from '../lib/loading-messages.js';
|
|
@@ -1062,6 +1063,7 @@ export function createChatCommand() {
|
|
|
1062
1063
|
}
|
|
1063
1064
|
else {
|
|
1064
1065
|
// Interactive mode - check auth first, then render UI with splash animation
|
|
1066
|
+
ensureInteractiveStdin('guild chat');
|
|
1065
1067
|
await ensureAuthenticated();
|
|
1066
1068
|
// Build the resume command string for exit hints
|
|
1067
1069
|
let resumeCommand = 'guild chat';
|
|
@@ -4,7 +4,8 @@ import { GuildAPIClient } from '../../../lib/api-client.js';
|
|
|
4
4
|
import { getAuthToken } from '../../../lib/auth.js';
|
|
5
5
|
import { getWorkspaceId } from '../../../lib/guild-config.js';
|
|
6
6
|
import { handleAxiosError, ErrorCodes } from '../../../lib/errors.js';
|
|
7
|
-
import { createOutputWriter } from '../../../lib/output.js';
|
|
7
|
+
import { createOutputWriter, formatWorkspaceAgentTable } from '../../../lib/output.js';
|
|
8
|
+
import { getOutputMode } from '../../../lib/output-mode.js';
|
|
8
9
|
export function createWorkspaceAgentListCommand() {
|
|
9
10
|
const cmd = new Command('list');
|
|
10
11
|
cmd
|
|
@@ -36,12 +37,12 @@ export function createWorkspaceAgentListCommand() {
|
|
|
36
37
|
params.append('limit', options.limit);
|
|
37
38
|
params.append('offset', options.offset);
|
|
38
39
|
const response = await client.get(`/workspaces/${workspaceId}/workspace_agents?${params.toString()}`);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if (getOutputMode() === 'json') {
|
|
41
|
+
output.data(response);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
formatWorkspaceAgentTable(response.items);
|
|
43
45
|
}
|
|
44
|
-
output.data(response);
|
|
45
46
|
}
|
|
46
47
|
catch (error) {
|
|
47
48
|
const formattedError = handleAxiosError(error);
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { GuildAPIClient } from '../../../lib/api-client.js';
|
|
4
4
|
import { handleAxiosError, ErrorCodes } from '../../../lib/errors.js';
|
|
5
|
-
import {
|
|
5
|
+
import { getOutputMode } from '../../../lib/output-mode.js';
|
|
6
|
+
import { createOutputWriter, formatContextTable } from '../../../lib/output.js';
|
|
6
7
|
export function createWorkspaceContextListCommand() {
|
|
7
8
|
const cmd = new Command('list');
|
|
8
9
|
cmd
|
|
@@ -18,7 +19,12 @@ export function createWorkspaceContextListCommand() {
|
|
|
18
19
|
params.append('limit', options.limit);
|
|
19
20
|
params.append('offset', options.offset);
|
|
20
21
|
const response = await client.get(`/workspaces/${workspaceId}/contexts?${params.toString()}`);
|
|
21
|
-
|
|
22
|
+
if (getOutputMode() === 'json') {
|
|
23
|
+
output.data(response);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
formatContextTable(response.items, response.pagination);
|
|
27
|
+
}
|
|
22
28
|
}
|
|
23
29
|
catch (error) {
|
|
24
30
|
const formattedError = handleAxiosError(error);
|
|
@@ -13,7 +13,7 @@ export function createWorkspaceContextPublishCommand() {
|
|
|
13
13
|
const output = createOutputWriter();
|
|
14
14
|
try {
|
|
15
15
|
const client = new GuildAPIClient();
|
|
16
|
-
await client.patch(`/
|
|
16
|
+
await client.patch(`/contexts/${contextId}`, {
|
|
17
17
|
status: 'PUBLISHED',
|
|
18
18
|
});
|
|
19
19
|
output.success(`Context ${contextId} published successfully`);
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ import { createAgentUnpublishCommand } from './commands/agent/unpublish.js';
|
|
|
28
28
|
import { createAgentRevalidateCommand } from './commands/agent/revalidate.js';
|
|
29
29
|
import { createAgentSearchCommand } from './commands/agent/search.js';
|
|
30
30
|
import { createAgentOwnersCommand } from './commands/agent/owners.js';
|
|
31
|
+
import { createAgentWorkspacesCommand } from './commands/agent/workspaces.js';
|
|
31
32
|
import { createAgentTagsListCommand } from './commands/agent/tags/list.js';
|
|
32
33
|
import { createAgentTagsAddCommand } from './commands/agent/tags/add.js';
|
|
33
34
|
import { createAgentTagsRemoveCommand } from './commands/agent/tags/remove.js';
|
|
@@ -66,6 +67,7 @@ import { createDoctorCommand } from './commands/doctor.js';
|
|
|
66
67
|
import { createSetupCommand } from './commands/setup.js';
|
|
67
68
|
import { showSplashScreen, shouldShowSplash } from './lib/splash.js';
|
|
68
69
|
import { checkForUpdate } from './lib/update-check.js';
|
|
70
|
+
import { setupUnknownCommandSuggestions } from './lib/did-you-mean.js';
|
|
69
71
|
import chalk from 'chalk';
|
|
70
72
|
// ESM equivalent of __dirname
|
|
71
73
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -131,6 +133,7 @@ agentCmd.addCommand(createAgentUnpublishCommand());
|
|
|
131
133
|
agentCmd.addCommand(createAgentRevalidateCommand());
|
|
132
134
|
agentCmd.addCommand(createAgentSearchCommand());
|
|
133
135
|
agentCmd.addCommand(createAgentOwnersCommand());
|
|
136
|
+
agentCmd.addCommand(createAgentWorkspacesCommand());
|
|
134
137
|
// Agent tags subcommand group
|
|
135
138
|
const tagsCmd = agentCmd.command('tags').description('Manage agent tags');
|
|
136
139
|
tagsCmd.addCommand(createAgentTagsListCommand());
|
|
@@ -214,6 +217,8 @@ configCmd.addCommand(createConfigListCommand());
|
|
|
214
217
|
configCmd.addCommand(createConfigGetCommand());
|
|
215
218
|
configCmd.addCommand(createConfigSetCommand());
|
|
216
219
|
configCmd.addCommand(createConfigPathCommand());
|
|
220
|
+
// Set up cross-level "Did you mean?" suggestions for unknown commands
|
|
221
|
+
setupUnknownCommandSuggestions(program);
|
|
217
222
|
// Parse arguments
|
|
218
223
|
program.parse(process.argv);
|
|
219
224
|
//# sourceMappingURL=index.js.map
|
|
@@ -66,8 +66,8 @@ export async function readAgentFiles(cwd) {
|
|
|
66
66
|
const content = await fs.readFile(fullPath, 'utf-8');
|
|
67
67
|
files.push({ path: filePath, content });
|
|
68
68
|
}
|
|
69
|
-
const
|
|
70
|
-
const missing = REQUIRED_AGENT_FILES.filter((
|
|
69
|
+
const filePaths = files.map((f) => f.path);
|
|
70
|
+
const missing = REQUIRED_AGENT_FILES.filter((req) => !filePaths.some((fp) => fp === req || fp.endsWith(`/${req}`)));
|
|
71
71
|
if (missing.length > 0) {
|
|
72
72
|
throw new Error(`Missing required files: ${missing.join(', ')}`);
|
|
73
73
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface CommandEntry {
|
|
3
|
+
path: string;
|
|
4
|
+
description: string;
|
|
5
|
+
leafName: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Recursively collects all commands from the command tree.
|
|
9
|
+
* Includes both leaf commands and command groups (parents with subcommands).
|
|
10
|
+
* Aliases are added as separate entries.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getAllCommands(cmd: Command, prefix?: string): CommandEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* Standard Levenshtein distance between two strings.
|
|
15
|
+
*/
|
|
16
|
+
export declare function levenshtein(a: string, b: string): number;
|
|
17
|
+
/**
|
|
18
|
+
* Sets up cross-level "Did you mean?" suggestions for unknown commands.
|
|
19
|
+
*
|
|
20
|
+
* Disables Commander's built-in same-level suggestions and intercepts
|
|
21
|
+
* "unknown command" errors to search the entire command tree.
|
|
22
|
+
*/
|
|
23
|
+
export declare function setupUnknownCommandSuggestions(program: Command): void;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=did-you-mean.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Copyright (c) 2026 Guild.ai All Rights Reserved
|
|
2
|
+
/**
|
|
3
|
+
* Recursively collects all commands from the command tree.
|
|
4
|
+
* Includes both leaf commands and command groups (parents with subcommands).
|
|
5
|
+
* Aliases are added as separate entries.
|
|
6
|
+
*/
|
|
7
|
+
export function getAllCommands(cmd, prefix = '') {
|
|
8
|
+
const results = [];
|
|
9
|
+
for (const sub of cmd.commands) {
|
|
10
|
+
const name = sub.name();
|
|
11
|
+
const cmdPath = prefix ? `${prefix} ${name}` : name;
|
|
12
|
+
const description = sub.description();
|
|
13
|
+
const hasChildren = sub.commands.length > 0;
|
|
14
|
+
// Always include this command (even if it has subcommands)
|
|
15
|
+
results.push({ path: cmdPath, description, leafName: name });
|
|
16
|
+
// If this command has subcommands, also recurse into them
|
|
17
|
+
if (hasChildren) {
|
|
18
|
+
results.push(...getAllCommands(sub, cmdPath));
|
|
19
|
+
}
|
|
20
|
+
// Add aliases as separate entries
|
|
21
|
+
for (const alias of sub.aliases()) {
|
|
22
|
+
const aliasPath = prefix ? `${prefix} ${alias}` : alias;
|
|
23
|
+
results.push({ path: aliasPath, description, leafName: alias });
|
|
24
|
+
if (hasChildren) {
|
|
25
|
+
results.push(...getAllCommands(sub, aliasPath));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Standard Levenshtein distance between two strings.
|
|
33
|
+
*/
|
|
34
|
+
export function levenshtein(a, b) {
|
|
35
|
+
const m = a.length;
|
|
36
|
+
const n = b.length;
|
|
37
|
+
// Use a single-row DP approach for space efficiency
|
|
38
|
+
const row = Array.from({ length: n + 1 }, (_, i) => i);
|
|
39
|
+
for (let i = 1; i <= m; i++) {
|
|
40
|
+
let prev = i - 1;
|
|
41
|
+
row[0] = i;
|
|
42
|
+
for (let j = 1; j <= n; j++) {
|
|
43
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
44
|
+
const val = Math.min(row[j] + 1, // deletion
|
|
45
|
+
row[j - 1] + 1, // insertion
|
|
46
|
+
prev + cost // substitution
|
|
47
|
+
);
|
|
48
|
+
prev = row[j];
|
|
49
|
+
row[j] = val;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return row[n];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Recursively disables Commander's built-in suggestion on all commands.
|
|
56
|
+
*/
|
|
57
|
+
function disableBuiltinSuggestions(cmd) {
|
|
58
|
+
cmd.showSuggestionAfterError(false);
|
|
59
|
+
for (const sub of cmd.commands) {
|
|
60
|
+
disableBuiltinSuggestions(sub);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates the writeErr handler that intercepts "unknown command" errors
|
|
65
|
+
* and prints cross-level suggestions.
|
|
66
|
+
*/
|
|
67
|
+
function createWriteErrHandler(program) {
|
|
68
|
+
return (str) => {
|
|
69
|
+
// Always write the original error
|
|
70
|
+
process.stderr.write(str);
|
|
71
|
+
// Check if this is an "unknown command" error
|
|
72
|
+
const match = /unknown command '([^']+)'/.exec(str);
|
|
73
|
+
if (!match) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const unknown = match[1];
|
|
77
|
+
const allCommands = getAllCommands(program);
|
|
78
|
+
// Determine the typed parent context from process.argv
|
|
79
|
+
// e.g., "guild agent trigger" → typed args are ["agent", "trigger"]
|
|
80
|
+
const typedArgs = process.argv.slice(2);
|
|
81
|
+
const typedParent = typedArgs.slice(0, -1).join(' ');
|
|
82
|
+
// Score each command
|
|
83
|
+
const scored = allCommands.map((entry) => {
|
|
84
|
+
const leafName = entry.leafName;
|
|
85
|
+
// Exact leaf name match gets score 0
|
|
86
|
+
let score = leafName === unknown ? 0 : levenshtein(unknown, leafName);
|
|
87
|
+
// Context bonus: if command's parent matches typed parent, reduce score
|
|
88
|
+
const entryParts = entry.path.split(' ');
|
|
89
|
+
const entryParent = entryParts.slice(0, -1).join(' ');
|
|
90
|
+
if (entryParent === typedParent && score > 0) {
|
|
91
|
+
score -= 0.5;
|
|
92
|
+
}
|
|
93
|
+
return { ...entry, score };
|
|
94
|
+
});
|
|
95
|
+
// Filter poor matches
|
|
96
|
+
const threshold = Math.max(2, unknown.length / 2);
|
|
97
|
+
const filtered = scored.filter((s) => s.score <= threshold);
|
|
98
|
+
if (filtered.length === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Sort by score, deduplicate by path
|
|
102
|
+
filtered.sort((a, b) => a.score - b.score);
|
|
103
|
+
const seen = new Set();
|
|
104
|
+
const unique = filtered.filter((entry) => {
|
|
105
|
+
if (seen.has(entry.path)) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
seen.add(entry.path);
|
|
109
|
+
return true;
|
|
110
|
+
});
|
|
111
|
+
// Print top 5 suggestions
|
|
112
|
+
const top = unique.slice(0, 5);
|
|
113
|
+
process.stderr.write('\nDid you mean?\n');
|
|
114
|
+
for (const entry of top) {
|
|
115
|
+
const cmd = `guild ${entry.path}`;
|
|
116
|
+
const padding = ' '.repeat(Math.max(2, 30 - cmd.length));
|
|
117
|
+
process.stderr.write(` ${cmd}${padding}${entry.description}\n`);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Recursively configures writeErr on a command and all its subcommands.
|
|
123
|
+
*/
|
|
124
|
+
function configureOutputRecursively(cmd, writeErr) {
|
|
125
|
+
cmd.configureOutput({ writeErr });
|
|
126
|
+
for (const sub of cmd.commands) {
|
|
127
|
+
configureOutputRecursively(sub, writeErr);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Sets up cross-level "Did you mean?" suggestions for unknown commands.
|
|
132
|
+
*
|
|
133
|
+
* Disables Commander's built-in same-level suggestions and intercepts
|
|
134
|
+
* "unknown command" errors to search the entire command tree.
|
|
135
|
+
*/
|
|
136
|
+
export function setupUnknownCommandSuggestions(program) {
|
|
137
|
+
// Disable Commander's built-in suggestion on all commands
|
|
138
|
+
disableBuiltinSuggestions(program);
|
|
139
|
+
// Create a shared writeErr handler and apply it to all commands
|
|
140
|
+
const writeErr = createWriteErrHandler(program);
|
|
141
|
+
configureOutputRecursively(program, writeErr);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=did-you-mean.js.map
|