@covibes/zeroshot 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/LICENSE +21 -0
  3. package/README.md +364 -0
  4. package/cli/index.js +3990 -0
  5. package/cluster-templates/base-templates/debug-workflow.json +181 -0
  6. package/cluster-templates/base-templates/full-workflow.json +455 -0
  7. package/cluster-templates/base-templates/single-worker.json +48 -0
  8. package/cluster-templates/base-templates/worker-validator.json +131 -0
  9. package/cluster-templates/conductor-bootstrap.json +122 -0
  10. package/cluster-templates/conductor-junior-bootstrap.json +69 -0
  11. package/docker/zeroshot-cluster/Dockerfile +132 -0
  12. package/lib/completion.js +174 -0
  13. package/lib/id-detector.js +53 -0
  14. package/lib/settings.js +97 -0
  15. package/lib/stream-json-parser.js +236 -0
  16. package/package.json +121 -0
  17. package/src/agent/agent-config.js +121 -0
  18. package/src/agent/agent-context-builder.js +241 -0
  19. package/src/agent/agent-hook-executor.js +329 -0
  20. package/src/agent/agent-lifecycle.js +555 -0
  21. package/src/agent/agent-stuck-detector.js +256 -0
  22. package/src/agent/agent-task-executor.js +1034 -0
  23. package/src/agent/agent-trigger-evaluator.js +67 -0
  24. package/src/agent-wrapper.js +459 -0
  25. package/src/agents/git-pusher-agent.json +20 -0
  26. package/src/attach/attach-client.js +438 -0
  27. package/src/attach/attach-server.js +543 -0
  28. package/src/attach/index.js +35 -0
  29. package/src/attach/protocol.js +220 -0
  30. package/src/attach/ring-buffer.js +121 -0
  31. package/src/attach/socket-discovery.js +242 -0
  32. package/src/claude-task-runner.js +468 -0
  33. package/src/config-router.js +80 -0
  34. package/src/config-validator.js +598 -0
  35. package/src/github.js +103 -0
  36. package/src/isolation-manager.js +1042 -0
  37. package/src/ledger.js +429 -0
  38. package/src/logic-engine.js +223 -0
  39. package/src/message-bus-bridge.js +139 -0
  40. package/src/message-bus.js +202 -0
  41. package/src/name-generator.js +232 -0
  42. package/src/orchestrator.js +1938 -0
  43. package/src/schemas/sub-cluster.js +156 -0
  44. package/src/sub-cluster-wrapper.js +545 -0
  45. package/src/task-runner.js +28 -0
  46. package/src/template-resolver.js +347 -0
  47. package/src/tui/CHANGES.txt +133 -0
  48. package/src/tui/LAYOUT.md +261 -0
  49. package/src/tui/README.txt +192 -0
  50. package/src/tui/TWO-LEVEL-NAVIGATION.md +186 -0
  51. package/src/tui/data-poller.js +325 -0
  52. package/src/tui/demo.js +208 -0
  53. package/src/tui/formatters.js +123 -0
  54. package/src/tui/index.js +193 -0
  55. package/src/tui/keybindings.js +383 -0
  56. package/src/tui/layout.js +317 -0
  57. package/src/tui/renderer.js +194 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * AttachClient - Terminal client for attaching to running tasks/agents
