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,511 @@
|
|
|
1
|
+
# Proposal: Enhanced Bash Tool
|
|
2
|
+
|
|
3
|
+
- **Proposal ID**: 0028
|
|
4
|
+
- **Author**: mycode team
|
|
5
|
+
- **Status**: Draft
|
|
6
|
+
- **Created**: 2025-01-15
|
|
7
|
+
- **Updated**: 2025-01-15
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Enhance the Bash tool with background execution, streaming output, improved timeout handling, and safety features. This enables running long commands without blocking and provides better control over shell operations.
|
|
12
|
+
|
|
13
|
+
## Motivation
|
|
14
|
+
|
|
15
|
+
The current Bash tool has limitations:
|
|
16
|
+
|
|
17
|
+
1. **Blocking only**: Must wait for command completion
|
|
18
|
+
2. **No streaming**: Output only shown after completion
|
|
19
|
+
3. **Fixed timeout**: 30 second limit is too restrictive
|
|
20
|
+
4. **Limited safety**: No command blocking
|
|
21
|
+
5. **No working directory persistence**: Each command is isolated
|
|
22
|
+
|
|
23
|
+
Enhanced Bash enables flexible, safe shell command execution.
|
|
24
|
+
|
|
25
|
+
## Claude Code Reference
|
|
26
|
+
|
|
27
|
+
Claude Code's Bash tool provides rich functionality:
|
|
28
|
+
|
|
29
|
+
### From Tool Description
|
|
30
|
+
```
|
|
31
|
+
- timeout: Optional timeout in milliseconds (up to 600000 / 10 minutes)
|
|
32
|
+
- run_in_background: Run command without waiting for completion
|
|
33
|
+
- description: Clear description of what the command does
|
|
34
|
+
- Output truncated at 30000 characters
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Features
|
|
38
|
+
- Configurable timeout up to 10 minutes
|
|
39
|
+
- Background execution with task ID
|
|
40
|
+
- Command description for clarity
|
|
41
|
+
- Output truncation
|
|
42
|
+
- Sandboxing option
|
|
43
|
+
|
|
44
|
+
### Safety Guidelines
|
|
45
|
+
```
|
|
46
|
+
- Quote paths with spaces
|
|
47
|
+
- Use absolute paths
|
|
48
|
+
- Avoid cd, prefer absolute paths
|
|
49
|
+
- Don't use find/grep/cat (use dedicated tools)
|
|
50
|
+
- Chain commands with && or ;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Detailed Design
|
|
54
|
+
|
|
55
|
+
### API Design
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// src/tools/bash/types.ts
|
|
59
|
+
interface BashInput {
|
|
60
|
+
command: string;
|
|
61
|
+
description?: string; // What this command does
|
|
62
|
+
timeout?: number; // Ms, default 120000, max 600000
|
|
63
|
+
run_in_background?: boolean; // Don't wait for completion
|
|
64
|
+
cwd?: string; // Working directory override
|
|
65
|
+
env?: Record<string, string>; // Additional env vars
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface BashOutput {
|
|
69
|
+
success: boolean;
|
|
70
|
+
stdout: string;
|
|
71
|
+
stderr: string;
|
|
72
|
+
exit_code: number;
|
|
73
|
+
duration_ms: number;
|
|
74
|
+
truncated?: boolean;
|
|
75
|
+
// For background tasks
|
|
76
|
+
task_id?: string;
|
|
77
|
+
output_file?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface BashConfig {
|
|
81
|
+
defaultTimeout: number;
|
|
82
|
+
maxTimeout: number;
|
|
83
|
+
maxOutputSize: number;
|
|
84
|
+
blockedCommands: string[];
|
|
85
|
+
requireDescription: boolean;
|
|
86
|
+
sandboxMode: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const DEFAULT_CONFIG: BashConfig = {
|
|
90
|
+
defaultTimeout: 120000, // 2 minutes
|
|
91
|
+
maxTimeout: 600000, // 10 minutes
|
|
92
|
+
maxOutputSize: 30000, // 30K characters
|
|
93
|
+
blockedCommands: [
|
|
94
|
+
'rm -rf /',
|
|
95
|
+
'mkfs',
|
|
96
|
+
'dd if=/dev/zero',
|
|
97
|
+
':(){:|:&};:', // Fork bomb
|
|
98
|
+
],
|
|
99
|
+
requireDescription: false,
|
|
100
|
+
sandboxMode: false
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Enhanced Bash Tool
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// src/tools/bash/bash-tool.ts
|
|
108
|
+
const bashTool: Tool<BashInput> = {
|
|
109
|
+
name: 'Bash',
|
|
110
|
+
description: `Execute shell commands with configurable options.
|
|
111
|
+
|
|
112
|
+
Parameters:
|
|
113
|
+
- command: The command to execute (required)
|
|
114
|
+
- description: What this command does (recommended)
|
|
115
|
+
- timeout: Timeout in milliseconds (default: 120000, max: 600000)
|
|
116
|
+
- run_in_background: Run without waiting (returns task_id)
|
|
117
|
+
- cwd: Working directory override
|
|
118
|
+
- env: Additional environment variables
|
|
119
|
+
|
|
120
|
+
Best practices:
|
|
121
|
+
- Always quote paths with spaces
|
|
122
|
+
- Use absolute paths when possible
|
|
123
|
+
- Use && to chain dependent commands
|
|
124
|
+
- Use ; to run commands regardless of previous success
|
|
125
|
+
- Prefer dedicated tools over: find, grep, cat, head, tail
|
|
126
|
+
|
|
127
|
+
For long-running commands, use run_in_background: true
|
|
128
|
+
and TaskOutput tool to check results.
|
|
129
|
+
`,
|
|
130
|
+
parameters: z.object({
|
|
131
|
+
command: z.string().min(1),
|
|
132
|
+
description: z.string().optional(),
|
|
133
|
+
timeout: z.number().min(0).max(600000).optional(),
|
|
134
|
+
run_in_background: z.boolean().optional(),
|
|
135
|
+
cwd: z.string().optional(),
|
|
136
|
+
env: z.record(z.string()).optional()
|
|
137
|
+
}),
|
|
138
|
+
execute: async (input, context) => {
|
|
139
|
+
return bashExecutor.execute(input, context);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Bash Executor
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// src/tools/bash/executor.ts
|
|
148
|
+
class BashExecutor {
|
|
149
|
+
private config: BashConfig;
|
|
150
|
+
private taskManager: TaskManager;
|
|
151
|
+
|
|
152
|
+
constructor(config?: Partial<BashConfig>) {
|
|
153
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
154
|
+
this.taskManager = new TaskManager();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async execute(input: BashInput, context: ToolContext): Promise<BashOutput> {
|
|
158
|
+
// Safety check
|
|
159
|
+
const safetyResult = this.checkSafety(input.command);
|
|
160
|
+
if (!safetyResult.safe) {
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
stdout: '',
|
|
164
|
+
stderr: safetyResult.reason,
|
|
165
|
+
exit_code: -1,
|
|
166
|
+
duration_ms: 0
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Determine execution mode
|
|
171
|
+
if (input.run_in_background) {
|
|
172
|
+
return this.executeBackground(input, context);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return this.executeForeground(input, context);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private async executeForeground(
|
|
179
|
+
input: BashInput,
|
|
180
|
+
context: ToolContext
|
|
181
|
+
): Promise<BashOutput> {
|
|
182
|
+
const timeout = Math.min(
|
|
183
|
+
input.timeout || this.config.defaultTimeout,
|
|
184
|
+
this.config.maxTimeout
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const cwd = input.cwd || context.cwd;
|
|
188
|
+
const start = Date.now();
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const { stdout, stderr } = await execAsync(input.command, {
|
|
192
|
+
cwd,
|
|
193
|
+
timeout,
|
|
194
|
+
maxBuffer: this.config.maxOutputSize * 2,
|
|
195
|
+
env: { ...process.env, ...input.env },
|
|
196
|
+
shell: '/bin/bash'
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
success: true,
|
|
201
|
+
stdout: this.truncate(stdout),
|
|
202
|
+
stderr: this.truncate(stderr),
|
|
203
|
+
exit_code: 0,
|
|
204
|
+
duration_ms: Date.now() - start,
|
|
205
|
+
truncated: stdout.length > this.config.maxOutputSize
|
|
206
|
+
};
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const execError = error as ExecException;
|
|
209
|
+
return {
|
|
210
|
+
success: false,
|
|
211
|
+
stdout: this.truncate(execError.stdout || ''),
|
|
212
|
+
stderr: this.truncate(execError.stderr || execError.message),
|
|
213
|
+
exit_code: execError.code || 1,
|
|
214
|
+
duration_ms: Date.now() - start
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private async executeBackground(
|
|
220
|
+
input: BashInput,
|
|
221
|
+
context: ToolContext
|
|
222
|
+
): Promise<BashOutput> {
|
|
223
|
+
const taskId = generateTaskId('bash');
|
|
224
|
+
const outputFile = path.join(os.tmpdir(), `mycode-${taskId}.log`);
|
|
225
|
+
const cwd = input.cwd || context.cwd;
|
|
226
|
+
|
|
227
|
+
const child = spawn('bash', ['-c', input.command], {
|
|
228
|
+
cwd,
|
|
229
|
+
detached: true,
|
|
230
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
231
|
+
env: { ...process.env, ...input.env }
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Setup output logging
|
|
235
|
+
const logStream = fs.createWriteStream(outputFile);
|
|
236
|
+
child.stdout?.pipe(logStream);
|
|
237
|
+
child.stderr?.pipe(logStream);
|
|
238
|
+
|
|
239
|
+
// Register with task manager
|
|
240
|
+
this.taskManager.register({
|
|
241
|
+
id: taskId,
|
|
242
|
+
type: 'bash',
|
|
243
|
+
command: input.command,
|
|
244
|
+
description: input.description,
|
|
245
|
+
pid: child.pid!,
|
|
246
|
+
outputFile,
|
|
247
|
+
startedAt: new Date(),
|
|
248
|
+
status: 'running'
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Handle completion
|
|
252
|
+
child.on('exit', (code) => {
|
|
253
|
+
this.taskManager.complete(taskId, code || 0);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Detach
|
|
257
|
+
child.unref();
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
success: true,
|
|
261
|
+
stdout: `Background task started: ${taskId}`,
|
|
262
|
+
stderr: '',
|
|
263
|
+
exit_code: 0,
|
|
264
|
+
duration_ms: 0,
|
|
265
|
+
task_id: taskId,
|
|
266
|
+
output_file: outputFile
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private checkSafety(command: string): { safe: boolean; reason: string } {
|
|
271
|
+
// Check blocked patterns
|
|
272
|
+
for (const blocked of this.config.blockedCommands) {
|
|
273
|
+
if (command.includes(blocked)) {
|
|
274
|
+
return {
|
|
275
|
+
safe: false,
|
|
276
|
+
reason: `Command contains blocked pattern: ${blocked}`
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Warn about dangerous patterns
|
|
282
|
+
const dangerous = [
|
|
283
|
+
{ pattern: /rm\s+-rf\s+\//, reason: 'Recursive delete from root' },
|
|
284
|
+
{ pattern: />\s*\/dev\/sd/, reason: 'Writing to block device' },
|
|
285
|
+
{ pattern: /chmod\s+-R\s+777/, reason: 'Insecure permissions' },
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
for (const { pattern, reason } of dangerous) {
|
|
289
|
+
if (pattern.test(command)) {
|
|
290
|
+
return { safe: false, reason };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return { safe: true, reason: '' };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private truncate(output: string): string {
|
|
298
|
+
if (output.length <= this.config.maxOutputSize) {
|
|
299
|
+
return output;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const half = Math.floor(this.config.maxOutputSize / 2);
|
|
303
|
+
const truncated = output.length - this.config.maxOutputSize;
|
|
304
|
+
|
|
305
|
+
return `${output.slice(0, half)}\n\n... (${truncated} characters truncated) ...\n\n${output.slice(-half)}`;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export const bashExecutor = new BashExecutor();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Streaming Support
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// src/tools/bash/streaming.ts
|
|
316
|
+
async function* executeWithStreaming(
|
|
317
|
+
command: string,
|
|
318
|
+
options: SpawnOptions
|
|
319
|
+
): AsyncGenerator<StreamEvent> {
|
|
320
|
+
const child = spawn('bash', ['-c', command], {
|
|
321
|
+
...options,
|
|
322
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Stream stdout
|
|
326
|
+
if (child.stdout) {
|
|
327
|
+
child.stdout.on('data', (data: Buffer) => {
|
|
328
|
+
// Yield to event loop
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Stream stderr
|
|
333
|
+
if (child.stderr) {
|
|
334
|
+
child.stderr.on('data', (data: Buffer) => {
|
|
335
|
+
// Yield to event loop
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Wait for completion
|
|
340
|
+
const exitCode = await new Promise<number>((resolve) => {
|
|
341
|
+
child.on('exit', (code) => resolve(code || 0));
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
yield { type: 'exit', code: exitCode };
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### File Changes
|
|
349
|
+
|
|
350
|
+
| File | Action | Description |
|
|
351
|
+
|------|--------|-------------|
|
|
352
|
+
| `src/tools/bash/types.ts` | Create | Type definitions |
|
|
353
|
+
| `src/tools/bash/bash-tool.ts` | Modify | Enhanced tool |
|
|
354
|
+
| `src/tools/bash/executor.ts` | Create | Command execution |
|
|
355
|
+
| `src/tools/bash/safety.ts` | Create | Safety checks |
|
|
356
|
+
| `src/tools/bash/streaming.ts` | Create | Streaming output |
|
|
357
|
+
| `src/tools/bash/index.ts` | Modify | Updated exports |
|
|
358
|
+
| `src/tasks/task-manager.ts` | Modify | Background task support |
|
|
359
|
+
|
|
360
|
+
## User Experience
|
|
361
|
+
|
|
362
|
+
### Foreground with Description
|
|
363
|
+
```
|
|
364
|
+
Agent: [Bash: command="npm run build", description="Build the project"]
|
|
365
|
+
|
|
366
|
+
Running: Build the project
|
|
367
|
+
|
|
368
|
+
✓ Command completed (4.2s)
|
|
369
|
+
stdout:
|
|
370
|
+
> mycode@1.0.0 build
|
|
371
|
+
> tsc
|
|
372
|
+
|
|
373
|
+
Build completed successfully.
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Background Execution
|
|
377
|
+
```
|
|
378
|
+
Agent: [Bash: command="npm test", run_in_background=true, description="Run tests"]
|
|
379
|
+
|
|
380
|
+
┌─ Background Task Started ─────────────────────────┐
|
|
381
|
+
│ Task ID: bash-abc123 │
|
|
382
|
+
│ Command: npm test │
|
|
383
|
+
│ Output: /tmp/mycode-bash-abc123.log │
|
|
384
|
+
│ │
|
|
385
|
+
│ Use /tasks to check status │
|
|
386
|
+
│ Use TaskOutput to get results │
|
|
387
|
+
└───────────────────────────────────────────────────┘
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Streaming Output (Long Command)
|
|
391
|
+
```
|
|
392
|
+
Agent: [Bash: command="npm install", timeout=300000]
|
|
393
|
+
|
|
394
|
+
Running: npm install
|
|
395
|
+
────────────────────────────────────────────────────
|
|
396
|
+
added 1 package in 1s
|
|
397
|
+
⠋ Installing dependencies...
|
|
398
|
+
added 50 packages in 5s
|
|
399
|
+
⠙ Installing dependencies...
|
|
400
|
+
added 200 packages in 15s
|
|
401
|
+
✓ added 523 packages in 32s
|
|
402
|
+
────────────────────────────────────────────────────
|
|
403
|
+
Exit code: 0
|
|
404
|
+
Duration: 32.4s
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Safety Block
|
|
408
|
+
```
|
|
409
|
+
Agent: [Bash: command="rm -rf /"]
|
|
410
|
+
|
|
411
|
+
⚠️ Command Blocked
|
|
412
|
+
|
|
413
|
+
This command matches a blocked pattern:
|
|
414
|
+
"rm -rf /" - Recursive delete from root
|
|
415
|
+
|
|
416
|
+
This operation is not allowed for safety.
|
|
417
|
+
If you need to delete files, specify exact paths.
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Output Truncation
|
|
421
|
+
```
|
|
422
|
+
Agent: [Bash: command="cat large-file.log"]
|
|
423
|
+
|
|
424
|
+
Output (truncated):
|
|
425
|
+
────────────────────────────────────────────────────
|
|
426
|
+
[2024-01-15 10:00:00] Starting application...
|
|
427
|
+
[2024-01-15 10:00:01] Loading configuration...
|
|
428
|
+
...
|
|
429
|
+
|
|
430
|
+
... (45,678 characters truncated) ...
|
|
431
|
+
|
|
432
|
+
[2024-01-15 10:15:42] Request completed
|
|
433
|
+
[2024-01-15 10:15:43] Shutting down...
|
|
434
|
+
────────────────────────────────────────────────────
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Alternatives Considered
|
|
438
|
+
|
|
439
|
+
### Alternative 1: PTY-based Execution
|
|
440
|
+
Use pseudo-terminal for full interactivity.
|
|
441
|
+
|
|
442
|
+
**Pros**: Interactive commands work
|
|
443
|
+
**Cons**: Complex, security concerns
|
|
444
|
+
**Decision**: Deferred - Consider for advanced use
|
|
445
|
+
|
|
446
|
+
### Alternative 2: Sandboxed Containers
|
|
447
|
+
Run commands in Docker/Podman.
|
|
448
|
+
|
|
449
|
+
**Pros**: Full isolation
|
|
450
|
+
**Cons**: Heavy, requires container runtime
|
|
451
|
+
**Decision**: Optional via sandboxMode config
|
|
452
|
+
|
|
453
|
+
### Alternative 3: PowerShell Support
|
|
454
|
+
Add Windows PowerShell support.
|
|
455
|
+
|
|
456
|
+
**Pros**: Cross-platform
|
|
457
|
+
**Cons**: Different syntax, complexity
|
|
458
|
+
**Decision**: Deferred - Focus on Bash first
|
|
459
|
+
|
|
460
|
+
## Security Considerations
|
|
461
|
+
|
|
462
|
+
1. **Command Blocking**: Block dangerous patterns
|
|
463
|
+
2. **Path Injection**: Validate file paths
|
|
464
|
+
3. **Environment Isolation**: Don't leak sensitive env vars
|
|
465
|
+
4. **Resource Limits**: Limit CPU/memory usage
|
|
466
|
+
5. **Timeout Enforcement**: Kill hung processes
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
const SAFETY_PATTERNS = [
|
|
470
|
+
/rm\s+-rf\s+\//,
|
|
471
|
+
/mkfs/,
|
|
472
|
+
/dd\s+if=.*of=\/dev/,
|
|
473
|
+
/>\s*\/etc/,
|
|
474
|
+
/curl.*\|\s*bash/, // Pipe to shell
|
|
475
|
+
];
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Testing Strategy
|
|
479
|
+
|
|
480
|
+
1. **Unit Tests**:
|
|
481
|
+
- Command parsing
|
|
482
|
+
- Safety checks
|
|
483
|
+
- Output truncation
|
|
484
|
+
- Timeout handling
|
|
485
|
+
|
|
486
|
+
2. **Integration Tests**:
|
|
487
|
+
- Background execution
|
|
488
|
+
- Streaming output
|
|
489
|
+
- Error handling
|
|
490
|
+
|
|
491
|
+
3. **Security Tests**:
|
|
492
|
+
- Blocked commands
|
|
493
|
+
- Injection attempts
|
|
494
|
+
- Resource limits
|
|
495
|
+
|
|
496
|
+
## Migration Path
|
|
497
|
+
|
|
498
|
+
1. **Phase 1**: Configurable timeout
|
|
499
|
+
2. **Phase 2**: Background execution
|
|
500
|
+
3. **Phase 3**: Streaming output
|
|
501
|
+
4. **Phase 4**: Enhanced safety
|
|
502
|
+
5. **Phase 5**: Sandboxing option
|
|
503
|
+
|
|
504
|
+
Backward compatible with existing Bash usage.
|
|
505
|
+
|
|
506
|
+
## References
|
|
507
|
+
|
|
508
|
+
- [Node.js Child Process](https://nodejs.org/api/child_process.html)
|
|
509
|
+
- [Bash Reference Manual](https://www.gnu.org/software/bash/manual/)
|
|
510
|
+
- [Shell Command Injection Prevention](https://owasp.org/www-community/attacks/Command_Injection)
|
|
511
|
+
- [Container Sandboxing](https://docs.docker.com/engine/security/)
|