@guildai/cli 0.6.2 → 0.7.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/README.md +3 -1
- package/dist/commands/agent/chat.js +17 -42
- package/dist/commands/agent/test.js +24 -51
- package/dist/commands/chat.d.ts +2 -1
- package/dist/commands/chat.js +161 -73
- package/dist/commands/session/interrupt.d.ts +3 -0
- package/dist/commands/session/interrupt.js +33 -0
- package/dist/commands/setup.js +70 -11
- package/dist/index.js +2 -0
- package/dist/lib/event-filter.d.ts +50 -0
- package/dist/lib/event-filter.js +91 -0
- package/dist/lib/generated-types.d.ts +2 -0
- package/dist/lib/generated-types.js +20 -0
- package/dist/lib/session-events.d.ts +26 -1
- package/docs/CLI_WORKFLOW.md +7 -1
- package/docs/DESIGN.md +1 -1
- package/docs/skills/codex-agent-dev.md +155 -0
- package/docs/skills/integrations.md +338 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -229,6 +229,8 @@ guild version # Show version info
|
|
|
229
229
|
```bash
|
|
230
230
|
guild setup # Install skills + configure MCP server
|
|
231
231
|
guild setup --claude-md # Also create a CLAUDE.md template
|
|
232
|
+
guild setup --codex # Install Codex skills + configure MCP server
|
|
233
|
+
guild setup --codex --agents-md # Also create an AGENTS.md template
|
|
232
234
|
guild setup --no-mcp # Install skills only, skip MCP configuration
|
|
233
235
|
guild mcp # Start MCP server (used by Claude Code, etc.)
|
|
234
236
|
```
|
|
@@ -243,7 +245,7 @@ The CLI includes an [MCP](https://modelcontextprotocol.io/) server that exposes
|
|
|
243
245
|
guild setup
|
|
244
246
|
```
|
|
245
247
|
|
|
246
|
-
This installs Claude Code skills and adds a `guild` entry to `.mcp.json` in your project. Claude Code
|
|
248
|
+
This installs Claude Code skills and adds a `guild` entry to `.mcp.json` in your project. Use `guild setup --codex` to install Codex skills instead. Claude Code and other MCP-compatible tools will automatically connect when they detect `.mcp.json`.
|
|
247
249
|
|
|
248
250
|
**What it provides:**
|
|
249
251
|
|
|
@@ -16,7 +16,6 @@ import { handleAxiosError, ErrorCodes } from '../../lib/errors.js';
|
|
|
16
16
|
import { format } from '../../lib/progress.js';
|
|
17
17
|
import { showBetaGuidance } from '../../lib/auth.js';
|
|
18
18
|
import { pollUntilComplete } from '../../lib/polling.js';
|
|
19
|
-
import { runGit } from '../../lib/git.js';
|
|
20
19
|
import { suppressScrollbackClear } from '../../lib/alternate-screen.js';
|
|
21
20
|
import * as readline from 'readline';
|
|
22
21
|
import { pollForResponse } from '../../lib/session-polling.js';
|
|
@@ -34,7 +33,7 @@ export function createAgentChatCommand() {
|
|
|
34
33
|
.option('--path <dir>', 'Path to agent directory (defaults to current directory)')
|
|
35
34
|
.option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
|
|
36
35
|
.option('--mode <format>', 'Input mode: json (one-shot) or jsonl (line-by-line)')
|
|
37
|
-
.option('--
|
|
36
|
+
.option('--version <id>', 'Chat with a specific version (UUID or version number)')
|
|
38
37
|
.option('--no-splash', 'Skip the splash screen animation')
|
|
39
38
|
.option('--resume <session-id>', 'Resume an existing session')
|
|
40
39
|
.option('--open', 'Open session in web dashboard')
|
|
@@ -67,10 +66,23 @@ export function createAgentChatCommand() {
|
|
|
67
66
|
const quiet = isQuietMode();
|
|
68
67
|
const resolvedPath = path.resolve(agentPath);
|
|
69
68
|
// Resolve the version to chat with.
|
|
70
|
-
// Default:
|
|
71
|
-
// --
|
|
69
|
+
// Default: create ephemeral version from working directory.
|
|
70
|
+
// --version: use a specific existing version.
|
|
72
71
|
let version;
|
|
73
|
-
if (options.
|
|
72
|
+
if (options.version) {
|
|
73
|
+
// Explicit version: look it up by ID or version number
|
|
74
|
+
const existingVersions = (await client.get(`/agents/${agentId}/versions`));
|
|
75
|
+
const match = existingVersions.items.find((v) => v.id === options.version || v.version_number === options.version);
|
|
76
|
+
if (!match) {
|
|
77
|
+
console.error(`Error: Version not found: ${options.version}`);
|
|
78
|
+
console.error('');
|
|
79
|
+
console.error('List available versions:');
|
|
80
|
+
console.error(` guild agent versions ${agentId}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
version = match;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
74
86
|
if (!quiet) {
|
|
75
87
|
console.error('Building agent...');
|
|
76
88
|
}
|
|
@@ -81,43 +93,6 @@ export function createAgentChatCommand() {
|
|
|
81
93
|
version_type: 'EPHEMERAL',
|
|
82
94
|
}));
|
|
83
95
|
}
|
|
84
|
-
else {
|
|
85
|
-
// Require clean working tree
|
|
86
|
-
const { stdout: statusOutput } = await runGit(['status', '--porcelain'], {
|
|
87
|
-
cwd: resolvedPath,
|
|
88
|
-
});
|
|
89
|
-
if (statusOutput.trim().length > 0) {
|
|
90
|
-
console.error('Error: Working tree has unsaved changes');
|
|
91
|
-
console.error('');
|
|
92
|
-
console.error('Options:');
|
|
93
|
-
console.error(' 1. Save your changes first:');
|
|
94
|
-
console.error(' guild agent save --message "your changes"');
|
|
95
|
-
console.error('');
|
|
96
|
-
console.error(' 2. Chat with unsaved changes:');
|
|
97
|
-
console.error(' guild agent chat --ephemeral');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
// Get commit SHA and look for an existing version
|
|
101
|
-
const { stdout: shaOutput } = await runGit(['rev-parse', 'HEAD'], {
|
|
102
|
-
cwd: resolvedPath,
|
|
103
|
-
});
|
|
104
|
-
const commitSha = shaOutput.trim();
|
|
105
|
-
const existingVersions = (await client.get(`/agents/${agentId}/versions`));
|
|
106
|
-
const existing = existingVersions.items.find((v) => v.sha === commitSha);
|
|
107
|
-
if (existing) {
|
|
108
|
-
version = existing;
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
if (!quiet) {
|
|
112
|
-
console.error('Building agent...');
|
|
113
|
-
}
|
|
114
|
-
version = (await client.post(`/agents/${agentId}/versions`, {
|
|
115
|
-
commit_sha: commitSha,
|
|
116
|
-
summary: '[Chat] Development version',
|
|
117
|
-
version_type: 'COMMITTED',
|
|
118
|
-
}));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
96
|
// Wait for build to complete (skip if version already validated)
|
|
122
97
|
if (version.validation_status === 'PENDING' ||
|
|
123
98
|
version.validation_status === 'RUNNING') {
|
|
@@ -17,7 +17,7 @@ import { isQuietMode, getOutputMode } from '../../lib/output-mode.js';
|
|
|
17
17
|
import { pollForResponse } from '../../lib/session-polling.js';
|
|
18
18
|
import { readStdinAsJSON, ensureInteractiveStdin } from '../../lib/stdin.js';
|
|
19
19
|
import { loadLocalConfig, getWorkspaceId } from '../../lib/guild-config.js';
|
|
20
|
-
import {
|
|
20
|
+
import { GitError, formatGitError } from '../../lib/git.js';
|
|
21
21
|
import { pollUntilComplete } from '../../lib/polling.js';
|
|
22
22
|
import { readAgentFiles } from '../../lib/agent-helpers.js';
|
|
23
23
|
import { fetchSession, fetchSessionEvents } from '../../lib/session-resume.js';
|
|
@@ -33,7 +33,7 @@ export function createAgentTestCommand() {
|
|
|
33
33
|
.description('Test agent in interactive REPL session')
|
|
34
34
|
.option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
|
|
35
35
|
.option('--mode <format>', 'Input mode: json (one-shot) or jsonl (line-by-line)')
|
|
36
|
-
.option('--
|
|
36
|
+
.option('--version <id>', 'Test a specific version (UUID or version number)')
|
|
37
37
|
.option('--resume <session-id>', 'Resume an existing test session')
|
|
38
38
|
.option('--open', 'Open session in web dashboard')
|
|
39
39
|
.action(async (options) => {
|
|
@@ -75,12 +75,10 @@ export function createAgentTestCommand() {
|
|
|
75
75
|
console.error(' guild agent clone <agent-id>');
|
|
76
76
|
process.exit(1);
|
|
77
77
|
}
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
let commitSha = null;
|
|
78
|
+
// Read agent files from disk for ephemeral version creation.
|
|
79
|
+
// Skipped when --version is provided (testing a specific existing version).
|
|
81
80
|
let agentFiles = null;
|
|
82
|
-
if (options.
|
|
83
|
-
// Ephemeral mode: read files from disk
|
|
81
|
+
if (!options.version) {
|
|
84
82
|
try {
|
|
85
83
|
agentFiles = await readAgentFiles(cwd);
|
|
86
84
|
}
|
|
@@ -96,28 +94,6 @@ export function createAgentTestCommand() {
|
|
|
96
94
|
process.exit(1);
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
|
-
else {
|
|
100
|
-
// Committed mode: require clean working tree
|
|
101
|
-
const { stdout: statusOutput } = await runGit(['status', '--porcelain'], {
|
|
102
|
-
cwd,
|
|
103
|
-
});
|
|
104
|
-
if (statusOutput.trim().length > 0) {
|
|
105
|
-
console.error('Error: Working tree has unsaved changes');
|
|
106
|
-
console.error('');
|
|
107
|
-
console.error('Options:');
|
|
108
|
-
console.error(' 1. Save your changes first:');
|
|
109
|
-
console.error(' guild agent save --message "your changes"');
|
|
110
|
-
console.error('');
|
|
111
|
-
console.error(' 2. Test unsaved changes:');
|
|
112
|
-
console.error(' guild agent test --ephemeral');
|
|
113
|
-
process.exit(1);
|
|
114
|
-
}
|
|
115
|
-
// Get commit SHA
|
|
116
|
-
const { stdout: shaOutput } = await runGit(['rev-parse', 'HEAD'], {
|
|
117
|
-
cwd,
|
|
118
|
-
});
|
|
119
|
-
commitSha = shaOutput.trim();
|
|
120
|
-
}
|
|
121
97
|
// If using JSON input, read and validate it early (before auth/session creation)
|
|
122
98
|
// This provides better UX - fail fast on bad JSON without needing auth
|
|
123
99
|
let inputData;
|
|
@@ -156,33 +132,31 @@ export function createAgentTestCommand() {
|
|
|
156
132
|
}
|
|
157
133
|
// Validate auth before any API calls or UI rendering
|
|
158
134
|
await ensureAuthenticated();
|
|
159
|
-
//
|
|
135
|
+
// Resolve version for testing
|
|
160
136
|
const client = new GuildAPIClient();
|
|
161
137
|
let version;
|
|
162
138
|
try {
|
|
163
|
-
if (options.
|
|
164
|
-
//
|
|
139
|
+
if (options.version) {
|
|
140
|
+
// Explicit version: look it up by ID or version number
|
|
141
|
+
const existingVersions = (await client.get(`/agents/${guildConfig.agent_id}/versions`));
|
|
142
|
+
const match = existingVersions.items.find((v) => v.id === options.version || v.version_number === options.version);
|
|
143
|
+
if (!match) {
|
|
144
|
+
console.error(`Error: Version not found: ${options.version}`);
|
|
145
|
+
console.error('');
|
|
146
|
+
console.error('List available versions:');
|
|
147
|
+
console.error(` guild agent versions ${guildConfig.agent_id}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
version = match;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
// Default: create ephemeral version from working directory
|
|
165
154
|
version = (await client.post(`/agents/${guildConfig.agent_id}/versions`, {
|
|
166
155
|
files: agentFiles,
|
|
167
156
|
summary: '[Test] Ephemeral development version',
|
|
168
157
|
version_type: 'EPHEMERAL',
|
|
169
158
|
}));
|
|
170
159
|
}
|
|
171
|
-
else {
|
|
172
|
-
// Committed mode: check if a version for this commit already exists
|
|
173
|
-
const existingVersions = (await client.get(`/agents/${guildConfig.agent_id}/versions`));
|
|
174
|
-
const existing = existingVersions.items.find((v) => v.sha === commitSha);
|
|
175
|
-
if (existing) {
|
|
176
|
-
version = existing;
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
version = (await client.post(`/agents/${guildConfig.agent_id}/versions`, {
|
|
180
|
-
commit_sha: commitSha,
|
|
181
|
-
summary: '[Test] Development version',
|
|
182
|
-
version_type: 'COMMITTED',
|
|
183
|
-
}));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
160
|
}
|
|
187
161
|
catch (error) {
|
|
188
162
|
const formattedError = handleAxiosError(error);
|
|
@@ -280,9 +254,9 @@ export function createAgentTestCommand() {
|
|
|
280
254
|
const quiet = isQuietMode();
|
|
281
255
|
if (!quiet) {
|
|
282
256
|
console.log(`✓ Agent: ${guildConfig.name} (${guildConfig.agent_id})`);
|
|
283
|
-
const versionDisplay = options.
|
|
284
|
-
?
|
|
285
|
-
:
|
|
257
|
+
const versionDisplay = options.version
|
|
258
|
+
? options.version
|
|
259
|
+
: 'ephemeral (working directory)';
|
|
286
260
|
console.log(`✓ Version: ${versionDisplay}`);
|
|
287
261
|
console.log(`✓ Workspace: ${workspaceId}`);
|
|
288
262
|
const sessionLink = session.session_url
|
|
@@ -452,7 +426,6 @@ export function createAgentTestCommand() {
|
|
|
452
426
|
console.error('Failed to start test session. Troubleshooting:');
|
|
453
427
|
console.error(' - Verify the agent exists: guild agent get');
|
|
454
428
|
console.error(' - Check your workspace: guild workspace list');
|
|
455
|
-
console.error(' - Test unsaved changes: guild agent test --ephemeral');
|
|
456
429
|
if (formattedError.code) {
|
|
457
430
|
console.error(` - Error code: ${formattedError.code}`);
|
|
458
431
|
}
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -17,8 +17,9 @@ export interface ChatAppProps {
|
|
|
17
17
|
resumeEvents?: SessionEvent[];
|
|
18
18
|
resumeCommand?: string;
|
|
19
19
|
openDashboard?: boolean;
|
|
20
|
+
eventFilter?: Set<string>;
|
|
20
21
|
}
|
|
21
|
-
export declare function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash, resumeSession, resumeEvents, resumeCommand, openDashboard, }: ChatAppProps): React.JSX.Element;
|
|
22
|
+
export declare function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash, resumeSession, resumeEvents, resumeCommand, openDashboard, eventFilter, }: ChatAppProps): React.JSX.Element;
|
|
22
23
|
export declare function ensureAuthenticated(): Promise<string>;
|
|
23
24
|
export declare function createSession(client: GuildAPIClient, workspaceId: string | undefined, initialPrompt: string, versionId?: string, onProgress?: (status: string) => void): Promise<Session>;
|
|
24
25
|
export declare function createChatCommand(): Command;
|
package/dist/commands/chat.js
CHANGED
|
@@ -15,6 +15,7 @@ import path from 'path';
|
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { isUnfulfilledAgentInstallRequest, isFilteredTaskName, getTaskDisplayName, matchesAgent, getAgentName, } from '../lib/session-events.js';
|
|
17
17
|
import { printResumeHint, fetchSession, fetchSessionEvents, eventsToDisplayMessages, } from '../lib/session-resume.js';
|
|
18
|
+
import { DEFAULT_EVENT_TYPES, parseEventFilter, shouldShowEvent, } from '../lib/event-filter.js';
|
|
18
19
|
import { fetchEvents, fetchTasks } from '../lib/session-events-fetch.js';
|
|
19
20
|
import { AgentInstallPrompt } from '../components/AgentInstallPrompt.js';
|
|
20
21
|
import { getWorkspaceId } from '../lib/guild-config.js';
|
|
@@ -117,7 +118,7 @@ function InputWrapper({ isReady, isInterrupted, input, setInput, handleSubmit, t
|
|
|
117
118
|
React.createElement(Text, { color: isReady ? BRAND_COLOR : 'gray' }, "> "),
|
|
118
119
|
isReady && isActive ? (React.createElement(CustomInput, { value: input, onChange: setInput, onSubmit: handleSubmit, trackedTasksSize: trackedTasksSize, setShowTaskPanel: setShowTaskPanel, isActive: isActive })) : isReady ? (React.createElement(Text, null, input)) : (React.createElement(Text, null, chalk.dim('(connecting...)')))));
|
|
119
120
|
}
|
|
120
|
-
export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash = true, resumeSession, resumeEvents, resumeCommand, openDashboard, }) {
|
|
121
|
+
export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentName, showSplash = true, resumeSession, resumeEvents, resumeCommand, openDashboard, eventFilter, }) {
|
|
121
122
|
const { exit } = useApp();
|
|
122
123
|
const isResuming = !!resumeSession;
|
|
123
124
|
const [phase, setPhase] = useState(isResuming || !showSplash ? 'chat' : 'splash');
|
|
@@ -241,13 +242,14 @@ export function ChatApp({ initialPrompt, version, workspaceId, versionId, agentN
|
|
|
241
242
|
}
|
|
242
243
|
// If not connected yet, ignore escape (let connection complete)
|
|
243
244
|
} })),
|
|
244
|
-
phase === 'chat' && (React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand })),
|
|
245
|
+
phase === 'chat' && (React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), resumeEvents: resumeEvents, resumeCommand: resumeCommand, eventFilter: eventFilter })),
|
|
245
246
|
(phase === 'splash' || phase === 'finalizing') &&
|
|
246
247
|
connectedSession &&
|
|
247
248
|
connectedClient && (React.createElement(Box, { display: "none" },
|
|
248
|
-
React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), isActive: false })))));
|
|
249
|
+
React.createElement(ChatUIWithConnection, { initialPrompt: initialPrompt, version: version, versionId: versionId, agentName: agentName, client: connectedClient, session: connectedSession, onFirstMessage: () => setFirstMessageReceived(true), isActive: false, eventFilter: eventFilter })))));
|
|
249
250
|
}
|
|
250
|
-
function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _versionId, agentName, client: preConnectedClient, session: preConnectedSession, onFirstMessage, isActive = true, resumeEvents, resumeCommand, }) {
|
|
251
|
+
function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _versionId, agentName, client: preConnectedClient, session: preConnectedSession, onFirstMessage, isActive = true, resumeEvents, resumeCommand, eventFilter, }) {
|
|
252
|
+
const activeFilter = eventFilter ?? DEFAULT_EVENT_TYPES;
|
|
251
253
|
// Note: We handle SIGINT directly via process.on, not using useApp().exit
|
|
252
254
|
// Task panel state - managed at this level to handle keyboard input before TextInput
|
|
253
255
|
// Default to showing task panel (Ctrl-T to toggle)
|
|
@@ -525,18 +527,107 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
525
527
|
}
|
|
526
528
|
// Process events that affect the chat UI (task state comes from tasks poll)
|
|
527
529
|
if (event.type === 'runtime_error') {
|
|
528
|
-
//
|
|
529
|
-
const errorText = typeof event.content === 'string' ? event.content : 'Unknown error';
|
|
530
|
-
const taskName = agentName || 'assistant';
|
|
531
|
-
setMessages((prev) => [
|
|
532
|
-
...prev,
|
|
533
|
-
{
|
|
534
|
-
key: `error-${Date.now()}`,
|
|
535
|
-
content: `${chalk.red('●')} ${chalk.bold(taskName)}\n${chalk.red(`Error: ${errorText}`)}`,
|
|
536
|
-
type: 'assistant',
|
|
537
|
-
},
|
|
538
|
-
]);
|
|
530
|
+
// Always clear the spinner on runtime errors so the UI doesn't get stuck
|
|
539
531
|
setCurrentOperation('');
|
|
532
|
+
// Show runtime errors in the chat (gated on --events filter)
|
|
533
|
+
if (shouldShowEvent('runtime_error', activeFilter)) {
|
|
534
|
+
const errorText = typeof event.content === 'string' ? event.content : 'Unknown error';
|
|
535
|
+
const taskName = agentName || 'assistant';
|
|
536
|
+
setMessages((prev) => [
|
|
537
|
+
...prev,
|
|
538
|
+
{
|
|
539
|
+
key: `error-${Date.now()}`,
|
|
540
|
+
content: `${chalk.red('●')} ${chalk.bold(taskName)}\n${chalk.red(`Error: ${errorText}`)}`,
|
|
541
|
+
type: 'assistant',
|
|
542
|
+
},
|
|
543
|
+
]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
else if (event.type === 'runtime_start') {
|
|
547
|
+
if (shouldShowEvent('runtime_start', activeFilter)) {
|
|
548
|
+
setMessages((prev) => [
|
|
549
|
+
...prev,
|
|
550
|
+
{
|
|
551
|
+
key: `runtime-start-${Date.now()}`,
|
|
552
|
+
content: chalk.dim('[runtime/start]'),
|
|
553
|
+
type: 'assistant',
|
|
554
|
+
},
|
|
555
|
+
]);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
else if (event.type === 'runtime_running') {
|
|
559
|
+
if (shouldShowEvent('runtime_running', activeFilter)) {
|
|
560
|
+
setMessages((prev) => [
|
|
561
|
+
...prev,
|
|
562
|
+
{
|
|
563
|
+
key: `runtime-running-${Date.now()}`,
|
|
564
|
+
content: chalk.dim('[runtime/running]'),
|
|
565
|
+
type: 'assistant',
|
|
566
|
+
},
|
|
567
|
+
]);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
else if (event.type === 'runtime_waiting') {
|
|
571
|
+
if (shouldShowEvent('runtime_waiting', activeFilter)) {
|
|
572
|
+
setMessages((prev) => [
|
|
573
|
+
...prev,
|
|
574
|
+
{
|
|
575
|
+
key: `runtime-waiting-${Date.now()}`,
|
|
576
|
+
content: chalk.dim('[runtime/waiting]'),
|
|
577
|
+
type: 'assistant',
|
|
578
|
+
},
|
|
579
|
+
]);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else if (event.type === 'trigger_message') {
|
|
583
|
+
if (shouldShowEvent('trigger_message', activeFilter)) {
|
|
584
|
+
const triggerText = typeof event.content === 'object' ? event.content?.data || '' : '';
|
|
585
|
+
setMessages((prev) => [
|
|
586
|
+
...prev,
|
|
587
|
+
{
|
|
588
|
+
key: `trigger-${Date.now()}`,
|
|
589
|
+
content: `${chalk.cyan('[trigger]')} ${triggerText}`,
|
|
590
|
+
type: 'assistant',
|
|
591
|
+
},
|
|
592
|
+
]);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else if (event.type === 'system_error') {
|
|
596
|
+
if (shouldShowEvent('system_error', activeFilter)) {
|
|
597
|
+
const errText = typeof event.content === 'object' ? event.content?.data || '' : '';
|
|
598
|
+
setMessages((prev) => [
|
|
599
|
+
...prev,
|
|
600
|
+
{
|
|
601
|
+
key: `system-error-${Date.now()}`,
|
|
602
|
+
content: `${chalk.red('[system_error]')} ${errText}`,
|
|
603
|
+
type: 'assistant',
|
|
604
|
+
},
|
|
605
|
+
]);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
else if (event.type === 'llm_start') {
|
|
609
|
+
if (shouldShowEvent('llm_start', activeFilter)) {
|
|
610
|
+
setMessages((prev) => [
|
|
611
|
+
...prev,
|
|
612
|
+
{
|
|
613
|
+
key: `llm-start-${Date.now()}`,
|
|
614
|
+
content: chalk.dim(`[llm_start] provider:${event.provider}`),
|
|
615
|
+
type: 'assistant',
|
|
616
|
+
},
|
|
617
|
+
]);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else if (event.type === 'llm_done') {
|
|
621
|
+
if (shouldShowEvent('llm_done', activeFilter)) {
|
|
622
|
+
setMessages((prev) => [
|
|
623
|
+
...prev,
|
|
624
|
+
{
|
|
625
|
+
key: `llm-done-${Date.now()}`,
|
|
626
|
+
content: chalk.dim(`[llm_done] HTTP ${event.status_code}`),
|
|
627
|
+
type: 'assistant',
|
|
628
|
+
},
|
|
629
|
+
]);
|
|
630
|
+
}
|
|
540
631
|
}
|
|
541
632
|
else if (event.type === 'agent_notification_progress') {
|
|
542
633
|
// Update status line with progress text (task tracking is done by tasks poll)
|
|
@@ -609,48 +700,63 @@ function ChatUIWithConnection({ initialPrompt, version: _version, versionId: _ve
|
|
|
609
700
|
]);
|
|
610
701
|
setCurrentOperation('');
|
|
611
702
|
}
|
|
612
|
-
else if (event.type === 'agent_console'
|
|
613
|
-
// Show console logs when
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
703
|
+
else if (event.type === 'agent_console') {
|
|
704
|
+
// Show console logs when enabled via --events or --debug
|
|
705
|
+
// --debug continues to show console logs for backwards compatibility
|
|
706
|
+
if (shouldShowEvent('agent_console', activeFilter) || isDebugMode()) {
|
|
707
|
+
const content = typeof event.content === 'string' ? event.content : '';
|
|
708
|
+
setMessages((prev) => [
|
|
709
|
+
...prev,
|
|
710
|
+
{
|
|
711
|
+
key: `console-${Date.now()}-${Math.random()}`,
|
|
712
|
+
content: chalk.dim(`[console.${event.level}] ${content}`),
|
|
713
|
+
type: 'assistant',
|
|
714
|
+
},
|
|
715
|
+
]);
|
|
716
|
+
}
|
|
623
717
|
}
|
|
624
|
-
else if (event.type === 'runtime_done'
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
taskInfo &&
|
|
628
|
-
'agent' in taskInfo) {
|
|
629
|
-
// One-shot agents may complete with runtime_done without sending
|
|
630
|
-
// agent_notification_message. Display the output if we haven't
|
|
631
|
-
// already shown a response for this input cycle.
|
|
632
|
-
const contentStr = typeof event.content === 'string'
|
|
633
|
-
? event.content
|
|
634
|
-
: JSON.stringify(event.content);
|
|
635
|
-
if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
|
|
636
|
-
const rendered = fixListItemMarkdown(marked.parse(contentStr));
|
|
637
|
-
const taskName = agentName || 'assistant';
|
|
638
|
-
const messageContent = `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
|
|
718
|
+
else if (event.type === 'runtime_done') {
|
|
719
|
+
if (shouldShowEvent('runtime_done', activeFilter)) {
|
|
720
|
+
// Show runtime_done as a system event when enabled via --events
|
|
639
721
|
setMessages((prev) => [
|
|
640
722
|
...prev,
|
|
641
723
|
{
|
|
642
|
-
key: `
|
|
643
|
-
content:
|
|
724
|
+
key: `runtime-done-${Date.now()}`,
|
|
725
|
+
content: chalk.dim('[runtime/done]'),
|
|
644
726
|
type: 'assistant',
|
|
645
|
-
timestamp: new Date().toLocaleTimeString(),
|
|
646
727
|
},
|
|
647
728
|
]);
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
729
|
+
}
|
|
730
|
+
if (!receivedResponseSinceLastInput.current &&
|
|
731
|
+
event.content !== undefined &&
|
|
732
|
+
taskInfo &&
|
|
733
|
+
'agent' in taskInfo) {
|
|
734
|
+
// One-shot agents may complete with runtime_done without sending
|
|
735
|
+
// agent_notification_message. Display the output if we haven't
|
|
736
|
+
// already shown a response for this input cycle.
|
|
737
|
+
const contentStr = typeof event.content === 'string'
|
|
738
|
+
? event.content
|
|
739
|
+
: JSON.stringify(event.content);
|
|
740
|
+
if (contentStr && contentStr !== '{}' && contentStr !== 'null') {
|
|
741
|
+
const rendered = fixListItemMarkdown(marked.parse(contentStr));
|
|
742
|
+
const taskName = agentName || 'assistant';
|
|
743
|
+
const messageContent = `${chalk.green('●')} ${chalk.bold(taskName)}\n${rendered.trim()}`;
|
|
744
|
+
setMessages((prev) => [
|
|
745
|
+
...prev,
|
|
746
|
+
{
|
|
747
|
+
key: `msg-${Date.now()}-${Math.random()}`,
|
|
748
|
+
content: messageContent,
|
|
749
|
+
type: 'assistant',
|
|
750
|
+
timestamp: new Date().toLocaleTimeString(),
|
|
751
|
+
},
|
|
752
|
+
]);
|
|
753
|
+
if (!firstMessageNotified.current && onFirstMessage) {
|
|
754
|
+
firstMessageNotified.current = true;
|
|
755
|
+
onFirstMessage();
|
|
756
|
+
}
|
|
757
|
+
receivedResponseSinceLastInput.current = true;
|
|
758
|
+
setCurrentOperation('');
|
|
651
759
|
}
|
|
652
|
-
receivedResponseSinceLastInput.current = true;
|
|
653
|
-
setCurrentOperation('');
|
|
654
760
|
}
|
|
655
761
|
}
|
|
656
762
|
else if (event.type === 'interrupted') {
|
|
@@ -887,29 +993,7 @@ export async function createSession(client, workspaceId, initialPrompt, versionI
|
|
|
887
993
|
}
|
|
888
994
|
}
|
|
889
995
|
if (!workspaceId) {
|
|
890
|
-
|
|
891
|
-
progress('Fetching workspaces');
|
|
892
|
-
const response = await client.get('/me/workspaces');
|
|
893
|
-
if (!response) {
|
|
894
|
-
throw new Error('Failed to fetch workspaces');
|
|
895
|
-
}
|
|
896
|
-
// Handle both array and paginated object responses
|
|
897
|
-
const workspaces = (Array.isArray(response)
|
|
898
|
-
? response
|
|
899
|
-
: response?.items || []);
|
|
900
|
-
if (workspaces.length === 0) {
|
|
901
|
-
// Get current user to use as workspace owner
|
|
902
|
-
progress('Creating workspace');
|
|
903
|
-
const me = (await client.get('/me'));
|
|
904
|
-
const newWorkspace = (await client.post('/workspaces', {
|
|
905
|
-
name: 'My Workspace',
|
|
906
|
-
owner_id: me.id,
|
|
907
|
-
}));
|
|
908
|
-
workspaceId = newWorkspace.id;
|
|
909
|
-
}
|
|
910
|
-
else {
|
|
911
|
-
workspaceId = workspaces[0].id;
|
|
912
|
-
}
|
|
996
|
+
throw new Error('No workspace configured. Run: guild workspace select, or pass --workspace <id>');
|
|
913
997
|
}
|
|
914
998
|
progress('Creating session');
|
|
915
999
|
const sessionData = {
|
|
@@ -950,9 +1034,13 @@ export function createChatCommand() {
|
|
|
950
1034
|
.option('--workspace <identifier>', 'Workspace ID or full name (e.g., owner/workspace-name)')
|
|
951
1035
|
.option('--no-splash', 'Skip the splash screen animation')
|
|
952
1036
|
.option('--resume <session-id>', 'Resume an existing session')
|
|
1037
|
+
.option('--events <types>', 'Event types to show (default: user). Shorthands: none, user, system, all, or comma-separated type names (e.g. agent_console,llm_start)')
|
|
953
1038
|
.addHelpText('after', '\nTo chat with a local agent under development: guild agent chat')
|
|
954
1039
|
.action(async (promptArgs, options) => {
|
|
955
1040
|
const initialPrompt = promptArgs.length > 0 ? promptArgs.join(' ') : 'Hello';
|
|
1041
|
+
const eventFilter = options.events
|
|
1042
|
+
? parseEventFilter(options.events)
|
|
1043
|
+
: DEFAULT_EVENT_TYPES;
|
|
956
1044
|
if (options.once) {
|
|
957
1045
|
// --once mode: use old spinner-based approach
|
|
958
1046
|
const spinner = createSpinner('Connecting to Guild servers...');
|
|
@@ -1075,7 +1163,7 @@ export function createChatCommand() {
|
|
|
1075
1163
|
if (shouldShowSplash) {
|
|
1076
1164
|
suppressScrollbackClear();
|
|
1077
1165
|
}
|
|
1078
|
-
const { waitUntilExit } = render(React.createElement(ChatApp, { initialPrompt: initialPrompt, version: packageJson.version, workspaceId: options.workspace, versionId: options.agent, showSplash: shouldShowSplash, resumeSession: resumeSession, resumeEvents: resumeSessionEvents, resumeCommand: resumeCommand }), {
|
|
1166
|
+
const { waitUntilExit } = render(React.createElement(ChatApp, { initialPrompt: initialPrompt, version: packageJson.version, workspaceId: options.workspace, versionId: options.agent, showSplash: shouldShowSplash, resumeSession: resumeSession, resumeEvents: resumeSessionEvents, resumeCommand: resumeCommand, eventFilter: eventFilter }), {
|
|
1079
1167
|
exitOnCtrlC: false, // We handle Ctrl-C in useInput (raw mode)
|
|
1080
1168
|
});
|
|
1081
1169
|
await waitUntilExit();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2026 Guild.ai
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { GuildAPIClient } from '../../lib/api-client.js';
|
|
5
|
+
import { getAuthToken } from '../../lib/auth.js';
|
|
6
|
+
import { handleAxiosError } from '../../lib/errors.js';
|
|
7
|
+
import { createOutputWriter } from '../../lib/output.js';
|
|
8
|
+
export function createSessionInterruptCommand() {
|
|
9
|
+
const cmd = new Command('interrupt');
|
|
10
|
+
cmd
|
|
11
|
+
.description('Interrupt a running session')
|
|
12
|
+
.argument('<session-id>', 'Session ID')
|
|
13
|
+
.action(async (sessionId) => {
|
|
14
|
+
const output = createOutputWriter();
|
|
15
|
+
try {
|
|
16
|
+
const token = await getAuthToken();
|
|
17
|
+
if (!token) {
|
|
18
|
+
output.error('Not authenticated. Run: guild auth login');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const client = new GuildAPIClient();
|
|
22
|
+
const response = await client.post(`/sessions/${sessionId}/interrupt`, {});
|
|
23
|
+
output.data(response);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const formattedError = handleAxiosError(error);
|
|
27
|
+
output.error(`Failed to interrupt session: ${formattedError.details}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return cmd;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=interrupt.js.map
|