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.
Files changed (274) hide show
  1. package/.env.example +11 -0
  2. package/CLAUDE.md +70 -0
  3. package/LICENSE +21 -0
  4. package/README.md +117 -0
  5. package/dist/agent/agent.d.ts +84 -0
  6. package/dist/agent/agent.d.ts.map +1 -0
  7. package/dist/agent/agent.js +233 -0
  8. package/dist/agent/agent.js.map +1 -0
  9. package/dist/agent/index.d.ts +6 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +6 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/types.d.ts +47 -0
  14. package/dist/agent/types.d.ts.map +1 -0
  15. package/dist/agent/types.js +5 -0
  16. package/dist/agent/types.js.map +1 -0
  17. package/dist/cli/components/App.d.ts +14 -0
  18. package/dist/cli/components/App.d.ts.map +1 -0
  19. package/dist/cli/components/App.js +395 -0
  20. package/dist/cli/components/App.js.map +1 -0
  21. package/dist/cli/components/CommandSuggestions.d.ts +13 -0
  22. package/dist/cli/components/CommandSuggestions.d.ts.map +1 -0
  23. package/dist/cli/components/CommandSuggestions.js +32 -0
  24. package/dist/cli/components/CommandSuggestions.js.map +1 -0
  25. package/dist/cli/components/Header.d.ts +9 -0
  26. package/dist/cli/components/Header.d.ts.map +1 -0
  27. package/dist/cli/components/Header.js +13 -0
  28. package/dist/cli/components/Header.js.map +1 -0
  29. package/dist/cli/components/Input.d.ts +13 -0
  30. package/dist/cli/components/Input.d.ts.map +1 -0
  31. package/dist/cli/components/Input.js +27 -0
  32. package/dist/cli/components/Input.js.map +1 -0
  33. package/dist/cli/components/Logo.d.ts +2 -0
  34. package/dist/cli/components/Logo.d.ts.map +1 -0
  35. package/dist/cli/components/Logo.js +8 -0
  36. package/dist/cli/components/Logo.js.map +1 -0
  37. package/dist/cli/components/Messages.d.ts +37 -0
  38. package/dist/cli/components/Messages.d.ts.map +1 -0
  39. package/dist/cli/components/Messages.js +106 -0
  40. package/dist/cli/components/Messages.js.map +1 -0
  41. package/dist/cli/components/ModelSelector.d.ts +13 -0
  42. package/dist/cli/components/ModelSelector.d.ts.map +1 -0
  43. package/dist/cli/components/ModelSelector.js +72 -0
  44. package/dist/cli/components/ModelSelector.js.map +1 -0
  45. package/dist/cli/components/Spinner.d.ts +12 -0
  46. package/dist/cli/components/Spinner.d.ts.map +1 -0
  47. package/dist/cli/components/Spinner.js +45 -0
  48. package/dist/cli/components/Spinner.js.map +1 -0
  49. package/dist/cli/components/index.d.ts +12 -0
  50. package/dist/cli/components/index.d.ts.map +1 -0
  51. package/dist/cli/components/index.js +12 -0
  52. package/dist/cli/components/index.js.map +1 -0
  53. package/dist/cli/components/theme.d.ts +31 -0
  54. package/dist/cli/components/theme.d.ts.map +1 -0
  55. package/dist/cli/components/theme.js +36 -0
  56. package/dist/cli/components/theme.js.map +1 -0
  57. package/dist/cli/index-legacy.d.ts +7 -0
  58. package/dist/cli/index-legacy.d.ts.map +1 -0
  59. package/dist/cli/index-legacy.js +431 -0
  60. package/dist/cli/index-legacy.js.map +1 -0
  61. package/dist/cli/index.d.ts +7 -0
  62. package/dist/cli/index.d.ts.map +1 -0
  63. package/dist/cli/index.js +116 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/ink-cli.d.ts +7 -0
  66. package/dist/cli/ink-cli.d.ts.map +1 -0
  67. package/dist/cli/ink-cli.js +105 -0
  68. package/dist/cli/ink-cli.js.map +1 -0
  69. package/dist/cli/session-picker.d.ts +16 -0
  70. package/dist/cli/session-picker.d.ts.map +1 -0
  71. package/dist/cli/session-picker.js +280 -0
  72. package/dist/cli/session-picker.js.map +1 -0
  73. package/dist/cli/ui.d.ts +61 -0
  74. package/dist/cli/ui.d.ts.map +1 -0
  75. package/dist/cli/ui.js +364 -0
  76. package/dist/cli/ui.js.map +1 -0
  77. package/dist/config/index.d.ts +7 -0
  78. package/dist/config/index.d.ts.map +1 -0
  79. package/dist/config/index.js +6 -0
  80. package/dist/config/index.js.map +1 -0
  81. package/dist/config/manager.d.ts +31 -0
  82. package/dist/config/manager.d.ts.map +1 -0
  83. package/dist/config/manager.js +65 -0
  84. package/dist/config/manager.js.map +1 -0
  85. package/dist/config/types.d.ts +22 -0
  86. package/dist/config/types.d.ts.map +1 -0
  87. package/dist/config/types.js +6 -0
  88. package/dist/config/types.js.map +1 -0
  89. package/dist/index.d.ts +12 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +21 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/memory/index.d.ts +10 -0
  94. package/dist/memory/index.d.ts.map +1 -0
  95. package/dist/memory/index.js +9 -0
  96. package/dist/memory/index.js.map +1 -0
  97. package/dist/memory/init.d.ts +20 -0
  98. package/dist/memory/init.d.ts.map +1 -0
  99. package/dist/memory/init.js +332 -0
  100. package/dist/memory/init.js.map +1 -0
  101. package/dist/memory/manager.d.ts +85 -0
  102. package/dist/memory/manager.d.ts.map +1 -0
  103. package/dist/memory/manager.js +234 -0
  104. package/dist/memory/manager.js.map +1 -0
  105. package/dist/memory/types.d.ts +74 -0
  106. package/dist/memory/types.d.ts.map +1 -0
  107. package/dist/memory/types.js +6 -0
  108. package/dist/memory/types.js.map +1 -0
  109. package/dist/permissions/index.d.ts +7 -0
  110. package/dist/permissions/index.d.ts.map +1 -0
  111. package/dist/permissions/index.js +6 -0
  112. package/dist/permissions/index.js.map +1 -0
  113. package/dist/permissions/manager.d.ts +32 -0
  114. package/dist/permissions/manager.d.ts.map +1 -0
  115. package/dist/permissions/manager.js +79 -0
  116. package/dist/permissions/manager.js.map +1 -0
  117. package/dist/permissions/types.d.ts +14 -0
  118. package/dist/permissions/types.d.ts.map +1 -0
  119. package/dist/permissions/types.js +17 -0
  120. package/dist/permissions/types.js.map +1 -0
  121. package/dist/providers/anthropic.d.ts +20 -0
  122. package/dist/providers/anthropic.d.ts.map +1 -0
  123. package/dist/providers/anthropic.js +185 -0
  124. package/dist/providers/anthropic.js.map +1 -0
  125. package/dist/providers/gemini.d.ts +21 -0
  126. package/dist/providers/gemini.d.ts.map +1 -0
  127. package/dist/providers/gemini.js +241 -0
  128. package/dist/providers/gemini.js.map +1 -0
  129. package/dist/providers/index.d.ts +34 -0
  130. package/dist/providers/index.d.ts.map +1 -0
  131. package/dist/providers/index.js +72 -0
  132. package/dist/providers/index.js.map +1 -0
  133. package/dist/providers/openai.d.ts +19 -0
  134. package/dist/providers/openai.d.ts.map +1 -0
  135. package/dist/providers/openai.js +221 -0
  136. package/dist/providers/openai.js.map +1 -0
  137. package/dist/providers/types.d.ts +125 -0
  138. package/dist/providers/types.d.ts.map +1 -0
  139. package/dist/providers/types.js +6 -0
  140. package/dist/providers/types.js.map +1 -0
  141. package/dist/session/index.d.ts +6 -0
  142. package/dist/session/index.d.ts.map +1 -0
  143. package/dist/session/index.js +6 -0
  144. package/dist/session/index.js.map +1 -0
  145. package/dist/session/manager.d.ts +101 -0
  146. package/dist/session/manager.d.ts.map +1 -0
  147. package/dist/session/manager.js +295 -0
  148. package/dist/session/manager.js.map +1 -0
  149. package/dist/session/types.d.ts +39 -0
  150. package/dist/session/types.d.ts.map +1 -0
  151. package/dist/session/types.js +10 -0
  152. package/dist/session/types.js.map +1 -0
  153. package/dist/tools/builtin/bash.d.ts +7 -0
  154. package/dist/tools/builtin/bash.d.ts.map +1 -0
  155. package/dist/tools/builtin/bash.js +80 -0
  156. package/dist/tools/builtin/bash.js.map +1 -0
  157. package/dist/tools/builtin/edit.d.ts +7 -0
  158. package/dist/tools/builtin/edit.d.ts.map +1 -0
  159. package/dist/tools/builtin/edit.js +32 -0
  160. package/dist/tools/builtin/edit.js.map +1 -0
  161. package/dist/tools/builtin/glob.d.ts +7 -0
  162. package/dist/tools/builtin/glob.d.ts.map +1 -0
  163. package/dist/tools/builtin/glob.js +36 -0
  164. package/dist/tools/builtin/glob.js.map +1 -0
  165. package/dist/tools/builtin/grep.d.ts +7 -0
  166. package/dist/tools/builtin/grep.d.ts.map +1 -0
  167. package/dist/tools/builtin/grep.js +59 -0
  168. package/dist/tools/builtin/grep.js.map +1 -0
  169. package/dist/tools/builtin/read.d.ts +7 -0
  170. package/dist/tools/builtin/read.d.ts.map +1 -0
  171. package/dist/tools/builtin/read.js +29 -0
  172. package/dist/tools/builtin/read.js.map +1 -0
  173. package/dist/tools/builtin/write.d.ts +7 -0
  174. package/dist/tools/builtin/write.d.ts.map +1 -0
  175. package/dist/tools/builtin/write.js +24 -0
  176. package/dist/tools/builtin/write.js.map +1 -0
  177. package/dist/tools/index.d.ts +38 -0
  178. package/dist/tools/index.d.ts.map +1 -0
  179. package/dist/tools/index.js +32 -0
  180. package/dist/tools/index.js.map +1 -0
  181. package/dist/tools/registry.d.ts +22 -0
  182. package/dist/tools/registry.d.ts.map +1 -0
  183. package/dist/tools/registry.js +71 -0
  184. package/dist/tools/registry.js.map +1 -0
  185. package/dist/tools/types.d.ts +62 -0
  186. package/dist/tools/types.d.ts.map +1 -0
  187. package/dist/tools/types.js +126 -0
  188. package/dist/tools/types.js.map +1 -0
  189. package/docs/README.md +16 -0
  190. package/docs/proposals/0001-web-fetch-tool.md +293 -0
  191. package/docs/proposals/0002-web-search-tool.md +306 -0
  192. package/docs/proposals/0003-task-subagents.md +333 -0
  193. package/docs/proposals/0004-plan-mode.md +338 -0
  194. package/docs/proposals/0005-todo-system.md +299 -0
  195. package/docs/proposals/0006-memory-system.md +539 -0
  196. package/docs/proposals/0007-context-management.md +429 -0
  197. package/docs/proposals/0008-checkpointing.md +327 -0
  198. package/docs/proposals/0009-hooks-system.md +343 -0
  199. package/docs/proposals/0010-mcp-integration.md +382 -0
  200. package/docs/proposals/0011-custom-commands.md +374 -0
  201. package/docs/proposals/0012-ask-user-question.md +317 -0
  202. package/docs/proposals/0013-multi-edit-tool.md +345 -0
  203. package/docs/proposals/0014-lsp-tool.md +478 -0
  204. package/docs/proposals/0015-ls-tool.md +407 -0
  205. package/docs/proposals/0016-kill-shell-tool.md +455 -0
  206. package/docs/proposals/0017-background-tasks.md +489 -0
  207. package/docs/proposals/0018-parallel-tool-execution.md +415 -0
  208. package/docs/proposals/0019-session-enhancements.md +462 -0
  209. package/docs/proposals/0020-session-summarization.md +447 -0
  210. package/docs/proposals/0021-skills-system.md +409 -0
  211. package/docs/proposals/0022-plugin-system.md +467 -0
  212. package/docs/proposals/0023-permission-enhancements.md +470 -0
  213. package/docs/proposals/0024-keyboard-shortcuts.md +443 -0
  214. package/docs/proposals/0025-cost-tracking.md +447 -0
  215. package/docs/proposals/0026-git-integration.md +475 -0
  216. package/docs/proposals/0027-enhanced-read-tool.md +514 -0
  217. package/docs/proposals/0028-enhanced-bash-tool.md +511 -0
  218. package/docs/proposals/0029-notebook-edit-tool.md +413 -0
  219. package/docs/proposals/0030-plugin-marketplace.md +360 -0
  220. package/docs/proposals/0031-command-suggestions.md +295 -0
  221. package/docs/proposals/0032-ide-integrations.md +328 -0
  222. package/docs/proposals/0033-enterprise-deployment.md +221 -0
  223. package/docs/proposals/0034-sandboxing.md +273 -0
  224. package/docs/proposals/0035-auto-updater.md +311 -0
  225. package/docs/proposals/0036-enhanced-glob-tool.md +267 -0
  226. package/docs/proposals/0037-enhanced-grep-tool.md +360 -0
  227. package/docs/proposals/0038-interactive-cli-ui.md +373 -0
  228. package/docs/proposals/0039-streaming-enhancements.md +359 -0
  229. package/docs/proposals/0040-multi-provider-enhancements.md +369 -0
  230. package/docs/proposals/README.md +84 -0
  231. package/docs/proposals/TEMPLATE.md +57 -0
  232. package/docs/proposals/research/claude-code-research.md +307 -0
  233. package/examples/agent-demo.ts +115 -0
  234. package/examples/basic.ts +166 -0
  235. package/package.json +50 -0
  236. package/src/agent/agent.ts +276 -0
  237. package/src/agent/index.ts +6 -0
  238. package/src/agent/types.ts +62 -0
  239. package/src/cli/components/App.tsx +565 -0
  240. package/src/cli/components/CommandSuggestions.tsx +58 -0
  241. package/src/cli/components/Header.tsx +36 -0
  242. package/src/cli/components/Input.tsx +60 -0
  243. package/src/cli/components/Logo.tsx +16 -0
  244. package/src/cli/components/Messages.tsx +210 -0
  245. package/src/cli/components/ModelSelector.tsx +135 -0
  246. package/src/cli/components/Spinner.tsx +72 -0
  247. package/src/cli/components/index.ts +21 -0
  248. package/src/cli/components/theme.ts +36 -0
  249. package/src/cli/index.tsx +136 -0
  250. package/src/config/index.ts +7 -0
  251. package/src/config/manager.ts +77 -0
  252. package/src/config/types.ts +25 -0
  253. package/src/index.ts +86 -0
  254. package/src/permissions/index.ts +7 -0
  255. package/src/permissions/manager.ts +97 -0
  256. package/src/permissions/types.ts +29 -0
  257. package/src/providers/anthropic.ts +224 -0
  258. package/src/providers/gemini.ts +295 -0
  259. package/src/providers/index.ts +97 -0
  260. package/src/providers/openai.ts +261 -0
  261. package/src/providers/types.ts +181 -0
  262. package/src/session/index.ts +6 -0
  263. package/src/session/manager.ts +354 -0
  264. package/src/session/types.ts +49 -0
  265. package/src/tools/builtin/bash.ts +92 -0
  266. package/src/tools/builtin/edit.ts +37 -0
  267. package/src/tools/builtin/glob.ts +42 -0
  268. package/src/tools/builtin/grep.ts +67 -0
  269. package/src/tools/builtin/read.ts +34 -0
  270. package/src/tools/builtin/write.ts +27 -0
  271. package/src/tools/index.ts +36 -0
  272. package/src/tools/registry.ts +83 -0
  273. package/src/tools/types.ts +172 -0
  274. package/tsconfig.json +21 -0
