@hanzo/dev 2.1.1 → 3.0.2

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.
@@ -1,435 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { spawn, ChildProcess } from 'child_process';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import chalk from 'chalk';
6
- import { FileEditor } from './editor';
7
- import { CodeActAgent } from './code-act-agent';
8
- import { FunctionCallingSystem } from './function-calling';
9
- import { MCPClient, MCPSession } from './mcp-client';
10
-
11
- export interface WorkspacePane {
12
- id: string;
13
- type: 'shell' | 'editor' | 'browser' | 'planner' | 'output';
14
- title: string;
15
- content?: string;
16
- active: boolean;
17
- }
18
-
19
- export interface ShellSession {
20
- id: string;
21
- process: ChildProcess;
22
- cwd: string;
23
- history: string[];
24
- output: string;
25
- }
26
-
27
- export class UnifiedWorkspace extends EventEmitter {
28
- private panes: Map<string, WorkspacePane> = new Map();
29
- private shellSessions: Map<string, ShellSession> = new Map();
30
- private editor: FileEditor;
31
- private agent: CodeActAgent;
32
- private functionCalling: FunctionCallingSystem;
33
- private mcpClient: MCPClient;
34
- private activePane: string = '';
35
- private browserUrl: string = '';
36
-
37
- constructor() {
38
- super();
39
- this.editor = new FileEditor();
40
- this.agent = new CodeActAgent();
41
- this.functionCalling = new FunctionCallingSystem();
42
- this.mcpClient = new MCPClient();
43
-
44
- // Initialize default panes
45
- this.initializeDefaultPanes();
46
- }
47
-
48
- private initializeDefaultPanes(): void {
49
- // Shell pane
50
- this.createPane('shell', 'Shell', '');
51
-
52
- // Editor pane
53
- this.createPane('editor', 'Editor', 'No file open');
54
-
55
- // Browser pane
56
- this.createPane('browser', 'Browser', 'Browser: Ready');
57
-
58
- // Planner pane
59
- this.createPane('planner', 'Planner', 'Task planner ready');
60
-
61
- // Output pane
62
- this.createPane('output', 'Output', '');
63
-
64
- // Set shell as active by default
65
- this.setActivePane('shell');
66
- }
67
-
68
- private createPane(type: WorkspacePane['type'], title: string, content: string): void {
69
- const id = `${type}-${Date.now()}`;
70
- const pane: WorkspacePane = {
71
- id,
72
- type,
73
- title,
74
- content,
75
- active: false
76
- };
77
- this.panes.set(id, pane);
78
- }
79
-
80
- setActivePane(type: WorkspacePane['type']): void {
81
- // Find pane by type
82
- for (const [id, pane] of this.panes) {
83
- if (pane.type === type) {
84
- this.activePane = id;
85
- pane.active = true;
86
- } else {
87
- pane.active = false;
88
- }
89
- }
90
- this.emit('pane-changed', type);
91
- }
92
-
93
- // Shell operations
94
- async executeShellCommand(command: string): Promise<void> {
95
- const shellPane = this.getPane('shell');
96
- if (!shellPane) return;
97
-
98
- // Get or create shell session
99
- let session = this.getOrCreateShellSession();
100
-
101
- // Add to history
102
- session.history.push(command);
103
-
104
- // Execute command
105
- this.appendToPane('shell', `\n$ ${command}\n`);
106
-
107
- try {
108
- const result = await this.functionCalling.callFunction({
109
- id: Date.now().toString(),
110
- name: 'run_command',
111
- arguments: { command, cwd: session.cwd }
112
- });
113
-
114
- if (result.result?.stdout) {
115
- this.appendToPane('shell', result.result.stdout);
116
- }
117
- if (result.result?.stderr) {
118
- this.appendToPane('shell', chalk.red(result.result.stderr));
119
- }
120
-
121
- // Update cwd if cd command
122
- if (command.startsWith('cd ')) {
123
- const newDir = command.substring(3).trim();
124
- session.cwd = path.resolve(session.cwd, newDir);
125
- }
126
- } catch (error) {
127
- this.appendToPane('shell', chalk.red(`Error: ${error}`));
128
- }
129
- }
130
-
131
- private getOrCreateShellSession(): ShellSession {
132
- const sessionId = 'main';
133
- if (!this.shellSessions.has(sessionId)) {
134
- const session: ShellSession = {
135
- id: sessionId,
136
- process: spawn('bash', [], { cwd: process.cwd() }),
137
- cwd: process.cwd(),
138
- history: [],
139
- output: ''
140
- };
141
- this.shellSessions.set(sessionId, session);
142
- }
143
- return this.shellSessions.get(sessionId)!;
144
- }
145
-
146
- // Editor operations
147
- async openFile(filePath: string): Promise<void> {
148
- const result = await this.editor.execute({
149
- command: 'view',
150
- path: filePath
151
- });
152
-
153
- if (result.success) {
154
- this.updatePane('editor', result.content || '');
155
- this.updatePaneTitle('editor', `Editor - ${path.basename(filePath)}`);
156
- this.setActivePane('editor');
157
- } else {
158
- this.appendToPane('output', chalk.red(`Failed to open file: ${result.message}`));
159
- }
160
- }
161
-
162
- async saveFile(filePath: string, content: string): Promise<void> {
163
- fs.writeFileSync(filePath, content);
164
- this.appendToPane('output', chalk.green(`✓ Saved ${filePath}`));
165
- }
166
-
167
- // Browser operations
168
- async navigateBrowser(url: string): Promise<void> {
169
- this.browserUrl = url;
170
- this.updatePane('browser', `Browser: ${url}`);
171
- this.appendToPane('output', `Navigated to ${url}`);
172
-
173
- // In a real implementation, this would use a headless browser
174
- // For now, we'll just simulate
175
- try {
176
- const response = await fetch(url);
177
- const text = await response.text();
178
- const preview = text.substring(0, 500) + '...';
179
- this.updatePane('browser', `URL: ${url}\n\n${preview}`);
180
- } catch (error) {
181
- this.updatePane('browser', `Failed to load ${url}: ${error}`);
182
- }
183
- }
184
-
185
- // Planner operations
186
- async planTask(description: string): Promise<void> {
187
- this.updatePane('planner', `Planning: ${description}\n\nGenerating execution plan...`);
188
- this.setActivePane('planner');
189
-
190
- // Use the agent to plan
191
- const plan = await this.generatePlan(description);
192
-
193
- let planContent = `Task: ${description}\n\nExecution Plan:\n`;
194
- plan.steps.forEach((step, i) => {
195
- const parallel = plan.parallelizable[i] ? ' [can run in parallel]' : '';
196
- planContent += `${i + 1}. ${step}${parallel}\n`;
197
- });
198
-
199
- this.updatePane('planner', planContent);
200
- }
201
-
202
- private async generatePlan(description: string): Promise<{
203
- steps: string[];
204
- parallelizable: boolean[];
205
- }> {
206
- // Simplified planning logic
207
- const steps: string[] = [];
208
- const parallelizable: boolean[] = [];
209
-
210
- if (description.includes('debug')) {
211
- steps.push('Reproduce the issue');
212
- parallelizable.push(false);
213
- steps.push('Analyze error logs');
214
- parallelizable.push(false);
215
- steps.push('Identify root cause');
216
- parallelizable.push(false);
217
- steps.push('Implement fix');
218
- parallelizable.push(false);
219
- steps.push('Test the fix');
220
- parallelizable.push(false);
221
- } else if (description.includes('feature')) {
222
- steps.push('Analyze requirements');
223
- parallelizable.push(false);
224
- steps.push('Design implementation');
225
- parallelizable.push(false);
226
- steps.push('Write tests');
227
- parallelizable.push(true);
228
- steps.push('Implement feature');
229
- parallelizable.push(true);
230
- steps.push('Run tests');
231
- parallelizable.push(false);
232
- steps.push('Update documentation');
233
- parallelizable.push(true);
234
- } else {
235
- steps.push('Analyze task');
236
- parallelizable.push(false);
237
- steps.push('Execute task');
238
- parallelizable.push(false);
239
- steps.push('Verify results');
240
- parallelizable.push(false);
241
- }
242
-
243
- return { steps, parallelizable };
244
- }
245
-
246
- // Execute planned task
247
- async executePlan(): Promise<void> {
248
- const plannerPane = this.getPane('planner');
249
- if (!plannerPane || !plannerPane.content) return;
250
-
251
- // Extract task from planner
252
- const lines = plannerPane.content.split('\n');
253
- const taskLine = lines.find(l => l.startsWith('Task:'));
254
- if (!taskLine) return;
255
-
256
- const task = taskLine.substring(5).trim();
257
- this.appendToPane('output', chalk.cyan(`\nExecuting task: ${task}\n`));
258
-
259
- // Execute using agent
260
- await this.agent.executeTask(task);
261
- }
262
-
263
- // Pane management
264
- private getPane(type: WorkspacePane['type']): WorkspacePane | undefined {
265
- for (const pane of this.panes.values()) {
266
- if (pane.type === type) return pane;
267
- }
268
- return undefined;
269
- }
270
-
271
- private updatePane(type: WorkspacePane['type'], content: string): void {
272
- const pane = this.getPane(type);
273
- if (pane) {
274
- pane.content = content;
275
- this.emit('pane-updated', type, content);
276
- }
277
- }
278
-
279
- private appendToPane(type: WorkspacePane['type'], content: string): void {
280
- const pane = this.getPane(type);
281
- if (pane) {
282
- pane.content = (pane.content || '') + content;
283
- this.emit('pane-updated', type, pane.content);
284
- }
285
- }
286
-
287
- private updatePaneTitle(type: WorkspacePane['type'], title: string): void {
288
- const pane = this.getPane(type);
289
- if (pane) {
290
- pane.title = title;
291
- this.emit('pane-title-updated', type, title);
292
- }
293
- }
294
-
295
- // Display workspace (simplified for CLI)
296
- displayWorkspace(): void {
297
- console.clear();
298
- console.log(chalk.bold.cyan('╔══════════════════════════════════════════════════════════════╗'));
299
- console.log(chalk.bold.cyan('║ 🚀 Hanzo Dev Workspace ║'));
300
- console.log(chalk.bold.cyan('╚══════════════════════════════════════════════════════════════╝'));
301
- console.log();
302
-
303
- // Display pane tabs
304
- const tabs: string[] = [];
305
- for (const pane of this.panes.values()) {
306
- const isActive = pane.id === this.activePane;
307
- const tab = isActive
308
- ? chalk.bold.yellow(`[${pane.title}]`)
309
- : chalk.gray(`[${pane.title}]`);
310
- tabs.push(tab);
311
- }
312
- console.log(tabs.join(' '));
313
- console.log(chalk.gray('─'.repeat(64)));
314
-
315
- // Display active pane content
316
- const activePane = this.panes.get(this.activePane);
317
- if (activePane && activePane.content) {
318
- const lines = activePane.content.split('\n');
319
- const maxLines = 20;
320
- const displayLines = lines.slice(-maxLines);
321
- console.log(displayLines.join('\n'));
322
- }
323
-
324
- console.log(chalk.gray('─'.repeat(64)));
325
- }
326
-
327
- // Cleanup
328
- async cleanup(): Promise<void> {
329
- // Close shell sessions
330
- for (const session of this.shellSessions.values()) {
331
- session.process.kill();
332
- }
333
-
334
- // Disconnect MCP sessions
335
- const sessions = this.mcpClient.getAllSessions();
336
- for (const session of sessions) {
337
- await this.mcpClient.disconnect(session.id);
338
- }
339
- }
340
- }
341
-
342
- // Interactive workspace session
343
- export class WorkspaceSession {
344
- private workspace: UnifiedWorkspace;
345
- private running: boolean = true;
346
-
347
- constructor() {
348
- this.workspace = new UnifiedWorkspace();
349
- }
350
-
351
- async start(): Promise<void> {
352
- console.log(chalk.bold.cyan('\n🎯 Starting Unified Workspace...\n'));
353
-
354
- // Set up event listeners
355
- this.workspace.on('pane-updated', () => {
356
- if (this.running) {
357
- this.workspace.displayWorkspace();
358
- }
359
- });
360
-
361
- // Initial display
362
- this.workspace.displayWorkspace();
363
-
364
- // Start interactive loop
365
- const readline = require('readline');
366
- const rl = readline.createInterface({
367
- input: process.stdin,
368
- output: process.stdout
369
- });
370
-
371
- console.log(chalk.gray('\nCommands: shell <cmd>, edit <file>, browse <url>, plan <task>, execute, switch <pane>, exit\n'));
372
-
373
- const prompt = () => {
374
- rl.question(chalk.green('workspace> '), async (input) => {
375
- if (!this.running) return;
376
-
377
- const [cmd, ...args] = input.trim().split(' ');
378
- const arg = args.join(' ');
379
-
380
- try {
381
- switch (cmd) {
382
- case 'shell':
383
- case 'sh':
384
- await this.workspace.executeShellCommand(arg);
385
- break;
386
-
387
- case 'edit':
388
- case 'e':
389
- await this.workspace.openFile(arg);
390
- break;
391
-
392
- case 'browse':
393
- case 'b':
394
- await this.workspace.navigateBrowser(arg);
395
- break;
396
-
397
- case 'plan':
398
- case 'p':
399
- await this.workspace.planTask(arg);
400
- break;
401
-
402
- case 'execute':
403
- case 'x':
404
- await this.workspace.executePlan();
405
- break;
406
-
407
- case 'switch':
408
- case 's':
409
- this.workspace.setActivePane(arg as any);
410
- this.workspace.displayWorkspace();
411
- break;
412
-
413
- case 'exit':
414
- case 'quit':
415
- this.running = false;
416
- await this.workspace.cleanup();
417
- rl.close();
418
- return;
419
-
420
- default:
421
- console.log(chalk.red(`Unknown command: ${cmd}`));
422
- }
423
- } catch (error) {
424
- console.log(chalk.red(`Error: ${error}`));
425
- }
426
-
427
- if (this.running) {
428
- prompt();
429
- }
430
- });
431
- };
432
-
433
- prompt();
434
- }
435
- }
@@ -1,6 +0,0 @@
1
- // Sample JavaScript file
2
- function calculateTotal(items) {
3
- return items.reduce((sum, item) => sum + item.price, 0);
4
- }
5
-
6
- module.exports = { calculateTotal };
@@ -1,12 +0,0 @@
1
- // TypeScript utility functions
2
- export function formatDate(date: Date): string {
3
- return date.toISOString().split('T')[0];
4
- }
5
-
6
- export function parseJSON<T>(json: string): T | null {
7
- try {
8
- return JSON.parse(json);
9
- } catch {
10
- return null;
11
- }
12
- }
@@ -1,15 +0,0 @@
1
- # Python data processing
2
- import json
3
- from typing import List, Dict
4
-
5
- def process_data(items: List[Dict]) -> Dict:
6
- """Process a list of items and return summary statistics."""
7
- total = sum(item.get('value', 0) for item in items)
8
- count = len(items)
9
- average = total / count if count > 0 else 0
10
-
11
- return {
12
- 'total': total,
13
- 'count': count,
14
- 'average': average
15
- }
@@ -1,13 +0,0 @@
1
- # Documentation
2
-
3
- This is a sample markdown file for testing the swarm functionality.
4
-
5
- ## Features
6
-
7
- - Parallel processing
8
- - Multiple file types
9
- - Automatic edits
10
-
11
- ## Usage
12
-
13
- Run the swarm command to process multiple files at once.
@@ -1,12 +0,0 @@
1
- {
2
- "name": "test-project",
3
- "version": "1.0.0",
4
- "description": "Test project for swarm processing",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "keywords": ["test", "swarm"],
10
- "author": "",
11
- "license": "ISC"
12
- }
@@ -1,22 +0,0 @@
1
- #!/bin/bash
2
-
3
- echo "🐝 Hanzo Dev Swarm Demo"
4
- echo "======================"
5
- echo ""
6
- echo "This demo will add copyright headers to 5 test files in parallel"
7
- echo ""
8
- echo "Files before:"
9
- echo "-------------"
10
- head -n 1 test-swarm/*.{js,ts,py,md,json} 2>/dev/null
11
-
12
- echo ""
13
- echo "Running swarm with 5 agents..."
14
- echo ""
15
-
16
- # Run the swarm command
17
- node dist/cli/dev.js --claude --swarm 5 -p "Add this copyright header at the very top of each file: '// Copyright 2025 Hanzo Industries Inc.' (use # for Python, // for JS/TS/JSON)"
18
-
19
- echo ""
20
- echo "Files after:"
21
- echo "------------"
22
- head -n 2 test-swarm/*.{js,ts,py,md,json} 2>/dev/null