@guildai/cli 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/commands/agent/chat.d.ts +1 -0
  2. package/dist/commands/agent/chat.js +24 -1
  3. package/dist/commands/agent/init.js +1 -1
  4. package/dist/commands/agent/test.d.ts +1 -0
  5. package/dist/commands/agent/test.js +80 -28
  6. package/dist/commands/chat.d.ts +1 -0
  7. package/dist/commands/chat.js +178 -46
  8. package/dist/commands/integration/connect.js +1 -1
  9. package/dist/commands/integration/create.js +36 -36
  10. package/dist/commands/integration/operation/create.js +2 -1
  11. package/dist/commands/integration/operation/list.js +23 -15
  12. package/dist/commands/job/get-step.d.ts +3 -0
  13. package/dist/commands/job/{step-get.js → get-step.js} +3 -3
  14. package/dist/commands/mcp.js +2 -2
  15. package/dist/commands/session/create.js +1 -1
  16. package/dist/commands/session/events.js +16 -7
  17. package/dist/commands/session/list.js +1 -1
  18. package/dist/commands/session/send.js +1 -1
  19. package/dist/commands/workspace/agent/add.js +16 -3
  20. package/dist/commands/workspace/agent/list.js +14 -1
  21. package/dist/commands/workspace/agent/remove.js +14 -1
  22. package/dist/commands/workspace/clear.d.ts +3 -0
  23. package/dist/commands/workspace/clear.js +45 -0
  24. package/dist/commands/workspace/select.js +3 -1
  25. package/dist/index.js +58 -8
  26. package/dist/lib/api-types.d.ts +7 -0
  27. package/dist/lib/generated-types.d.ts +1 -1
  28. package/dist/lib/generated-types.js +1 -0
  29. package/dist/lib/guild-config.d.ts +13 -0
  30. package/dist/lib/guild-config.js +19 -0
  31. package/dist/lib/npmrc.js +6 -2
  32. package/dist/lib/output.js +25 -99
  33. package/dist/lib/polling.d.ts +7 -0
  34. package/dist/lib/polling.js +12 -3
  35. package/dist/lib/session-events.d.ts +32 -16
  36. package/dist/lib/session-events.js +22 -0
  37. package/dist/lib/session-polling.d.ts +4 -3
  38. package/dist/lib/session-polling.js +75 -17
  39. package/dist/lib/session-resume.js +4 -1
  40. package/dist/lib/stdin.d.ts +4 -0
  41. package/dist/lib/stdin.js +23 -0
  42. package/dist/lib/validate-input-schema.d.ts +19 -0
  43. package/dist/lib/validate-input-schema.js +208 -0
  44. package/dist/lib/version-helpers.js +38 -0
  45. package/dist/lib/workspace-helpers.d.ts +20 -0
  46. package/dist/lib/workspace-helpers.js +49 -0
  47. package/dist/mcp/tools.js +8 -52
  48. package/docs/CLI_WORKFLOW.md +1 -1
  49. package/docs/skills/agent-dev.md +192 -129
  50. package/docs/skills/integrations.md +1 -1
  51. package/package.json +2 -1
  52. package/dist/commands/job/step-get.d.ts +0 -3
@@ -8,6 +8,10 @@ export declare function isInteractive(): boolean;
8
8
  * accidentally pipe input without --mode get clear guidance instead of a hang.
9
9
  */
10
10
  export declare function ensureInteractiveStdin(command: string): void;
11
+ /**
12
+ * Read raw text from stdin with a timeout.
13
+ */
14
+ export declare function readStdinAsText(): Promise<string>;
11
15
  /**
12
16
  * Read JSON from stdin with a timeout.
13
17
  */
package/dist/lib/stdin.js CHANGED
@@ -24,6 +24,29 @@ export function ensureInteractiveStdin(command) {
24
24
  process.exit(1);
25
25
  }
26
26
  }