3
+ *
4
+ * Key sequences:
5
+ * - Ctrl+C: Clean detach (return to shell, task continues running)
6
+ * - Ctrl+B d: Also detach (for tmux muscle memory)
7
+ * - Ctrl+B ?: Show help
8
+ * - Ctrl+B c: Send SIGINT to process (interrupt agent - USE WITH CAUTION)
9
+ * - Ctrl+Z: Forward SIGTSTP to process
10
+ *
11
+ * Features:
12
+ * - Raw terminal mode (passes through all input)
13
+ * - Output history replay on attach
14
+ * - Terminal resize forwarding
15
+ * - Graceful cleanup on exit
16
+ */
17
+
18
+ const net = require('net');
19
+ const EventEmitter = require('events');
20
+ const crypto = require('crypto');
21
+
22
+ const protocol = require('./protocol');
23
+
24
+ // Key codes
25
+ const CTRL_B = '\x02';
26
+ const CTRL_C = '\x03';
27
+ const CTRL_Z = '\x1a';
28
+
29
+ // Detach timeout (ms) - how long to wait for second key after Ctrl+B
30
+ const DETACH_TIMEOUT = 500;
31
+
32
+ class AttachClient extends EventEmitter {
33
+ /**
34
+ * @param {object} options
35
+ * @param {string} options.socketPath - Unix socket path to connect to
36
+ * @param {object} [options.stdin] - Input stream (default process.stdin)
37
+ * @param {object} [options.stdout] - Output stream (default process.stdout)
38
+ */
39
+ constructor(options) {
40
+ super();
41
+
42
+ if (!options.socketPath) {
43
+ throw new Error('AttachClient: socketPath is required');
44
+ }
45
+
46
+ this.socketPath = options.socketPath;
47
+ this.stdin = options.stdin || process.stdin;
48
+ this.stdout = options.stdout || process.stdout;
49
+
50
+ this.clientId = crypto.randomUUID();
51
+ this.socket = null;
52
+ this.decoder = new protocol.MessageDecoder();
53
+ this.connected = false;
54
+ this.wasRawMode = null;
55
+
56
+ // Ctrl+B sequence detection
57
+ this.ctrlBPressed = false;
58
+ this.ctrlBTimeout = null;
59
+
60
+ // Bind handlers
61
+ this._onSocketData = this._onSocketData.bind(this);
62
+ this._onSocketClose = this._onSocketClose.bind(this);
63
+ this._onSocketError = this._onSocketError.bind(this);
64
+ this._onStdinData = this._onStdinData.bind(this);
65
+ this._onResize = this._onResize.bind(this);
66
+ }
67
+
68
+ /**
69
+ * Connect to the attach server
70
+ * @returns {Promise<void>}
71
+ */
72
+ connect() {
73
+ if (this.connected) {
74
+ throw new Error('AttachClient: Already connected');
75
+ }
76
+
77
+ return new Promise((resolve, reject) => {
78
+ this.socket = net.createConnection(this.socketPath);
79
+
80
+ this.socket.on('connect', () => {
81
+ this.connected = true;
82
+
83
+ // Send attach message with terminal dimensions
84
+ const cols = this.stdout.columns || 80;
85
+ const rows = this.stdout.rows || 24;
86
+
87
+ this.socket.write(protocol.encode(protocol.createAttachMessage(this.clientId, cols, rows)));
88
+
89
+ // Set up terminal
90
+ this._setupTerminal();
91
+
92
+ // Set up socket handlers
93
+ this.socket.on('data', this._onSocketData);
94
+ this.socket.on('close', this._onSocketClose);
95
+ this.socket.on('error', this._onSocketError);
96
+
97
+ resolve();
98
+ });
99
+
100
+ this.socket.on('error', (err) => {
101
+ if (!this.connected) {
102
+ reject(err);
103
+ }
104
+ });
105
+
106
+ // Connection timeout
107
+ const timeout = setTimeout(() => {
108
+ if (!this.connected) {
109
+ this.socket.destroy();
110
+ reject(new Error('Connection timeout'));
111
+ }
112
+ }, 5000);
113
+
114
+ this.socket.on('connect', () => clearTimeout(timeout));
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Disconnect from the server
120
+ */
121
+ disconnect() {
122
+ if (!this.connected) {
123
+ return;
124
+ }
125
+
126
+ // Send detach message
127
+ try {
128
+ this.socket.write(protocol.encode(protocol.createDetachMessage(this.clientId)));
129
+ } catch {
130
+ // Ignore
131
+ }
132
+
133
+ this._cleanup();
134
+ this.emit('detach');
135
+ }
136
+
137
+ /**
138
+ * Send a signal to the remote process
139
+ * @param {string} signal - Signal name
140
+ */
141
+ sendSignal(signal) {
142
+ if (!this.connected) {
143
+ return;
144
+ }
145
+
146
+ try {
147
+ this.socket.write(protocol.encode(protocol.createSignalMessage(signal)));
148
+ } catch {
149
+ // Ignore
150
+ }
151
+ }
152
+
153
+ // ─────────────────────────────────────────────────────────────────
154
+ // Private methods
155
+ // ─────────────────────────────────────────────────────────────────
156
+
157
+ /**
158
+ * Set up terminal for raw mode
159
+ * @private
160
+ */
161
+ _setupTerminal() {
162
+ // Enable raw mode if stdin is a TTY
163
+ if (this.stdin.isTTY && this.stdin.setRawMode) {
164
+ this.wasRawMode = this.stdin.isRaw;
165
+ this.stdin.setRawMode(true);
166
+ }
167
+
168
+ // Resume stdin (may be paused)
169
+ this.stdin.resume();
170
+
171
+ // Listen for input
172
+ this.stdin.on('data', this._onStdinData);
173
+
174
+ // Listen for resize events
175
+ if (this.stdout.isTTY) {
176
+ this.stdout.on('resize', this._onResize);
177
+ }
178
+
179
+ // Handle process signals for cleanup
180
+ process.on('SIGINT', () => {
181
+ // Clean detach on Ctrl+C - task continues running
182
+ this.disconnect();
183
+ });
184
+
185
+ process.on('SIGTERM', () => {
186
+ this._cleanup();
187
+ process.exit(0);
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Restore terminal state
193
+ * @private
194
+ */
195
+ _restoreTerminal() {
196
+ // Restore raw mode
197
+ if (this.stdin.isTTY && this.stdin.setRawMode && this.wasRawMode !== null) {
198
+ this.stdin.setRawMode(this.wasRawMode);
199
+ }
200
+
201
+ // Remove listeners
202
+ this.stdin.removeListener('data', this._onStdinData);
203
+ if (this.stdout.isTTY) {
204
+ this.stdout.removeListener('resize', this._onResize);
205
+ }
206
+
207
+ // Pause stdin
208
+ this.stdin.pause();
209
+ }
210
+
211
+ /**
212
+ * Handle data from socket
213
+ * @private
214
+ */
215
+ _onSocketData(data) {
216
+ try {
217
+ const messages = this.decoder.feed(data);
218
+ for (const msg of messages) {
219
+ this._handleMessage(msg);
220
+ }
221
+ } catch (e) {
222
+ this.emit('error', new Error(`Protocol error: ${e.message}`));
223
+ this._cleanup();
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Handle message from server
229
+ * @private
230
+ */
231
+ _handleMessage(message) {
232
+ switch (message.type) {
233
+ case protocol.MessageType.OUTPUT: {
234
+ const data = protocol.decodeData(message);
235
+ if (data) {
236
+ this.stdout.write(data);
237
+ }
238
+ break;
239
+ }
240
+
241
+ case protocol.MessageType.HISTORY: {
242
+ const data = protocol.decodeData(message);
243
+ if (data) {
244
+ this.stdout.write(data);
245
+ }
246
+ break;
247
+ }
248
+
249
+ case protocol.MessageType.STATE: {
250
+ this.emit('state', message);
251
+ break;
252
+ }
253
+
254
+ case protocol.MessageType.EXIT: {
255
+ const { code, signal } = message;
256
+ this.emit('exit', { code, signal });
257
+ this._cleanup();
258
+ break;
259
+ }
260
+
261
+ case protocol.MessageType.ERROR: {
262
+ this.emit('error', new Error(message.message));
263
+ break;
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Handle stdin data
270
+ * @private
271
+ */
272
+ _onStdinData(data) {
273
+ const str = data.toString();
274
+
275
+ // Handle Ctrl+B sequence
276
+ if (this.ctrlBPressed) {
277
+ this.ctrlBPressed = false;
278
+ if (this.ctrlBTimeout) {
279
+ clearTimeout(this.ctrlBTimeout);
280
+ this.ctrlBTimeout = null;
281
+ }
282
+
283
+ // Check for command keys
284
+ if (str === 'd' || str === 'D') {
285
+ // Detach
286
+ this.disconnect();
287
+ return;
288
+ }
289
+
290
+ if (str === 'c' || str === 'C') {
291
+ // Send SIGINT to process (interrupt agent - USE WITH CAUTION)
292
+ this.stdout.write('\r\n⚠️ Sending SIGINT to agent (interrupting task)...\r\n');
293
+ this.sendSignal('SIGINT');
294
+ return;
295
+ }
296
+
297
+ if (str === '?') {
298
+ // Show help
299
+ this._showHelp();
300
+ return;
301
+ }
302
+
303
+ // Not a recognized command, forward Ctrl+B + this key
304
+ this._forwardInput(Buffer.from([0x02]));
305
+ this._forwardInput(data);
306
+ return;
307
+ }
308
+
309
+ // Check for Ctrl+B
310
+ if (str === CTRL_B) {
311
+ this.ctrlBPressed = true;
312
+
313
+ // Set timeout to forward if no follow-up key
314
+ this.ctrlBTimeout = setTimeout(() => {
315
+ if (this.ctrlBPressed) {
316
+ this.ctrlBPressed = false;
317
+ this._forwardInput(data);
318
+ }
319
+ }, DETACH_TIMEOUT);
320
+ return;
321
+ }
322
+
323
+ // Check for Ctrl+C - clean detach (task continues running)
324
+ if (str === CTRL_C) {
325
+ this.disconnect();
326
+ return;
327
+ }
328
+
329
+ // Check for Ctrl+Z
330
+ if (str === CTRL_Z) {
331
+ this.sendSignal('SIGTSTP');
332
+ return;
333
+ }
334
+
335
+ // Forward other input (future interactive mode)
336
+ this._forwardInput(data);
337
+ }
338
+
339
+ /**
340
+ * Forward input to remote process
341
+ * @private
342
+ */
343
+ _forwardInput(data) {
344
+ if (!this.connected) {
345
+ return;
346
+ }
347
+
348
+ try {
349
+ this.socket.write(protocol.encode(protocol.createStdinMessage(data)));
350
+ } catch {
351
+ // Ignore
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Handle terminal resize
357
+ * @private
358
+ */
359
+ _onResize() {
360
+ if (!this.connected) {
361
+ return;
362
+ }
363
+
364
+ const cols = this.stdout.columns;
365
+ const rows = this.stdout.rows;
366
+
367
+ try {
368
+ this.socket.write(protocol.encode(protocol.createResizeMessage(cols, rows)));
369
+ } catch {
370
+ // Ignore
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Handle socket close
376
+ * @private
377
+ */
378
+ _onSocketClose() {
379
+ if (this.connected) {
380
+ this.emit('close');
381
+ this._cleanup();
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Handle socket error
387
+ * @private
388
+ */
389
+ _onSocketError(err) {
390
+ this.emit('error', err);
391
+ this._cleanup();
392
+ }
393
+
394
+ /**
395
+ * Show help message
396
+ * @private
397
+ */
398
+ _showHelp() {
399
+ const help = `
400
+ \r\n╭──────────────────────────────────────────────────────────╮
401
+ \r\n│ Vibe Attach - Key Bindings │
402
+ \r\n├──────────────────────────────────────────────────────────┤
403
+ \r\n│ Ctrl+C Detach (task continues running) │
404
+ \r\n│ Ctrl+B d Also detach (for tmux muscle memory) │
405
+ \r\n│ Ctrl+B ? Show this help │
406
+ \r\n│ Ctrl+B c ⚠️ Interrupt agent (sends SIGINT) │
407
+ \r\n│ Ctrl+Z Suspend process (sends SIGTSTP) │
408
+ \r\n╰──────────────────────────────────────────────────────────╯
409
+ \r\n`;
410
+ this.stdout.write(help);
411
+ }
412
+
413
+ /**
414
+ * Clean up resources
415
+ * @private
416
+ */
417
+ _cleanup() {
418
+ this.connected = false;
419
+
420
+ // Clear Ctrl+B timeout
421
+ if (this.ctrlBTimeout) {
422
+ clearTimeout(this.ctrlBTimeout);
423
+ this.ctrlBTimeout = null;
424
+ }
425
+
426
+ // Restore terminal
427
+ this._restoreTerminal();
428
+
429
+ // Close socket
430
+ if (this.socket) {
431
+ this.socket.removeAllListeners();
432
+ this.socket.destroy();
433
+ this.socket = null;
434
+ }
435
+ }
436
+ }
437
+
438
+ module.exports = AttachClient;