gencode-ai 0.1.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/.env.example +11 -0
- package/CLAUDE.md +70 -0
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/dist/agent/agent.d.ts +84 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +233 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/index.d.ts +6 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +6 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/types.d.ts +47 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +5 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/components/App.d.ts +14 -0
- package/dist/cli/components/App.d.ts.map +1 -0
- package/dist/cli/components/App.js +395 -0
- package/dist/cli/components/App.js.map +1 -0
- package/dist/cli/components/CommandSuggestions.d.ts +13 -0
- package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
- package/dist/cli/components/CommandSuggestions.js +32 -0
- package/dist/cli/components/CommandSuggestions.js.map +1 -0
- package/dist/cli/components/Header.d.ts +9 -0
- package/dist/cli/components/Header.d.ts.map +1 -0
- package/dist/cli/components/Header.js +13 -0
- package/dist/cli/components/Header.js.map +1 -0
- package/dist/cli/components/Input.d.ts +13 -0
- package/dist/cli/components/Input.d.ts.map +1 -0
- package/dist/cli/components/Input.js +27 -0
- package/dist/cli/components/Input.js.map +1 -0
- package/dist/cli/components/Logo.d.ts +2 -0
- package/dist/cli/components/Logo.d.ts.map +1 -0
- package/dist/cli/components/Logo.js +8 -0
- package/dist/cli/components/Logo.js.map +1 -0
- package/dist/cli/components/Messages.d.ts +37 -0
- package/dist/cli/components/Messages.d.ts.map +1 -0
- package/dist/cli/components/Messages.js +106 -0
- package/dist/cli/components/Messages.js.map +1 -0
- package/dist/cli/components/ModelSelector.d.ts +13 -0
- package/dist/cli/components/ModelSelector.d.ts.map +1 -0
- package/dist/cli/components/ModelSelector.js +72 -0
- package/dist/cli/components/ModelSelector.js.map +1 -0
- package/dist/cli/components/Spinner.d.ts +12 -0
- package/dist/cli/components/Spinner.d.ts.map +1 -0
- package/dist/cli/components/Spinner.js +45 -0
- package/dist/cli/components/Spinner.js.map +1 -0
- package/dist/cli/components/index.d.ts +12 -0
- package/dist/cli/components/index.d.ts.map +1 -0
- package/dist/cli/components/index.js +12 -0
- package/dist/cli/components/index.js.map +1 -0
- package/dist/cli/components/theme.d.ts +31 -0
- package/dist/cli/components/theme.d.ts.map +1 -0
- package/dist/cli/components/theme.js +36 -0
- package/dist/cli/components/theme.js.map +1 -0
- package/dist/cli/index-legacy.d.ts +7 -0
- package/dist/cli/index-legacy.d.ts.map +1 -0
- package/dist/cli/index-legacy.js +431 -0
- package/dist/cli/index-legacy.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +116 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ink-cli.d.ts +7 -0
- package/dist/cli/ink-cli.d.ts.map +1 -0
- package/dist/cli/ink-cli.js +105 -0
- package/dist/cli/ink-cli.js.map +1 -0
- package/dist/cli/session-picker.d.ts +16 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +280 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/ui.d.ts +61 -0
- package/dist/cli/ui.d.ts.map +1 -0
- package/dist/cli/ui.js +364 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/manager.d.ts +31 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +65 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/memory/index.d.ts +10 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/init.d.ts +20 -0
- package/dist/memory/init.d.ts.map +1 -0
- package/dist/memory/init.js +332 -0
- package/dist/memory/init.js.map +1 -0
- package/dist/memory/manager.d.ts +85 -0
- package/dist/memory/manager.d.ts.map +1 -0
- package/dist/memory/manager.js +234 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/types.d.ts +74 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +6 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/permissions/index.d.ts +7 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/manager.d.ts +32 -0
- package/dist/permissions/manager.d.ts.map +1 -0
- package/dist/permissions/manager.js +79 -0
- package/dist/permissions/manager.js.map +1 -0
- package/dist/permissions/types.d.ts +14 -0
- package/dist/permissions/types.d.ts.map +1 -0
- package/dist/permissions/types.js +17 -0
- package/dist/permissions/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +20 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +185 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +21 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +241 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/index.d.ts +34 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +72 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +19 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +221 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +125 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +6 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/session/index.d.ts +6 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +6 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/manager.d.ts +101 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +295 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/types.d.ts +39 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +10 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +7 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +80 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +7 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +32 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/glob.d.ts +7 -0
- package/dist/tools/builtin/glob.d.ts.map +1 -0
- package/dist/tools/builtin/glob.js +36 -0
- package/dist/tools/builtin/glob.js.map +1 -0
- package/dist/tools/builtin/grep.d.ts +7 -0
- package/dist/tools/builtin/grep.d.ts.map +1 -0
- package/dist/tools/builtin/grep.js +59 -0
- package/dist/tools/builtin/grep.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +7 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +29 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +7 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +24 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/index.d.ts +38 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +32 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +22 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +71 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +62 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +126 -0
- package/dist/tools/types.js.map +1 -0
- package/docs/README.md +16 -0
- package/docs/proposals/0001-web-fetch-tool.md +293 -0
- package/docs/proposals/0002-web-search-tool.md +306 -0
- package/docs/proposals/0003-task-subagents.md +333 -0
- package/docs/proposals/0004-plan-mode.md +338 -0
- package/docs/proposals/0005-todo-system.md +299 -0
- package/docs/proposals/0006-memory-system.md +539 -0
- package/docs/proposals/0007-context-management.md +429 -0
- package/docs/proposals/0008-checkpointing.md +327 -0
- package/docs/proposals/0009-hooks-system.md +343 -0
- package/docs/proposals/0010-mcp-integration.md +382 -0
- package/docs/proposals/0011-custom-commands.md +374 -0
- package/docs/proposals/0012-ask-user-question.md +317 -0
- package/docs/proposals/0013-multi-edit-tool.md +345 -0
- package/docs/proposals/0014-lsp-tool.md +478 -0
- package/docs/proposals/0015-ls-tool.md +407 -0
- package/docs/proposals/0016-kill-shell-tool.md +455 -0
- package/docs/proposals/0017-background-tasks.md +489 -0
- package/docs/proposals/0018-parallel-tool-execution.md +415 -0
- package/docs/proposals/0019-session-enhancements.md +462 -0
- package/docs/proposals/0020-session-summarization.md +447 -0
- package/docs/proposals/0021-skills-system.md +409 -0
- package/docs/proposals/0022-plugin-system.md +467 -0
- package/docs/proposals/0023-permission-enhancements.md +470 -0
- package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
- package/docs/proposals/0025-cost-tracking.md +447 -0
- package/docs/proposals/0026-git-integration.md +475 -0
- package/docs/proposals/0027-enhanced-read-tool.md +514 -0
- package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
- package/docs/proposals/0029-notebook-edit-tool.md +413 -0
- package/docs/proposals/0030-plugin-marketplace.md +360 -0
- package/docs/proposals/0031-command-suggestions.md +295 -0
- package/docs/proposals/0032-ide-integrations.md +328 -0
- package/docs/proposals/0033-enterprise-deployment.md +221 -0
- package/docs/proposals/0034-sandboxing.md +273 -0
- package/docs/proposals/0035-auto-updater.md +311 -0
- package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
- package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
- package/docs/proposals/0038-interactive-cli-ui.md +373 -0
- package/docs/proposals/0039-streaming-enhancements.md +359 -0
- package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
- package/docs/proposals/README.md +84 -0
- package/docs/proposals/TEMPLATE.md +57 -0
- package/docs/proposals/research/claude-code-research.md +307 -0
- package/examples/agent-demo.ts +115 -0
- package/examples/basic.ts +166 -0
- package/package.json +50 -0
- package/src/agent/agent.ts +276 -0
- package/src/agent/index.ts +6 -0
- package/src/agent/types.ts +62 -0
- package/src/cli/components/App.tsx +565 -0
- package/src/cli/components/CommandSuggestions.tsx +58 -0
- package/src/cli/components/Header.tsx +36 -0
- package/src/cli/components/Input.tsx +60 -0
- package/src/cli/components/Logo.tsx +16 -0
- package/src/cli/components/Messages.tsx +210 -0
- package/src/cli/components/ModelSelector.tsx +135 -0
- package/src/cli/components/Spinner.tsx +72 -0
- package/src/cli/components/index.ts +21 -0
- package/src/cli/components/theme.ts +36 -0
- package/src/cli/index.tsx +136 -0
- package/src/config/index.ts +7 -0
- package/src/config/manager.ts +77 -0
- package/src/config/types.ts +25 -0
- package/src/index.ts +86 -0
- package/src/permissions/index.ts +7 -0
- package/src/permissions/manager.ts +97 -0
- package/src/permissions/types.ts +29 -0
- package/src/providers/anthropic.ts +224 -0
- package/src/providers/gemini.ts +295 -0
- package/src/providers/index.ts +97 -0
- package/src/providers/openai.ts +261 -0
- package/src/providers/types.ts +181 -0
- package/src/session/index.ts +6 -0
- package/src/session/manager.ts +354 -0
- package/src/session/types.ts +49 -0
- package/src/tools/builtin/bash.ts +92 -0
- package/src/tools/builtin/edit.ts +37 -0
- package/src/tools/builtin/glob.ts +42 -0
- package/src/tools/builtin/grep.ts +67 -0
- package/src/tools/builtin/read.ts +34 -0
- package/src/tools/builtin/write.ts +27 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/registry.ts +83 -0
- package/src/tools/types.ts +172 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Proposal: KillShell Tool
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0016
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Implement a KillShell tool that terminates background shell processes spawned by the Bash tool. This enables the agent to manage long-running processes, cancel stuck commands, and clean up resources properly.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
With background task execution (Bash with `run_in_background: true`), the agent needs control over running processes:
|
|
16
|
+
|
|
17
|
+
1. **Stuck processes**: Commands may hang and need termination
|
|
18
|
+
2. **Resource cleanup**: Long-running servers need graceful shutdown
|
|
19
|
+
3. **Task cancellation**: User may want to abort running operations
|
|
20
|
+
4. **Process management**: Track and control multiple background tasks
|
|
21
|
+
5. **Memory control**: Prevent runaway processes from consuming resources
|
|
22
|
+
|
|
23
|
+
A KillShell tool provides explicit control over background process lifecycle.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code's KillShell tool works with background Bash commands:
|
|
28
|
+
|
|
29
|
+
### Tool Definition
|
|
30
|
+
```typescript
|
|
31
|
+
KillShell({
|
|
32
|
+
shell_id: "bg-abc123" // ID returned by background Bash command
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Integration with Bash Tool
|
|
37
|
+
```typescript
|
|
38
|
+
// Background command returns shell_id
|
|
39
|
+
Bash({
|
|
40
|
+
command: "npm run dev",
|
|
41
|
+
run_in_background: true
|
|
42
|
+
})
|
|
43
|
+
// Returns: { task_id: "bg-abc123", ... }
|
|
44
|
+
|
|
45
|
+
// Later, terminate the process
|
|
46
|
+
KillShell({
|
|
47
|
+
shell_id: "bg-abc123"
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Key Characteristics
|
|
52
|
+
- Works with shell_id from background tasks
|
|
53
|
+
- Sends appropriate signals (SIGTERM, then SIGKILL)
|
|
54
|
+
- Returns success/failure status
|
|
55
|
+
- Available via /tasks command to view running tasks
|
|
56
|
+
|
|
57
|
+
## Detailed Design
|
|
58
|
+
|
|
59
|
+
### API Design
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// src/tools/kill-shell/types.ts
|
|
63
|
+
interface KillShellInput {
|
|
64
|
+
shell_id: string; // ID of the background shell to kill
|
|
65
|
+
signal?: 'SIGTERM' | 'SIGKILL' | 'SIGINT'; // Signal to send (default: SIGTERM)
|
|
66
|
+
force?: boolean; // Force kill after timeout (default: true)
|
|
67
|
+
timeout?: number; // Ms to wait before SIGKILL (default: 5000)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface KillShellOutput {
|
|
71
|
+
success: boolean;
|
|
72
|
+
shell_id: string;
|
|
73
|
+
signal_sent: string;
|
|
74
|
+
force_killed?: boolean; // True if SIGKILL was needed
|
|
75
|
+
exit_code?: number;
|
|
76
|
+
error?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Related: Background shell tracking
|
|
80
|
+
interface BackgroundShell {
|
|
81
|
+
id: string;
|
|
82
|
+
pid: number;
|
|
83
|
+
command: string;
|
|
84
|
+
started_at: Date;
|
|
85
|
+
status: 'running' | 'completed' | 'killed' | 'error';
|
|
86
|
+
output_file: string;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// src/tools/kill-shell/kill-shell-tool.ts
|
|
92
|
+
const killShellTool: Tool<KillShellInput> = {
|
|
93
|
+
name: 'KillShell',
|
|
94
|
+
description: `Terminate a running background shell process.
|
|
95
|
+
|
|
96
|
+
Parameters:
|
|
97
|
+
- shell_id: The ID of the background shell to kill
|
|
98
|
+
(returned by Bash tool with run_in_background=true)
|
|
99
|
+
- signal: Signal to send (SIGTERM, SIGKILL, SIGINT). Default: SIGTERM
|
|
100
|
+
- force: If true, send SIGKILL after timeout. Default: true
|
|
101
|
+
- timeout: Milliseconds to wait before force kill. Default: 5000
|
|
102
|
+
|
|
103
|
+
Use /tasks command to see running background shells and their IDs.
|
|
104
|
+
|
|
105
|
+
Returns success status and whether force kill was required.
|
|
106
|
+
`,
|
|
107
|
+
parameters: z.object({
|
|
108
|
+
shell_id: z.string(),
|
|
109
|
+
signal: z.enum(['SIGTERM', 'SIGKILL', 'SIGINT']).optional().default('SIGTERM'),
|
|
110
|
+
force: z.boolean().optional().default(true),
|
|
111
|
+
timeout: z.number().int().positive().optional().default(5000)
|
|
112
|
+
}),
|
|
113
|
+
execute: async (input, context) => { ... }
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Implementation Approach
|
|
118
|
+
|
|
119
|
+
1. **Shell Registry**: Track all background shells
|
|
120
|
+
2. **Process Lookup**: Find process by shell_id
|
|
121
|
+
3. **Signal Sending**: Send requested signal
|
|
122
|
+
4. **Wait/Force**: Wait for exit or force kill
|
|
123
|
+
5. **Cleanup**: Remove from registry, close files
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// src/tools/kill-shell/shell-registry.ts
|
|
127
|
+
class ShellRegistry {
|
|
128
|
+
private shells: Map<string, BackgroundShell> = new Map();
|
|
129
|
+
|
|
130
|
+
register(shell: BackgroundShell): void {
|
|
131
|
+
this.shells.set(shell.id, shell);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get(id: string): BackgroundShell | undefined {
|
|
135
|
+
return this.shells.get(id);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
remove(id: string): boolean {
|
|
139
|
+
return this.shells.delete(id);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
listRunning(): BackgroundShell[] {
|
|
143
|
+
return Array.from(this.shells.values())
|
|
144
|
+
.filter(s => s.status === 'running');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
cleanup(): void {
|
|
148
|
+
// Remove completed/killed shells older than 1 hour
|
|
149
|
+
const cutoff = Date.now() - 3600000;
|
|
150
|
+
for (const [id, shell] of this.shells) {
|
|
151
|
+
if (shell.status !== 'running' && shell.started_at.getTime() < cutoff) {
|
|
152
|
+
this.shells.delete(id);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export const shellRegistry = new ShellRegistry();
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Core kill implementation
|
|
163
|
+
async function killShell(input: KillShellInput): Promise<KillShellOutput> {
|
|
164
|
+
const { shell_id, signal = 'SIGTERM', force = true, timeout = 5000 } = input;
|
|
165
|
+
|
|
166
|
+
const shell = shellRegistry.get(shell_id);
|
|
167
|
+
if (!shell) {
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
shell_id,
|
|
171
|
+
signal_sent: signal,
|
|
172
|
+
error: `Shell not found: ${shell_id}`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (shell.status !== 'running') {
|
|
177
|
+
return {
|
|
178
|
+
success: true,
|
|
179
|
+
shell_id,
|
|
180
|
+
signal_sent: 'none',
|
|
181
|
+
error: `Shell already ${shell.status}`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
// Send initial signal
|
|
187
|
+
process.kill(shell.pid, signal);
|
|
188
|
+
shell.status = 'killed';
|
|
189
|
+
|
|
190
|
+
// Wait for process to exit
|
|
191
|
+
const exited = await waitForExit(shell.pid, timeout);
|
|
192
|
+
|
|
193
|
+
if (!exited && force) {
|
|
194
|
+
// Force kill if still running
|
|
195
|
+
process.kill(shell.pid, 'SIGKILL');
|
|
196
|
+
await waitForExit(shell.pid, 1000);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
shell_id,
|
|
201
|
+
signal_sent: signal,
|
|
202
|
+
force_killed: true
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
success: exited,
|
|
208
|
+
shell_id,
|
|
209
|
+
signal_sent: signal,
|
|
210
|
+
force_killed: false
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
// Process may already be dead
|
|
214
|
+
if ((error as NodeJS.ErrnoException).code === 'ESRCH') {
|
|
215
|
+
shell.status = 'completed';
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
shell_id,
|
|
219
|
+
signal_sent: signal,
|
|
220
|
+
error: 'Process already terminated'
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function waitForExit(pid: number, timeout: number): Promise<boolean> {
|
|
228
|
+
const start = Date.now();
|
|
229
|
+
while (Date.now() - start < timeout) {
|
|
230
|
+
try {
|
|
231
|
+
process.kill(pid, 0); // Check if process exists
|
|
232
|
+
await new Promise(r => setTimeout(r, 100));
|
|
233
|
+
} catch {
|
|
234
|
+
return true; // Process exited
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return false; // Timeout
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Enhanced Bash Tool Integration
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// Update to src/tools/builtin/bash.ts
|
|
245
|
+
interface BashInput {
|
|
246
|
+
command: string;
|
|
247
|
+
timeout?: number;
|
|
248
|
+
run_in_background?: boolean; // NEW
|
|
249
|
+
description?: string;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
interface BashOutput {
|
|
253
|
+
success: boolean;
|
|
254
|
+
stdout: string;
|
|
255
|
+
stderr: string;
|
|
256
|
+
exit_code: number;
|
|
257
|
+
// NEW: For background tasks
|
|
258
|
+
task_id?: string;
|
|
259
|
+
output_file?: string;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function executeBash(input: BashInput, context: ToolContext): Promise<BashOutput> {
|
|
263
|
+
if (input.run_in_background) {
|
|
264
|
+
return executeInBackground(input, context);
|
|
265
|
+
}
|
|
266
|
+
return executeSync(input, context);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function executeInBackground(
|
|
270
|
+
input: BashInput,
|
|
271
|
+
context: ToolContext
|
|
272
|
+
): Promise<BashOutput> {
|
|
273
|
+
const shellId = generateShellId();
|
|
274
|
+
const outputFile = path.join(os.tmpdir(), `mycode-${shellId}.log`);
|
|
275
|
+
|
|
276
|
+
const child = spawn('bash', ['-c', input.command], {
|
|
277
|
+
cwd: context.cwd,
|
|
278
|
+
detached: true,
|
|
279
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Setup output logging
|
|
283
|
+
const logStream = fs.createWriteStream(outputFile);
|
|
284
|
+
child.stdout?.pipe(logStream);
|
|
285
|
+
child.stderr?.pipe(logStream);
|
|
286
|
+
|
|
287
|
+
// Register in shell registry
|
|
288
|
+
shellRegistry.register({
|
|
289
|
+
id: shellId,
|
|
290
|
+
pid: child.pid!,
|
|
291
|
+
command: input.command,
|
|
292
|
+
started_at: new Date(),
|
|
293
|
+
status: 'running',
|
|
294
|
+
output_file: outputFile
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Detach process
|
|
298
|
+
child.unref();
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
success: true,
|
|
302
|
+
stdout: '',
|
|
303
|
+
stderr: '',
|
|
304
|
+
exit_code: -1,
|
|
305
|
+
task_id: shellId,
|
|
306
|
+
output_file: outputFile
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### File Changes
|
|
312
|
+
|
|
313
|
+
| File | Action | Description |
|
|
314
|
+
|------|--------|-------------|
|
|
315
|
+
| `src/tools/kill-shell/types.ts` | Create | Type definitions |
|
|
316
|
+
| `src/tools/kill-shell/kill-shell-tool.ts` | Create | Tool implementation |
|
|
317
|
+
| `src/tools/kill-shell/shell-registry.ts` | Create | Background shell tracking |
|
|
318
|
+
| `src/tools/kill-shell/index.ts` | Create | Module exports |
|
|
319
|
+
| `src/tools/builtin/bash.ts` | Modify | Add background execution |
|
|
320
|
+
| `src/tools/index.ts` | Modify | Register KillShell tool |
|
|
321
|
+
| `src/cli/commands/tasks.ts` | Create | /tasks command |
|
|
322
|
+
|
|
323
|
+
## User Experience
|
|
324
|
+
|
|
325
|
+
### View Running Tasks
|
|
326
|
+
```
|
|
327
|
+
User: /tasks
|
|
328
|
+
|
|
329
|
+
Active Background Tasks:
|
|
330
|
+
┌────────────────────────────────────────────────────────────┐
|
|
331
|
+
│ ID Command Started Status │
|
|
332
|
+
├────────────────────────────────────────────────────────────┤
|
|
333
|
+
│ bg-a1b2c3 npm run dev 5 min ago running │
|
|
334
|
+
│ bg-d4e5f6 pytest --watch 2 min ago running │
|
|
335
|
+
│ bg-g7h8i9 docker compose up 10 min ago running │
|
|
336
|
+
└────────────────────────────────────────────────────────────┘
|
|
337
|
+
|
|
338
|
+
Use KillShell tool with shell_id to terminate a task.
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Kill Background Process
|
|
342
|
+
```
|
|
343
|
+
User: Stop the npm dev server
|
|
344
|
+
|
|
345
|
+
Agent: I'll terminate the background npm process.
|
|
346
|
+
|
|
347
|
+
[KillShell: shell_id="bg-a1b2c3"]
|
|
348
|
+
|
|
349
|
+
┌─ Process Terminated ──────────────────────────────┐
|
|
350
|
+
│ Shell ID: bg-a1b2c3 │
|
|
351
|
+
│ Command: npm run dev │
|
|
352
|
+
│ Signal: SIGTERM │
|
|
353
|
+
│ Status: Terminated gracefully │
|
|
354
|
+
└───────────────────────────────────────────────────┘
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Force Kill Hung Process
|
|
358
|
+
```
|
|
359
|
+
Agent: [KillShell: shell_id="bg-stuck", timeout=3000]
|
|
360
|
+
|
|
361
|
+
┌─ Process Force Killed ────────────────────────────┐
|
|
362
|
+
│ Shell ID: bg-stuck │
|
|
363
|
+
│ Signal: SIGTERM → SIGKILL │
|
|
364
|
+
│ Status: Force killed after 3s timeout │
|
|
365
|
+
│ Note: Process did not respond to SIGTERM │
|
|
366
|
+
└───────────────────────────────────────────────────┘
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Shell Not Found
|
|
370
|
+
```
|
|
371
|
+
Agent: [KillShell: shell_id="invalid-id"]
|
|
372
|
+
|
|
373
|
+
Error: Shell not found: invalid-id
|
|
374
|
+
Use /tasks to see available background shells.
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Alternatives Considered
|
|
378
|
+
|
|
379
|
+
### Alternative 1: Kill via Bash
|
|
380
|
+
Use `kill` command through Bash tool.
|
|
381
|
+
|
|
382
|
+
**Pros**: No new tool needed
|
|
383
|
+
**Cons**: Requires PID tracking, shell escaping risks
|
|
384
|
+
**Decision**: Rejected - Dedicated tool is safer
|
|
385
|
+
|
|
386
|
+
### Alternative 2: Automatic Cleanup Only
|
|
387
|
+
Kill processes automatically on session end.
|
|
388
|
+
|
|
389
|
+
**Pros**: Simpler, no user action needed
|
|
390
|
+
**Cons**: No mid-session control
|
|
391
|
+
**Decision**: Rejected - Manual control is essential
|
|
392
|
+
|
|
393
|
+
### Alternative 3: Process Groups
|
|
394
|
+
Use process groups for batch termination.
|
|
395
|
+
|
|
396
|
+
**Pros**: Kill entire process trees
|
|
397
|
+
**Cons**: More complex, may affect other processes
|
|
398
|
+
**Decision**: Deferred - Can add later
|
|
399
|
+
|
|
400
|
+
## Security Considerations
|
|
401
|
+
|
|
402
|
+
1. **Registry Isolation**: Only kill processes we spawned
|
|
403
|
+
2. **PID Validation**: Verify PID still belongs to expected command
|
|
404
|
+
3. **No Arbitrary Signals**: Only allow safe signals
|
|
405
|
+
4. **Cleanup on Exit**: Kill orphaned processes on mycode exit
|
|
406
|
+
5. **User Confirmation**: Consider confirmation for long-running tasks
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// Safety check before kill
|
|
410
|
+
function validateShell(shell: BackgroundShell): boolean {
|
|
411
|
+
try {
|
|
412
|
+
// Verify process exists and matches
|
|
413
|
+
const procCommand = getProcessCommand(shell.pid);
|
|
414
|
+
return procCommand.includes(shell.command.slice(0, 20));
|
|
415
|
+
} catch {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Testing Strategy
|
|
422
|
+
|
|
423
|
+
1. **Unit Tests**:
|
|
424
|
+
- Shell registration/lookup
|
|
425
|
+
- Signal sending
|
|
426
|
+
- Timeout handling
|
|
427
|
+
- Force kill logic
|
|
428
|
+
|
|
429
|
+
2. **Integration Tests**:
|
|
430
|
+
- Background command execution
|
|
431
|
+
- Graceful termination
|
|
432
|
+
- Force kill scenarios
|
|
433
|
+
- Process not found
|
|
434
|
+
|
|
435
|
+
3. **Manual Testing**:
|
|
436
|
+
- Long-running servers
|
|
437
|
+
- Hung processes
|
|
438
|
+
- Multiple concurrent tasks
|
|
439
|
+
|
|
440
|
+
## Migration Path
|
|
441
|
+
|
|
442
|
+
1. **Phase 1**: Basic kill with SIGTERM
|
|
443
|
+
2. **Phase 2**: Force kill with timeout
|
|
444
|
+
3. **Phase 3**: Shell registry and /tasks command
|
|
445
|
+
4. **Phase 4**: Process group support
|
|
446
|
+
5. **Phase 5**: Cleanup automation
|
|
447
|
+
|
|
448
|
+
Requires Enhanced Bash Tool (0028) for full functionality.
|
|
449
|
+
|
|
450
|
+
## References
|
|
451
|
+
|
|
452
|
+
- [Node.js Child Process](https://nodejs.org/api/child_process.html)
|
|
453
|
+
- [Unix Signals](https://man7.org/linux/man-pages/man7/signal.7.html)
|
|
454
|
+
- [Process Groups and Sessions](https://www.win.tue.nl/~aeb/linux/lk/lk-10.html)
|
|
455
|
+
- [Claude Code Background Tasks](https://code.claude.com/docs/en/tools)
|