27
+ /**
28
+ * Read raw text from stdin with a timeout.
29
+ */
30
+ export function readStdinAsText() {
31
+ return new Promise((resolve, reject) => {
32
+ let input = '';
33
+ const timeout = setTimeout(() => {
34
+ reject(new Error('Timeout waiting for stdin'));
35
+ }, 5000);
36
+ process.stdin.setEncoding('utf8');
37
+ process.stdin.on('data', (chunk) => {
38
+ input += chunk;
39
+ });
40
+ process.stdin.on('end', () => {
41
+ clearTimeout(timeout);
42
+ resolve(input);
43
+ });
44
+ process.stdin.on('error', (error) => {
45
+ clearTimeout(timeout);
46
+ reject(error);
47
+ });
48
+ });
49
+ }
27
50
  /**
28
51
  * Read JSON from stdin with a timeout.
29
52
  */
@@ -0,0 +1,19 @@
1
+ interface ValidationSuccess {
2
+ valid: true;
3
+ }
4
+ interface ValidationFailure {
5
+ valid: false;
6
+ errors: Array<{
7
+ message: string;
8
+ path?: string[];
9
+ }>;
10
+ }
11
+ interface ValidationSkipped {
12
+ valid: true;
13
+ skipped: true;
14
+ reason: string;
15
+ }
16
+ export type ValidationResult = ValidationSuccess | ValidationFailure | ValidationSkipped;
17
+ export declare function validateInputSchema(agentDir: string, inputs: unknown[]): Promise<ValidationResult>;
18
+ export {};
19
+ //# sourceMappingURL=validate-input-schema.d.ts.map
@@ -0,0 +1,208 @@
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 * as os from 'os';
6
+ import { execFile } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { debug } from './errors.js';
9
+ const execFileAsync = promisify(execFile);
10
+ function resolveSourceEntryPoint(agentDir, packageJson) {
11
+ const exports = packageJson.exports;
12
+ if (exports?.['.']) {
13
+ const distPath = exports['.'];
14
+ const sourcePath = distPath
15
+ .replace(/^\.\/dist\//, '')
16
+ .replace(/\.compiled\.js$/, '.ts')
17
+ .replace(/\.js$/, '.ts');
18
+ return path.join(agentDir, sourcePath);
19
+ }
20
+ return path.join(agentDir, 'agent.ts');
21
+ }
22
+ async function fileExists(filePath) {
23
+ return fs
24
+ .access(filePath)
25
+ .then(() => true)
26
+ .catch(() => false);
27
+ }
28
+ /**
29
+ * Extract a single `const <name> = z.<call>(...)` declaration from source,
30
+ * starting at the given regex match. Uses balanced parenthesis matching
31
+ * with string literal awareness.
32
+ */
33
+ function extractZodDeclaration(source, match) {
34
+ const startIndex = match.index;
35
+ const afterEquals = match.index + match[0].length;
36
+ let depth = 0;
37
+ let foundOpen = false;
38
+ let endIndex = afterEquals;
39
+ let inString = null;
40
+ for (let charIndex = afterEquals; charIndex < source.length; charIndex++) {
41
+ const char = source[charIndex];
42
+ if (inString) {
43
+ if (char === '\\') {
44
+ charIndex++;
45
+ }
46
+ else if (char === inString) {
47
+ inString = null;
48
+ }
49
+ continue;
50
+ }
51
+ if (char === '"' || char === "'" || char === '`') {
52
+ inString = char;
53
+ }
54
+ else if (char === '(') {
55
+ depth++;
56
+ foundOpen = true;
57
+ }
58
+ else if (char === ')') {
59
+ depth--;
60
+ if (foundOpen && depth === 0) {
61
+ endIndex = charIndex + 1;
62
+ if (source[endIndex] === ';')
63
+ endIndex++;
64
+ break;
65
+ }
66
+ }
67
+ }
68
+ if (!foundOpen)
69
+ return null;
70
+ return source.substring(startIndex, endIndex);
71
+ }
72
+ /**
73
+ * Extract inputSchema and any Zod schema dependencies from agent source.
74
+ * Finds all `const <name> = z.<call>(...)` declarations that appear before
75
+ * inputSchema (which may be referenced inside it), plus inputSchema itself.
76
+ * Returns them in source order so dependencies are defined before use.
77
+ */
78
+ function extractInputSchema(source) {
79
+ const inputPattern = /const\s+inputSchema\s*=\s*/;
80
+ const inputMatch = inputPattern.exec(source);
81
+ if (!inputMatch)
82
+ return null;
83
+ const inputBlock = extractZodDeclaration(source, inputMatch);
84
+ if (!inputBlock)
85
+ return null;
86
+ const blocks = [];
87
+ const zodDeclPattern = /const\s+\w+\s*=\s*z\s*\.\s*/g;
88
+ let declMatch;
89
+ while ((declMatch = zodDeclPattern.exec(source)) !== null) {
90
+ if (declMatch.index >= inputMatch.index)
91
+ break;
92
+ const block = extractZodDeclaration(source, declMatch);
93
+ if (block)
94
+ blocks.push(block);
95
+ }
96
+ blocks.push(inputBlock);
97
+ return blocks.join('\n');
98
+ }
99
+ export async function validateInputSchema(agentDir, inputs) {
100
+ const nodeModulesPath = path.join(agentDir, 'node_modules');
101
+ if (!(await fileExists(nodeModulesPath))) {
102
+ return {
103
+ valid: true,
104
+ skipped: true,
105
+ reason: 'run npm install for pre-build input validation',
106
+ };
107
+ }
108
+ const pkgJsonPath = path.join(agentDir, 'package.json');
109
+ if (!(await fileExists(pkgJsonPath))) {
110
+ return {
111
+ valid: true,
112
+ skipped: true,
113
+ reason: 'no package.json found',
114
+ };
115
+ }
116
+ const pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'));
117
+ let entryPoint = resolveSourceEntryPoint(agentDir, pkgJson);
118
+ if (!(await fileExists(entryPoint))) {
119
+ const fallback = path.join(agentDir, 'agent.ts');
120
+ if (await fileExists(fallback)) {
121
+ entryPoint = fallback;
122
+ }
123
+ else {
124
+ return {
125
+ valid: true,
126
+ skipped: true,
127
+ reason: `could not find agent source file (tried ${path.basename(entryPoint)})`,
128
+ };
129
+ }
130
+ }
131
+ const agentSource = await fs.readFile(entryPoint, 'utf-8');
132
+ const schemaBlock = extractInputSchema(agentSource);
133
+ if (!schemaBlock) {
134
+ return {
135
+ valid: true,
136
+ skipped: true,
137
+ reason: 'could not find inputSchema definition in agent source',
138
+ };
139
+ }
140
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'guild-validate-'));
141
+ try {
142
+ const extractorSource = [
143
+ `import { z } from "zod";`,
144
+ schemaBlock,
145
+ `const inputs = JSON.parse(process.argv[2]);`,
146
+ `const errors = [];`,
147
+ `for (let i = 0; i < inputs.length; i++) {`,
148
+ ` try {`,
149
+ ` inputSchema.parse(inputs[i]);`,
150
+ ` } catch (e) {`,
151
+ ` const issues = e.issues || [{ message: e.message }];`,
152
+ ` errors.push({ index: i, issues: issues.map(iss => ({ message: iss.message, path: iss.path })) });`,
153
+ ` }`,
154
+ `}`,
155
+ `if (errors.length > 0) {`,
156
+ ` console.log(JSON.stringify({ valid: false, errors }));`,
157
+ `} else {`,
158
+ ` console.log(JSON.stringify({ valid: true }));`,
159
+ `}`,
160
+ ].join('\n');
161
+ const extractorPath = path.join(tmpDir, 'validate.mjs');
162
+ await fs.writeFile(extractorPath, extractorSource);
163
+ const { build } = await import('esbuild');
164
+ const bundlePath = path.join(tmpDir, 'validate-bundle.mjs');
165
+ await build({
166
+ entryPoints: [extractorPath],
167
+ bundle: true,
168
+ format: 'esm',
169
+ target: 'esnext',
170
+ platform: 'node',
171
+ outfile: bundlePath,
172
+ logLevel: 'silent',
173
+ nodePaths: [path.join(agentDir, 'node_modules')],
174
+ });
175
+ const inputArg = JSON.stringify(inputs);
176
+ const { stdout } = await execFileAsync('node', [bundlePath, inputArg], {
177
+ cwd: agentDir,
178
+ timeout: 10000,
179
+ });
180
+ const result = JSON.parse(stdout.trim());
181
+ if (result.valid) {
182
+ return { valid: true };
183
+ }
184
+ const flatErrors = [];
185
+ for (const entry of result.errors) {
186
+ for (const issue of entry.issues) {
187
+ const prefix = inputs.length > 1 ? `Line ${entry.index + 1}: ` : '';
188
+ flatErrors.push({
189
+ message: `${prefix}${issue.message}`,
190
+ path: issue.path,
191
+ });
192
+ }
193
+ }
194
+ return { valid: false, errors: flatErrors };
195
+ }
196
+ catch (error) {
197
+ debug('Local input validation failed: %s', error);
198
+ return {
199
+ valid: true,
200
+ skipped: true,
201
+ reason: 'local validation encountered an error (server-side validation will still run)',
202
+ };
203
+ }
204
+ finally {
205
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => { });
206
+ }
207
+ }
208
+ //# sourceMappingURL=validate-input-schema.js.map
@@ -2,12 +2,41 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { GuildAPIClient } from './api-client.js';
4
4
  import { pollUntilComplete } from './polling.js';