@@ -0,0 +1,489 @@
1
+ # Proposal: Background Tasks & TaskOutput
2
+
3
+ - **Proposal ID**: 0017
4
+ - **Author**: mycode team
5
+ - **Status**: Draft
6
+ - **Created**: 2025-01-15
7
+ - **Updated**: 2025-01-15
8
+
9
+ ## Summary
10
+
11
+ Implement a comprehensive background task system with a TaskOutput tool for retrieving results from asynchronous operations. This enables non-blocking execution of long-running tasks like builds, tests, and subagent operations.
12
+
13
+ ## Motivation
14
+
15
+ Currently, mycode executes all operations synchronously, blocking the conversation:
16
+
17
+ 1. **Blocking execution**: Long builds freeze the conversation
18
+ 2. **No parallel work**: Can't work while tests run
19
+ 3. **Timeout issues**: Long tasks may timeout
20
+ 4. **Poor UX**: User waits with no progress indication
21
+ 5. **Lost results**: Can't check output of past commands
22
+
23
+ A background task system enables async operations with result retrieval.
24
+
25
+ ## Claude Code Reference
26
+
27
+ Claude Code provides TaskOutput tool for background task management:
28
+
29
+ ### TaskOutput Tool
30
+ ```typescript
31
+ TaskOutput({
32
+ task_id: "agent-abc123",
33
+ block: true, // Wait for completion (default: true)
34
+ timeout: 30000 // Max wait time in ms
35
+ })
36
+ ```
37
+
38
+ ### Background Execution Pattern
39
+ ```typescript
40
+ // Launch background task
41
+ Task({
42
+ description: "Run tests",
43
+ prompt: "Execute the full test suite",
44
+ subagent_type: "Bash",
45
+ run_in_background: true
46
+ })
47
+ // Returns: { task_id: "agent-abc123", output_file: "/path/to/output.log" }
48
+
49
+ // Check status later
50
+ TaskOutput({
51
+ task_id: "agent-abc123",
52
+ block: false // Non-blocking check
53
+ })
54
+ // Returns current status and partial output
55
+ ```
56
+
57
+ ### Key Characteristics
58
+ - Works with Task tool and Bash tool
59
+ - Supports blocking and non-blocking modes
60
+ - Configurable timeout up to 10 minutes
61
+ - Returns task status and output
62
+ - Available via /tasks command
63
+
64
+ ## Detailed Design
65
+
66
+ ### API Design
67
+
68
+ ```typescript
69
+ // src/tasks/types.ts
70
+ type TaskStatus = 'pending' | 'running' | 'completed' | 'error' | 'cancelled';
71
+ type TaskType = 'bash' | 'agent' | 'remote';
72
+
73
+ interface BackgroundTask {
74
+ id: string;
75
+ type: TaskType;
76
+ description: string;
77
+ status: TaskStatus;
78
+ started_at: Date;
79
+ completed_at?: Date;
80
+ output_file: string;
81
+ exit_code?: number;
82
+ error?: string;
83
+ metadata?: Record<string, unknown>;
84
+ }
85
+
86
+ interface TaskOutputInput {
87
+ task_id: string;
88
+ block?: boolean; // Wait for completion (default: true)
89
+ timeout?: number; // Max wait time in ms (default: 30000, max: 600000)
90
+ }
91
+
92
+ interface TaskOutputOutput {
93
+ success: boolean;
94
+ task_id: string;
95
+ status: TaskStatus;
96
+ output?: string; // Full or partial output
97
+ exit_code?: number;
98
+ error?: string;
99
+ elapsed_ms?: number; // Time since task started
100
+ truncated?: boolean; // Output was truncated
101
+ }
102
+ ```
103
+
104
+ ```typescript
105
+ // src/tools/task-output/task-output-tool.ts
106
+ const taskOutputTool: Tool<TaskOutputInput> = {
107
+ name: 'TaskOutput',
108
+ description: `Retrieve output from a running or completed background task.
109
+
110
+ Parameters:
111
+ - task_id: The ID of the background task
112
+ - block: If true (default), wait for task completion
113
+ - timeout: Maximum wait time in milliseconds (default: 30000, max: 600000)
114
+
115
+ Use block=false for non-blocking status check.
116
+ Use /tasks command to see all running/completed tasks.
117
+
118
+ Returns task status, output content, and exit code if completed.
119
+ `,
120
+ parameters: z.object({
121
+ task_id: z.string(),
122
+ block: z.boolean().optional().default(true),
123
+ timeout: z.number().min(0).max(600000).optional().default(30000)
124
+ }),
125
+ execute: async (input, context) => { ... }
126
+ };
127
+ ```
128
+
129
+ ### Implementation Approach
130
+
131
+ ```typescript
132
+ // src/tasks/task-manager.ts
133
+ class TaskManager {
134
+ private tasks: Map<string, BackgroundTask> = new Map();
135
+ private processes: Map<string, ChildProcess> = new Map();
136
+ private outputDir: string;
137
+
138
+ constructor(outputDir = '~/.mycode/tasks') {
139
+ this.outputDir = expandPath(outputDir);
140
+ fs.mkdirSync(this.outputDir, { recursive: true });
141
+ this.loadExistingTasks();
142
+ }
143
+
144
+ async createBashTask(command: string, description: string): Promise<BackgroundTask> {
145
+ const id = generateTaskId('bash');
146
+ const outputFile = path.join(this.outputDir, `${id}.log`);
147
+
148
+ const task: BackgroundTask = {
149
+ id,
150
+ type: 'bash',
151
+ description,
152
+ status: 'running',
153
+ started_at: new Date(),
154
+ output_file: outputFile
155
+ };
156
+
157
+ // Spawn process
158
+ const child = spawn('bash', ['-c', command], {
159
+ detached: true,
160
+ stdio: ['ignore', 'pipe', 'pipe']
161
+ });
162
+
163
+ // Setup output logging
164
+ const logStream = fs.createWriteStream(outputFile);
165
+ child.stdout?.pipe(logStream);
166
+ child.stderr?.pipe(logStream);
167
+
168
+ // Track completion
169
+ child.on('exit', (code) => {
170
+ task.status = code === 0 ? 'completed' : 'error';
171
+ task.completed_at = new Date();
172
+ task.exit_code = code ?? -1;
173
+ this.saveTasks();
174
+ });
175
+
176
+ this.tasks.set(id, task);
177
+ this.processes.set(id, child);
178
+ this.saveTasks();
179
+
180
+ return task;
181
+ }
182
+
183
+ async createAgentTask(
184
+ subagentType: string,
185
+ prompt: string,
186
+ description: string
187
+ ): Promise<BackgroundTask> {
188
+ const id = generateTaskId('agent');
189
+ const outputFile = path.join(this.outputDir, `${id}.json`);
190
+
191
+ const task: BackgroundTask = {
192
+ id,
193
+ type: 'agent',
194
+ description,
195
+ status: 'running',
196
+ started_at: new Date(),
197
+ output_file: outputFile,
198
+ metadata: { subagentType, prompt }
199
+ };
200
+
201
+ // Run agent in separate context
202
+ this.runAgentAsync(id, subagentType, prompt, outputFile);
203
+
204
+ this.tasks.set(id, task);
205
+ this.saveTasks();
206
+
207
+ return task;
208
+ }
209
+
210
+ async getOutput(
211
+ taskId: string,
212
+ options: { block: boolean; timeout: number }
213
+ ): Promise<TaskOutputOutput> {
214
+ const task = this.tasks.get(taskId);
215
+ if (!task) {
216
+ return {
217
+ success: false,
218
+ task_id: taskId,
219
+ status: 'error',
220
+ error: 'Task not found'
221
+ };
222
+ }
223
+
224
+ // If blocking, wait for completion
225
+ if (options.block && task.status === 'running') {
226
+ await this.waitForCompletion(taskId, options.timeout);
227
+ }
228
+
229
+ // Read output
230
+ const output = await this.readOutput(task.output_file);
231
+
232
+ return {
233
+ success: true,
234
+ task_id: taskId,
235
+ status: task.status,
236
+ output,
237
+ exit_code: task.exit_code,
238
+ elapsed_ms: Date.now() - task.started_at.getTime()
239
+ };
240
+ }
241
+
242
+ private async waitForCompletion(taskId: string, timeout: number): Promise<void> {
243
+ const start = Date.now();
244
+ const task = this.tasks.get(taskId);
245
+ if (!task) return;
246
+
247
+ while (task.status === 'running' && Date.now() - start < timeout) {
248
+ await new Promise(r => setTimeout(r, 500));
249
+ }
250
+ }
251
+
252
+ async cancelTask(taskId: string): Promise<boolean> {
253
+ const task = this.tasks.get(taskId);
254
+ const process = this.processes.get(taskId);
255
+
256
+ if (!task || task.status !== 'running') return false;
257
+
258
+ if (process && !process.killed) {
259
+ process.kill('SIGTERM');
260
+ }
261
+
262
+ task.status = 'cancelled';
263
+ task.completed_at = new Date();
264
+ this.saveTasks();
265
+
266
+ return true;
267
+ }
268
+
269
+ listTasks(filter?: { status?: TaskStatus; type?: TaskType }): BackgroundTask[] {
270
+ let tasks = Array.from(this.tasks.values());
271
+
272
+ if (filter?.status) {
273
+ tasks = tasks.filter(t => t.status === filter.status);
274
+ }
275
+ if (filter?.type) {
276
+ tasks = tasks.filter(t => t.type === filter.type);
277
+ }
278
+
279
+ return tasks.sort((a, b) => b.started_at.getTime() - a.started_at.getTime());
280
+ }
281
+
282
+ cleanup(maxAge: number = 24 * 60 * 60 * 1000): number {
283
+ const cutoff = Date.now() - maxAge;
284
+ let removed = 0;
285
+
286
+ for (const [id, task] of this.tasks) {
287
+ if (task.status !== 'running' && task.started_at.getTime() < cutoff) {
288
+ // Delete output file
289
+ try { fs.unlinkSync(task.output_file); } catch {}
290
+ this.tasks.delete(id);
291
+ removed++;
292
+ }
293
+ }
294
+
295
+ this.saveTasks();
296
+ return removed;
297
+ }
298
+ }
299
+
300
+ export const taskManager = new TaskManager();
301
+ ```
302
+
303
+ ### CLI Command: /tasks
304
+
305
+ ```typescript
306
+ // src/cli/commands/tasks.ts
307
+ function tasksCommand(args: string[]): void {
308
+ const tasks = taskManager.listTasks();
309
+
310
+ if (tasks.length === 0) {
311
+ console.log('No background tasks.');
312
+ return;
313
+ }
314
+
315
+ console.log('\nBackground Tasks:');
316
+ console.log('─'.repeat(70));
317
+
318
+ for (const task of tasks) {
319
+ const elapsed = formatDuration(Date.now() - task.started_at.getTime());
320
+ const statusIcon = {
321
+ running: '⏳',
322
+ completed: '✓',
323
+ error: '✗',
324
+ cancelled: '⊘',
325
+ pending: '○'
326
+ }[task.status];
327
+
328
+ console.log(`${statusIcon} ${task.id.padEnd(15)} ${task.description.slice(0, 30).padEnd(32)} ${elapsed}`);
329
+ }
330
+
331
+ console.log('─'.repeat(70));
332
+ console.log('Use TaskOutput tool to get task output.');
333
+ }
334
+ ```
335
+
336
+ ### File Changes
337
+
338
+ | File | Action | Description |
339
+ |------|--------|-------------|
340
+ | `src/tasks/types.ts` | Create | Task type definitions |
341
+ | `src/tasks/task-manager.ts` | Create | Task lifecycle management |
342
+ | `src/tasks/index.ts` | Create | Module exports |
343
+ | `src/tools/task-output/task-output-tool.ts` | Create | TaskOutput tool |
344
+ | `src/tools/task-output/index.ts` | Create | Module exports |
345
+ | `src/tools/index.ts` | Modify | Register TaskOutput tool |
346
+ | `src/cli/commands/tasks.ts` | Create | /tasks command |
347
+ | `src/cli/index.ts` | Modify | Register /tasks command |
348
+
349
+ ## User Experience
350
+
351
+ ### Launch Background Task
352
+ ```
353
+ User: Run the tests but don't wait for them
354
+
355
+ Agent: I'll start the tests in the background.
356
+
357
+ [Bash: command="npm test", run_in_background=true]
358
+
359
+ ┌─ Background Task Started ─────────────────────────┐
360
+ │ Task ID: bash-a1b2c3d4 │
361
+ │ Command: npm test │
362
+ │ Status: Running │
363
+ │ Output: ~/.mycode/tasks/bash-a1b2c3d4.log │
364
+ └───────────────────────────────────────────────────┘
365
+
366
+ You can continue working. Use /tasks to check status.
367
+ ```
368
+
369
+ ### Check Task Status (Non-blocking)
370
+ ```
371
+ User: How are those tests doing?
372
+
373
+ Agent: [TaskOutput: task_id="bash-a1b2c3d4", block=false]
374
+
375
+ ┌─ Task Status ─────────────────────────────────────┐
376
+ │ Task ID: bash-a1b2c3d4 │
377
+ │ Status: Running (2m 15s elapsed) │
378
+ │ Recent Output: │
379
+ │ PASS src/tools/edit.test.ts │
380
+ │ PASS src/tools/glob.test.ts │
381
+ │ Running: src/agent/agent.test.ts... │
382
+ └───────────────────────────────────────────────────┘
383
+
384
+ Tests are still running (45/60 passed so far).
385
+ ```
386
+
387
+ ### Wait for Completion
388
+ ```
389
+ Agent: [TaskOutput: task_id="bash-a1b2c3d4", block=true, timeout=60000]
390
+
391
+ ┌─ Task Completed ──────────────────────────────────┐
392
+ │ Task ID: bash-a1b2c3d4 │
393
+ │ Status: Completed │
394
+ │ Exit Code: 0 │
395
+ │ Duration: 3m 42s │
396
+ │ Summary: │
397
+ │ 60 tests passed │
398
+ │ 0 tests failed │
399
+ │ Coverage: 87.3% │
400
+ └───────────────────────────────────────────────────┘
401
+ ```
402
+
403
+ ### List All Tasks
404
+ ```
405
+ User: /tasks
406
+
407
+ Background Tasks:
408
+ ┌────────────────────────────────────────────────────────────────┐
409
+ │ Status ID Description Elapsed │
410
+ ├────────────────────────────────────────────────────────────────┤
411
+ │ ✓ bash-a1b2c3d4 Run npm test 3m 42s │
412
+ │ ⏳ agent-e5f6g7h8 Explore auth code 45s │
413
+ │ ✗ bash-i9j0k1l2 Build docker image 12m 3s │
414
+ └────────────────────────────────────────────────────────────────┘
415
+ ```
416
+
417
+ ## Alternatives Considered
418
+
419
+ ### Alternative 1: Promise-based Only
420
+ Use JavaScript promises without task registry.
421
+
422
+ **Pros**: Simpler implementation
423
+ **Cons**: No persistence, no /tasks visibility
424
+ **Decision**: Rejected - Registry provides better UX
425
+
426
+ ### Alternative 2: Worker Threads
427
+ Use Node.js worker threads.
428
+
429
+ **Pros**: Better isolation
430
+ **Cons**: More complex, limited shell access
431
+ **Decision**: Deferred - Can add for CPU-intensive tasks
432
+
433
+ ### Alternative 3: External Task Queue
434
+ Use Redis/BullMQ for task management.
435
+
436
+ **Pros**: Robust, distributed
437
+ **Cons**: Heavy dependency, overkill for CLI
438
+ **Decision**: Rejected - Keep it simple
439
+
440
+ ## Security Considerations
441
+
442
+ 1. **Task Isolation**: Each task runs in separate process
443
+ 2. **Output Sanitization**: Don't expose sensitive data in logs
444
+ 3. **Resource Limits**: Limit concurrent tasks and output size
445
+ 4. **Cleanup**: Regular cleanup of old task files
446
+ 5. **Permission**: Only access own tasks
447
+
448
+ ```typescript
449
+ const LIMITS = {
450
+ maxConcurrentTasks: 10,
451
+ maxOutputSize: 10 * 1024 * 1024, // 10MB
452
+ taskRetention: 24 * 60 * 60 * 1000 // 24 hours
453
+ };
454
+ ```
455
+
456
+ ## Testing Strategy
457
+
458
+ 1. **Unit Tests**:
459
+ - Task creation and tracking
460
+ - Output retrieval
461
+ - Blocking vs non-blocking
462
+ - Timeout handling
463
+
464
+ 2. **Integration Tests**:
465
+ - Background bash execution
466
+ - Agent task execution
467
+ - Concurrent tasks
468
+ - Cleanup logic
469
+
470
+ 3. **Manual Testing**:
471
+ - Long-running tasks
472
+ - Task cancellation
473
+ - /tasks command
474
+
475
+ ## Migration Path
476
+
477
+ 1. **Phase 1**: Basic TaskManager and TaskOutput tool
478
+ 2. **Phase 2**: /tasks CLI command
479
+ 3. **Phase 3**: Agent background execution
480
+ 4. **Phase 4**: Task persistence across sessions
481
+ 5. **Phase 5**: Task notifications on completion
482
+
483
+ Integrates with KillShell (0016) and Enhanced Bash (0028).
484
+
485
+ ## References
486
+
487
+ - [Node.js Child Process](https://nodejs.org/api/child_process.html)
488
+ - [Claude Code TaskOutput Documentation](https://code.claude.com/docs/en/tools)
489
+ - [BullMQ - Node.js Job Queue](https://docs.bullmq.io/)