5
+ /**
6
+ * Format a list of validation steps into a compact single-line status string
7
+ * suitable for spinner text, e.g. `✓ build ⟳ test ○ deploy`.
8
+ * Returns null when the steps array is empty so callers can fall back to the
9
+ * default attempt-counter text.
10
+ */
11
+ function formatStepProgress(steps) {
12
+ if (steps.length === 0)
13
+ return null;
14
+ const tokens = steps.map((step) => {
15
+ let prefix;
16
+ switch (step.status) {
17
+ case 'SUCCEEDED':
18
+ prefix = '✓';
19
+ break;
20
+ case 'RUNNING':
21
+ prefix = '⟳';
22
+ break;
23
+ case 'FAILED':
24
+ prefix = '✗';
25
+ break;
26
+ default:
27
+ prefix = '○';
28
+ }
29
+ return `${prefix} ${step.name}`;
30
+ });
31
+ return tokens.join(' ');
32
+ }
5
33
  /**
6
34
  * Poll until a version's validation completes, then check the result.
7
35
  * Exits the process on timeout or validation failure.
8
36
  * Returns the updated version on success.
9
37
  */
10
38
  export async function waitForValidation(versionId, output) {
39
+ const client = new GuildAPIClient();
11
40
  const pollResult = await pollUntilComplete({
12
41
  resourceId: versionId,
13
42
  endpoint: `/versions/${versionId}`,
@@ -18,6 +47,10 @@ export async function waitForValidation(versionId, output) {
18
47
  timeoutMessage: 'Validation timed out',
19
48
  maxAttempts: 120,
20
49
  delayMs: 1000,
50
+ onPoll: async () => {
51
+ const r = await client.get(`/versions/${versionId}/validation/steps`);
52
+ return formatStepProgress(r.steps);
53
+ },
21
54
  });
22
55
  if (!pollResult.success || !pollResult.response) {
23
56
  output.error('Validation did not complete in time', 'Check status manually:\n guild agent versions');
@@ -64,6 +97,7 @@ async function fetchValidationFailureDetails(versionId) {
64
97
  * Returns the updated version on success.
65
98
  */
66
99
  export async function waitForPublish(versionId, output) {
100
+ const client = new GuildAPIClient();
67
101
  const pollResult = await pollUntilComplete({
68
102
  resourceId: versionId,
69
103
  endpoint: `/versions/${versionId}`,
@@ -73,6 +107,10 @@ export async function waitForPublish(versionId, output) {
73
107
  timeoutMessage: 'Publish timed out',
74
108
  maxAttempts: 60,
75
109
  delayMs: 1000,
110
+ onPoll: async () => {
111
+ const r = await client.get(`/versions/${versionId}/publish/steps`);
112
+ return formatStepProgress(r.steps);
113
+ },
76
114
  });
77
115
  if (!pollResult.success || pollResult.response?.status !== 'PUBLISHED') {
78
116
  output.error('Publish did not complete in time', 'Check status manually:\n guild agent versions');
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Workspace resolution helper for workspace subcommands.
3
+ *
4
+ * Resolves a workspace identifier (UUID or human-readable name) to a UUID.
5
+ * Used by `guild workspace agent add`, `list`, and `remove`.
6
+ */
7
+ import { GuildAPIClient } from './api-client.js';
8
+ /**
9
+ * Resolve a workspace ID or name to a workspace UUID.
10
+ *
11
+ * UUID inputs pass through unchanged. Name inputs are resolved by searching
12
+ * the user's workspaces via `GET /me/workspaces?filter=all&search=...` first,
13
+ * with a `fetchAll` fallback for IDs and names not matched by the search.
14
+ *
15
+ * Throws an `Error` with message `Workspace "<idOrName>" not found` if
16
+ * nothing resolves — callers should catch this and call `output.error` +
17
+ * `process.exit(1)`.
18
+ */
19
+ export declare function resolveWorkspaceId(client: GuildAPIClient, idOrName: string): Promise<string>;
20
+ //# sourceMappingURL=workspace-helpers.d.ts.map
@@ -0,0 +1,49 @@
1
+ // Copyright 2026 Guild.ai
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4
+ /**
5
+ * Match a workspace against a search argument (case-insensitive name, full_name, or exact ID).
6
+ */
7
+ function matchesWorkspaceArg(w, arg) {
8
+ return (w.id === arg ||
9
+ w.name === arg ||
10
+ w.name.toLowerCase() === arg.toLowerCase() ||
11
+ w.full_name === arg ||
12
+ w.full_name?.toLowerCase() === arg.toLowerCase());
13
+ }
14
+ /**
15
+ * Resolve a workspace ID or name to a workspace UUID.
16
+ *
17
+ * UUID inputs pass through unchanged. Name inputs are resolved by searching
18
+ * the user's workspaces via `GET /me/workspaces?filter=all&search=...` first,
19
+ * with a `fetchAll` fallback for IDs and names not matched by the search.
20
+ *
21
+ * Throws an `Error` with message `Workspace "<idOrName>" not found` if
22
+ * nothing resolves — callers should catch this and call `output.error` +
23
+ * `process.exit(1)`.
24
+ */
25
+ export async function resolveWorkspaceId(client, idOrName) {
26
+ // Short-circuit UUID inputs — pass through unchanged
27
+ if (UUID_RE.test(idOrName)) {
28
+ return idOrName;
29
+ }
30
+ // Use server-side search first. Extract just the name part for full_name
31
+ // inputs (e.g. "owner/workspace-name") since the backend searches by name.
32
+ const searchTerm = idOrName.includes('/')
33
+ ? (idOrName.split('/').pop() ?? idOrName)
34
+ : idOrName;
35
+ const searchResults = await client.get(`/me/workspaces?filter=all&search=${encodeURIComponent(searchTerm)}&limit=100`);
36
+ const directMatch = searchResults.items.find((w) => matchesWorkspaceArg(w, idOrName));
37
+ if (directMatch) {
38
+ return directMatch.id;
39
+ }
40
+ // Fall back to fetching all workspaces (handles IDs and names not returned
41
+ // by search, e.g. when the backend's ILIKE search doesn't match full_name).
42
+ const allWorkspaces = await client.fetchAll('/me/workspaces?filter=all');
43
+ const fallbackMatch = allWorkspaces.find((w) => matchesWorkspaceArg(w, idOrName));
44
+ if (fallbackMatch) {
45
+ return fallbackMatch.id;
46
+ }
47
+ throw new Error(`Workspace "${idOrName}" not found`);
48
+ }
49
+ //# sourceMappingURL=workspace-helpers.js.map
package/dist/mcp/tools.js CHANGED
@@ -1,10 +1,10 @@
1
1
  // Copyright 2026 Guild.ai
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { z } from 'zod';
4
+ import { pollForResponse as pollForSessionResponse } from '../lib/session-polling.js';
4
5
  // ---------------------------------------------------------------------------
5
6
  // Constants
6
7
  // ---------------------------------------------------------------------------
7
- const POLL_INTERVAL_MS = 2000;
8
8
  const POLL_TIMEOUT_MS = 120_000;
9
9
  // ---------------------------------------------------------------------------
10
10
  // Helpers
@@ -14,55 +14,6 @@ function debugLog(debug, message) {
14
14
  process.stderr.write(`[guild-mcp] ${message}\n`);
15
15
  }
16
16
  }
17
- /**
18
- * Poll session events until runtime_done or runtime_error.
19
- * Returns collected agent messages.
20
- */
21
- async function pollForResponse(apiClient, sessionId, debug) {
22
- const startTime = Date.now();
23
- const messages = [];
24
- let lastEventId;
25
- while (Date.now() - startTime < POLL_TIMEOUT_MS) {
26
- try {
27
- let url = `/sessions/${sessionId}/events`;
28
- if (lastEventId) {
29
- url += `?from_id=${lastEventId}`;
30
- }
31
- const response = await apiClient.get(url);
32
- const events = response.events || [];
33
- for (const event of events) {
34
- debugLog(debug, `Event: ${event.event_type}`);
35
- if (event.event_type === 'agent_notification_message') {
36
- const data = extractEventText(event);
37
- if (data) {
38
- messages.push(data);
39
- }
40
- }
41
- if (event.event_type === 'runtime_error') {
42
- const errorText = extractEventText(event);
43
- if (errorText) {
44
- return `Error: ${errorText}`;
45
- }
46
- return 'Error: Agent encountered an error';
47
- }
48
- if (event.event_type === 'runtime_done') {
49
- debugLog(debug, 'Runtime done');
50
- return messages.join('\n\n') || 'Agent completed without output.';
51
- }
52
- }
53
- if (events.length > 0) {
54
- lastEventId = events[events.length - 1].id;
55
- }
56
- }
57
- catch (error) {
58
- debugLog(debug, `Poll error: ${String(error)}`);
59
- }
60
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
61
- }
62
- return messages.length > 0
63
- ? messages.join('\n\n') + '\n\n(Timed out waiting for completion)'
64
- : 'Timed out waiting for agent response.';
65
- }
66
17
  function extractEventText(event) {
67
18
  if (!event.content)
68
19
  return undefined;
@@ -74,6 +25,11 @@ function extractEventText(event) {
74
25
  }
75
26
  return undefined;
76
27
  }
28
+ async function pollForMcpResponse(apiClient, sessionId, debug) {
29
+ debugLog(debug, `Polling response for session ${sessionId}`);
30
+ const result = await pollForSessionResponse(apiClient, sessionId, undefined, POLL_TIMEOUT_MS);
31
+ return result.response || 'Agent completed without output.';
32
+ }
77
33
  function errText(action, error) {
78
34
  return `Failed to ${action}: ${error instanceof Error ? error.message : String(error)}`;
79
35
  }
@@ -504,7 +460,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
504
460
  }
505
461
  const session = await apiClient.post(`/workspaces/${wsId}/sessions`, body);
506
462
  debugLog(debug, `Session created: ${session.id}`);
507
- const response = await pollForResponse(apiClient, session.id, debug);
463
+ const response = await pollForMcpResponse(apiClient, session.id, debug);
508
464
  return {
509
465
  content: [{ type: 'text', text: response }],
510
466
  };
@@ -526,7 +482,7 @@ export function registerTools(server, apiClient, defaultWorkspaceId, debug) {
526
482
  event_type: 'user_message',
527
483
  content: { type: 'text', data: message },
528
484
  });
529
- const response = await pollForResponse(apiClient, session_id, debug);
485
+ const response = await pollForMcpResponse(apiClient, session_id, debug);
530
486
  return {
531
487
  content: [{ type: 'text', text: response }],
532
488
  };
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: Guild CLI Workflow
2
+ name: guild-cli-workflow
3
3
  description: Agent development using the Guild CLI. Activated when user mentions guild agent commands, saving/publishing agents, clone/pull workflow, or agent testing. Covers CLI commands and common workflows.
4
4
  ---
5
5