diff --git a/.workspace/context/last-sync.json b/.workspace/context/last-sync.json new file mode 100644 index 00000000..9af3cb87 --- /dev/null +++ b/.workspace/context/last-sync.json @@ -0,0 +1,109 @@ +{ + "timestamp": "2025-07-23T21:21:48.376Z", + "session": { + "id": "85db2f3a0f721c3c", + "ide": "vscode", + "created": "2025-07-23T21:21:29.435Z", + "lastHeartbeat": "2025-07-23T21:21:29.435Z", + "pid": 97230, + "user": "bryan", + "cwd": "/mnt/c/Projects/BMAD-Method", + "nodeVersion": "v20.19.2", + "platform": "linux", + "arch": "x64", + "metadata": { + "ideSpecific": { + "supportsTerminalCommands": true, + "hasIntegratedGit": true, + "supportsPanels": true, + "hasExtensionSystem": true, + "features": [ + "extensions", + "integrated-terminal", + "git-integration" + ] + }, + "features": [ + "context-sharing", + "agent-handoffs", + "quality-tracking" + ] + } + }, + "context": { + "filesFound": 0, + "syncErrors": 0, + "files": [] + }, + "progress": { + "recentUpdates": 0, + "updates": [] + }, + "handoffs": { + "pending": 1, + "recent": [ + { + "id": "d3b9448d06e1", + "timestamp": "2025-07-23T21:21:44.381Z", + "fromAgent": "unknown", + "toAgent": "dev", + "currentWork": "No work description provided", + "notes": "", + "session": { + "id": "85db2f3a0f721c3c", + "ide": "vscode", + "created": "2025-07-23T21:21:29.435Z", + "lastHeartbeat": "2025-07-23T21:21:29.435Z", + "pid": 97230, + "user": "bryan", + "cwd": "/mnt/c/Projects/BMAD-Method", + "nodeVersion": "v20.19.2", + "platform": "linux", + "arch": "x64", + "metadata": { + "ideSpecific": { + "supportsTerminalCommands": true, + "hasIntegratedGit": true, + "supportsPanels": true, + "hasExtensionSystem": true, + "features": [ + "extensions", + "integrated-terminal", + "git-integration" + ] + }, + "features": [ + "context-sharing", + "agent-handoffs", + "quality-tracking" + ] + } + }, + "context": { + "availableFiles": [], + "recentProgress": [], + "workspaceHealth": { + "score": 100, + "missingDirectories": [] + } + }, + "recommendations": [ + "Update workspace context with latest findings", + "Review any TODO items or pending decisions" + ], + "status": "pending" + } + ] + }, + "quality": { + "recentReports": 1, + "reports": [ + { + "file": "health-report.json", + "modified": "2025-07-23T21:21:36.053Z", + "type": "report", + "preview": "{\n \"timestamp\": \"2025-07-23T21:21:35.971Z\",\n \"overallScore\": 93,\n \"status\": \"excellent\",\n \"check..." + } + ] + } +} \ No newline at end of file diff --git a/.workspace/context/sync-summary.md b/.workspace/context/sync-summary.md new file mode 100644 index 00000000..af0d7207 --- /dev/null +++ b/.workspace/context/sync-summary.md @@ -0,0 +1,33 @@ +# Workspace Sync Summary + +**Sync Time:** 7/23/2025, 5:21:48 PM + +## Session Information +**Current Session:** 85db2f3a0f721c3c (vscode) +**User:** bryan +**Last Activity:** 7/23/2025, 5:21:29 PM + +## Context Files (0) +No context files found + + + +## Recent Progress (0) +No recent progress updates + +## Pending Handoffs (1) +- **d3b9448d06e1:** unknown โ†’ dev + Work: No work description provided + Time: 7/23/2025, 5:21:44 PM + +## Quality Reports (1) +- **health-report.json** (report, 7/23/2025, 5:21:36 PM) + { + "timestamp": "2025-07-23T21:21:35.971Z", + "overallScore": 93, + "status": "excellent", + "check... + +--- +*Last synced: 7/23/2025, 5:21:48 PM* +*Generated by BMAD Cross-IDE Workspace System* diff --git a/.workspace/handoffs/d3b9448d06e1.json b/.workspace/handoffs/d3b9448d06e1.json new file mode 100644 index 00000000..a914b542 --- /dev/null +++ b/.workspace/handoffs/d3b9448d06e1.json @@ -0,0 +1,51 @@ +{ + "id": "d3b9448d06e1", + "timestamp": "2025-07-23T21:21:44.381Z", + "fromAgent": "unknown", + "toAgent": "dev", + "currentWork": "No work description provided", + "notes": "", + "session": { + "id": "85db2f3a0f721c3c", + "ide": "vscode", + "created": "2025-07-23T21:21:29.435Z", + "lastHeartbeat": "2025-07-23T21:21:29.435Z", + "pid": 97230, + "user": "bryan", + "cwd": "/mnt/c/Projects/BMAD-Method", + "nodeVersion": "v20.19.2", + "platform": "linux", + "arch": "x64", + "metadata": { + "ideSpecific": { + "supportsTerminalCommands": true, + "hasIntegratedGit": true, + "supportsPanels": true, + "hasExtensionSystem": true, + "features": [ + "extensions", + "integrated-terminal", + "git-integration" + ] + }, + "features": [ + "context-sharing", + "agent-handoffs", + "quality-tracking" + ] + } + }, + "context": { + "availableFiles": [], + "recentProgress": [], + "workspaceHealth": { + "score": 100, + "missingDirectories": [] + } + }, + "recommendations": [ + "Update workspace context with latest findings", + "Review any TODO items or pending decisions" + ], + "status": "pending" +} \ No newline at end of file diff --git a/.workspace/handoffs/d3b9448d06e1.md b/.workspace/handoffs/d3b9448d06e1.md new file mode 100644 index 00000000..586c668f --- /dev/null +++ b/.workspace/handoffs/d3b9448d06e1.md @@ -0,0 +1,33 @@ +# Agent Handoff: unknown โ†’ dev + +**Handoff ID:** d3b9448d06e1 +**Timestamp:** 7/23/2025, 5:21:44 PM +**To Agent:** Developer (James) - Code implementation and debugging + +## Current Work +No work description provided + +## Notes +No additional notes provided + +## Context Summary +- **Available context files:** 0 +- **Recent progress entries:** 0 +- **Workspace health:** 100/100 + + + +## Recommendations +- Update workspace context with latest findings +- Review any TODO items or pending decisions + +## Session Information + +- **IDE:** vscode +- **User:** bryan +- **Created:** 7/23/2025, 5:21:29 PM +- **Last Activity:** 7/23/2025, 5:21:29 PM + + +--- +*Generated by BMAD Cross-IDE Workspace System* diff --git a/.workspace/quality/health-report.json b/.workspace/quality/health-report.json new file mode 100644 index 00000000..3c229054 --- /dev/null +++ b/.workspace/quality/health-report.json @@ -0,0 +1,170 @@ +{ + "timestamp": "2025-07-23T21:21:35.971Z", + "overallScore": 93, + "status": "excellent", + "checks": { + "directoryStructure": { + "score": 100, + "issues": [], + "missing": [], + "present": [ + { + "name": "sessions", + "critical": true, + "description": "Session management" + }, + { + "name": "context", + "critical": true, + "description": "Shared context storage" + }, + { + "name": "handoffs", + "critical": true, + "description": "Agent handoff coordination" + }, + { + "name": "decisions", + "critical": false, + "description": "Decision tracking" + }, + { + "name": "progress", + "critical": false, + "description": "Progress monitoring" + }, + { + "name": "quality", + "critical": false, + "description": "Quality reports" + }, + { + "name": "archive", + "critical": false, + "description": "Archived data" + }, + { + "name": "hooks", + "critical": false, + "description": "Integration hooks" + }, + { + "name": "templates", + "critical": false, + "description": "Workspace templates" + }, + { + "name": "logs", + "critical": true, + "description": "Activity logging" + } + ] + }, + "workspaceConfig": { + "score": 100, + "issues": [], + "valid": true, + "config": { + "version": "1.0.0", + "created": "2025-07-23T21:21:29.430Z", + "lastUpdated": "2025-07-23T21:21:29.431Z", + "features": { + "crossIDESupport": true, + "sessionManagement": true, + "contextPersistence": true, + "agentHandoffs": true, + "qualityTracking": true + }, + "settings": { + "maxSessions": 10, + "sessionTimeout": 3600000, + "autoCleanup": true, + "logLevel": "info" + } + } + }, + "sessionHealth": { + "score": 100, + "issues": [], + "totalSessions": 1, + "activeSessions": 1, + "staleSessions": 0, + "corruptedSessions": 0, + "sessions": [ + { + "id": "85db2f3a0f721c3c", + "ide": "vscode", + "created": "2025-07-23T21:21:29.435Z", + "lastHeartbeat": "2025-07-23T21:21:29.435Z", + "pid": 97230, + "user": "bryan", + "cwd": "/mnt/c/Projects/BMAD-Method", + "nodeVersion": "v20.19.2", + "platform": "linux", + "arch": "x64", + "metadata": { + "ideSpecific": { + "supportsTerminalCommands": true, + "hasIntegratedGit": true, + "supportsPanels": true, + "hasExtensionSystem": true, + "features": [ + "extensions", + "integrated-terminal", + "git-integration" + ] + }, + "features": [ + "context-sharing", + "agent-handoffs", + "quality-tracking" + ] + }, + "status": "active", + "timeSinceLastHeartbeat": 6550 + } + ] + }, + "fileSystemPermissions": { + "score": 100, + "issues": [], + "canRead": true, + "canWrite": true, + "canExecute": true + }, + "logHealth": { + "score": 100, + "issues": [], + "exists": true, + "size": 128, + "recentEntries": 1, + "corruptedEntries": 0 + }, + "crossIDECompatibility": { + "score": 55, + "issues": [ + "Limited IDE template support: 1 templates found", + "No integration hooks configured" + ], + "ideSupport": { + "cursor": false, + "windsurf": false, + "vscode": true, + "trae": false, + "roo": false, + "cline": false, + "gemini": false, + "github-copilot": false + }, + "templateCount": 1, + "hookCount": 0 + } + }, + "summary": { + "totalIssues": 2, + "criticalIssues": 0, + "recommendations": [ + "Generate additional IDE-specific templates for better compatibility" + ] + } +} \ No newline at end of file diff --git a/.workspace/sessions/85db2f3a0f721c3c.json b/.workspace/sessions/85db2f3a0f721c3c.json new file mode 100644 index 00000000..d852974c --- /dev/null +++ b/.workspace/sessions/85db2f3a0f721c3c.json @@ -0,0 +1,30 @@ +{ + "id": "85db2f3a0f721c3c", + "ide": "vscode", + "created": "2025-07-23T21:21:29.435Z", + "lastHeartbeat": "2025-07-23T21:21:48.354Z", + "pid": 97230, + "user": "bryan", + "cwd": "/mnt/c/Projects/BMAD-Method", + "nodeVersion": "v20.19.2", + "platform": "linux", + "arch": "x64", + "metadata": { + "ideSpecific": { + "supportsTerminalCommands": true, + "hasIntegratedGit": true, + "supportsPanels": true, + "hasExtensionSystem": true, + "features": [ + "extensions", + "integrated-terminal", + "git-integration" + ] + }, + "features": [ + "context-sharing", + "agent-handoffs", + "quality-tracking" + ] + } +} \ No newline at end of file diff --git a/.workspace/templates/vscode-setup.md b/.workspace/templates/vscode-setup.md new file mode 100644 index 00000000..86994593 --- /dev/null +++ b/.workspace/templates/vscode-setup.md @@ -0,0 +1,16 @@ +# VSCODE Workspace Setup + +## VSCODE Integration +- Use terminal commands for workspace management +- Full workspace functionality available +- Context persists across IDE sessions + +## Available Commands +```bash +npm run workspace-init # Initialize workspace session +npm run workspace-status # Show workspace status +npm run workspace-cleanup # Clean and optimize workspace +npm run workspace-handoff # Manage agent handoffs +npm run workspace-sync # Synchronize context +npm run workspace-health # Check workspace health +``` diff --git a/.workspace/workspace-config.json b/.workspace/workspace-config.json new file mode 100644 index 00000000..349c4d54 --- /dev/null +++ b/.workspace/workspace-config.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0", + "created": "2025-07-23T21:21:29.430Z", + "lastUpdated": "2025-07-23T21:21:29.431Z", + "features": { + "crossIDESupport": true, + "sessionManagement": true, + "contextPersistence": true, + "agentHandoffs": true, + "qualityTracking": true + }, + "settings": { + "maxSessions": 10, + "sessionTimeout": 3600000, + "autoCleanup": true, + "logLevel": "info" + } +} \ No newline at end of file diff --git a/tools/demo-context-100-percent.js b/tools/demo-context-100-percent.js new file mode 100644 index 00000000..cfdc114d --- /dev/null +++ b/tools/demo-context-100-percent.js @@ -0,0 +1,272 @@ +#!/usr/bin/env node + +// Demo script to test 100% Complete Context Persistence functionality +const path = require('path'); +const fs = require('fs'); + +// Import context manager +const ContextManager = require('./installer/lib/context-manager'); + +async function demoContext100Percent() { + console.log('๐ŸŽฏ BMAD Context Persistence 100% Complete Demo'); + console.log('===============================================\n'); + + // Create a test workspace in /tmp + const testWorkspace = '/tmp/bmad-context-100-test/.workspace'; + if (fs.existsSync(testWorkspace)) { + fs.rmSync(path.dirname(testWorkspace), { recursive: true, force: true }); + } + + const contextManager = new ContextManager(testWorkspace); + console.log('โœ… Initialized test workspace at:', testWorkspace); + console.log(`๐Ÿ“ New directories: versions/, locks/`); + + try { + // Demo 1: Context Versioning System + console.log('\n๐Ÿ“š Demo 1: Context Versioning System'); + console.log('====================================='); + + // Create initial context + await contextManager.updateSharedContext({ + currentFocus: 'Implementing advanced context versioning system', + nextSteps: ['Add version tracking', 'Implement conflict resolution', 'Test rollback functionality'] + }); + + // Create first version + const contextFile = path.join(testWorkspace, 'context', 'shared-context.md'); + const initialContent = fs.readFileSync(contextFile, 'utf8'); + const version1 = await contextManager.createContextVersion('shared-context', initialContent, 'session-001', 'dev-agent'); + console.log(`โœ… Created version 1: ${version1}`); + + // Modify context + await contextManager.updateSharedContext({ + currentFocus: 'Implementing advanced context versioning system with conflict resolution', + nextSteps: ['Add version tracking', 'Implement conflict resolution', 'Test rollback functionality', 'Add merge capabilities'] + }); + + // Create second version + const modifiedContent = fs.readFileSync(contextFile, 'utf8'); + const version2 = await contextManager.createContextVersion('shared-context', modifiedContent, 'session-002', 'architect-agent'); + console.log(`โœ… Created version 2: ${version2}`); + + // Test version retrieval + const recentVersions = await contextManager.getRecentVersions('shared-context', 5); + console.log(`โœ… Retrieved ${recentVersions.length} recent versions`); + recentVersions.forEach((v, i) => { + console.log(` ${i + 1}. ${v.id} by ${v.agent} at ${new Date(v.timestamp).toLocaleString()}`); + }); + + // Demo 2: Conflict Detection and Resolution + console.log('\nโš”๏ธ Demo 2: Conflict Detection and Resolution'); + console.log('============================================='); + + // Simulate concurrent modification + const newContent1 = modifiedContent.replace('conflict resolution', 'advanced conflict resolution'); + const newContent2 = modifiedContent.replace('versioning system', 'versioning and backup system'); + + // Check for conflicts + const conflict1 = await contextManager.detectContextConflicts('shared-context', newContent1, 'session-003'); + console.log(`โœ… Conflict detection 1: ${conflict1.hasConflict ? 'CONFLICT DETECTED' : 'No conflict'}`); + + const conflict2 = await contextManager.detectContextConflicts('shared-context', newContent2, 'session-004'); + console.log(`โœ… Conflict detection 2: ${conflict2.hasConflict ? 'CONFLICT DETECTED' : 'No conflict'}`); + + // Test merge capabilities + if (conflict1.hasConflict || conflict2.hasConflict) { + console.log('โ„น๏ธ Conflicts detected - would normally trigger merge process'); + } + + // Create versions for merge testing + const change1 = { content: newContent1, timestamp: new Date().toISOString() }; + const change2 = { content: newContent2, timestamp: new Date(Date.now() + 1000).toISOString() }; + + try { + const mergedContent = await contextManager.mergeContextChanges('shared-context', initialContent, change1, change2); + console.log('โœ… Context merge successful'); + console.log(` Merged content length: ${mergedContent.length} characters`); + } catch (error) { + console.log(`โš ๏ธ Merge test: ${error.message}`); + } + + // Demo 3: Context Locking System + console.log('\n๐Ÿ”’ Demo 3: Context Locking System'); + console.log('================================='); + + // Acquire lock for session 1 + const lock1 = await contextManager.acquireContextLock('shared-context', 'session-001', 10000); + console.log(`โœ… Lock acquisition (session-001): ${lock1.acquired ? 'SUCCESS' : 'FAILED'}`); + + // Try to acquire same lock from session 2 + const lock2 = await contextManager.acquireContextLock('shared-context', 'session-002', 5000); + console.log(`โœ… Lock acquisition (session-002): ${lock2.acquired ? 'SUCCESS' : 'FAILED'}`); + if (!lock2.acquired) { + console.log(` Locked by: ${lock2.lockedBy}, expires: ${new Date(lock2.expiresAt).toLocaleString()}`); + } + + // Release lock + const release1 = await contextManager.releaseContextLock('shared-context', 'session-001'); + console.log(`โœ… Lock release (session-001): ${release1.released ? 'SUCCESS' : 'FAILED'}`); + + // Now session 2 can acquire the lock + const lock3 = await contextManager.acquireContextLock('shared-context', 'session-002', 5000); + console.log(`โœ… Lock acquisition (session-002) after release: ${lock3.acquired ? 'SUCCESS' : 'FAILED'}`); + + // Cleanup expired locks test + const cleanup = await contextManager.cleanupExpiredLocks(); + console.log(`โœ… Expired locks cleaned up: ${cleanup.cleanedCount}`); + + // Release remaining locks + await contextManager.releaseContextLock('shared-context', 'session-002'); + + // Demo 4: Rollback System + console.log('\nโช Demo 4: Context Rollback System'); + console.log('=================================='); + + // Get current context + const currentContext = await contextManager.loadSharedContext(); + console.log(`โœ… Current context focus: "${currentContext.currentFocus.substring(0, 50)}..."`); + + // Rollback to version 1 + if (recentVersions.length > 0) { + const rollbackResult = await contextManager.rollbackToVersion('shared-context', recentVersions[0].id); + console.log(`โœ… Rollback result: ${rollbackResult.success ? 'SUCCESS' : 'FAILED'}`); + if (rollbackResult.success) { + console.log(` Rolled back to: ${new Date(rollbackResult.rolledBackTo).toLocaleString()}`); + console.log(` Original agent: ${rollbackResult.agent}`); + } + + // Verify rollback worked + const rolledBackContext = await contextManager.loadSharedContext(); + console.log(`โœ… After rollback focus: "${rolledBackContext.currentFocus.substring(0, 50)}..."`); + } + + // Demo 5: BMAD Agent Integration Hooks + console.log('\n๐Ÿค– Demo 5: BMAD Agent Integration Hooks'); + console.log('======================================='); + + // Test story start hook + await contextManager.onStoryStart('Story-3.1-Advanced-Context-System', 'dev-agent', 'session-005'); + console.log('โœ… Story start hook executed'); + + // Test decision made hook + await contextManager.onDecisionMade({ + title: 'Implement context versioning with Git-like functionality', + decision: 'Use JSON-based versioning with content hashing for conflict detection', + rationale: 'Provides reliable conflict detection and merge capabilities', + impact: 'Enables safe concurrent context modifications' + }, 'architect-agent', 'session-005'); + console.log('โœ… Decision made hook executed'); + + // Test quality audit hook + await contextManager.onQualityAudit({ + story: 'Story-3.1-Advanced-Context-System', + realityAuditScore: '95/100', + patternCompliance: '92/100', + technicalDebtScore: '88/100', + overallQuality: 'A- (95/100)', + details: 'Context versioning system implemented with comprehensive conflict resolution' + }, 'qa-agent', 'session-005'); + console.log('โœ… Quality audit hook executed'); + + // Test agent handoff hook + await contextManager.onAgentHandoff('dev-agent', 'qa-agent', 'session-005', + 'Context versioning system complete. All features implemented and tested. Ready for comprehensive quality validation.'); + console.log('โœ… Agent handoff hook executed'); + + // Demo 6: Integration Verification + console.log('\n๐Ÿ”— Demo 6: Integration Verification'); + console.log('===================================='); + + // Verify all hook integrations worked + const finalStatus = await contextManager.getWorkspaceStatus(); + console.log('โœ… Integration verification:'); + console.log(` - Primary Agent: ${finalStatus.context.primaryAgent}`); + console.log(` - Current Focus: ${finalStatus.context.currentFocus.substring(0, 60)}...`); + console.log(` - Quality Score: ${finalStatus.progress.qualityScore}`); + console.log(` - Recent Decisions: ${finalStatus.recentDecisions.length}`); + + // Check that decision was auto-logged + const decisions = await contextManager.getDecisions(); + console.log(` - Total Decisions: ${decisions.length}`); + const autoDecision = decisions.find(d => d.title.includes('context versioning')); + console.log(` - Auto-logged decision: ${autoDecision ? 'YES' : 'NO'}`); + + // Demo 7: Directory Structure Verification + console.log('\n๐Ÿ“ Demo 7: Enhanced Directory Structure'); + console.log('======================================='); + + const directories = ['context', 'decisions', 'progress', 'quality', 'archive', 'versions', 'locks']; + console.log('โœ… Directory verification:'); + directories.forEach(dir => { + const dirPath = path.join(testWorkspace, dir); + const exists = fs.existsSync(dirPath); + const files = exists ? fs.readdirSync(dirPath).length : 0; + console.log(` - ${dir}/: ${exists ? 'EXISTS' : 'MISSING'} (${files} files)`); + }); + + // Verify version files + const versionFiles = fs.readdirSync(path.join(testWorkspace, 'versions')); + console.log(`โœ… Version files created: ${versionFiles.length}`); + versionFiles.forEach((file, index) => { + console.log(` ${index + 1}. ${file}`); + }); + + // Demo 8: Performance and Concurrency Testing + console.log('\nโšก Demo 8: Performance and Concurrency Testing'); + console.log('==============================================='); + + // Test multiple concurrent operations + const startTime = Date.now(); + + const operations = [ + contextManager.updateSharedContext({ currentFocus: 'Concurrent test 1' }), + contextManager.logDecision({ + title: 'Concurrent decision test', + agent: 'test-agent', + decision: 'Testing concurrent operations', + rationale: 'Verify system stability under load' + }), + contextManager.updateProgress({ + currentStory: 'Concurrency Test Story', + completedTasks: ['Test task 1', 'Test task 2'] + }), + contextManager.updateQualityMetrics({ + agent: 'test-agent', + story: 'Concurrency Test Story', + overallQuality: 'B+ (88/100)', + details: 'Concurrent operation test' + }) + ]; + + await Promise.all(operations); + const endTime = Date.now(); + + console.log(`โœ… Concurrent operations completed in ${endTime - startTime}ms`); + console.log(` All operations completed successfully`); + + console.log('\n๐ŸŽ‰ Context Persistence 100% Complete Demo Finished!'); + console.log('===================================================='); + console.log(`๐Ÿ“ Test workspace: ${testWorkspace}`); + console.log('๐ŸŽฏ 100% Features Demonstrated:'); + console.log(' โœ… Context versioning with content hashing'); + console.log(' โœ… Conflict detection and intelligent merging'); + console.log(' โœ… Context locking for concurrent access safety'); + console.log(' โœ… Rollback capabilities with backup protection'); + console.log(' โœ… BMAD agent integration hooks for automatic capture'); + console.log(' โœ… Story/Decision/Quality/Handoff event processing'); + console.log(' โœ… Performance optimization for concurrent operations'); + console.log(' โœ… Enterprise-grade directory structure'); + console.log('๐Ÿš€ Production-ready with enterprise features!'); + + } catch (error) { + console.error('\nโŒ Demo failed:', error.message); + console.error(error.stack); + } +} + +// Run the demo +if (require.main === module) { + demoContext100Percent(); +} + +module.exports = { demoContext100Percent }; \ No newline at end of file diff --git a/tools/demo-context-persistence.js b/tools/demo-context-persistence.js new file mode 100644 index 00000000..21478156 --- /dev/null +++ b/tools/demo-context-persistence.js @@ -0,0 +1,187 @@ +#!/usr/bin/env node + +// Demo script to test Context Persistence functionality +const path = require('path'); +const fs = require('fs'); + +// Import the ContextManager +const ContextManager = require('./installer/lib/context-manager'); + +async function demoContextPersistence() { + console.log('๐Ÿš€ BMAD Context Persistence Demo'); + console.log('=================================\n'); + + // Create a test workspace in /tmp + const testWorkspace = '/tmp/bmad-context-test/.workspace'; + if (fs.existsSync(testWorkspace)) { + fs.rmSync(path.dirname(testWorkspace), { recursive: true, force: true }); + } + + const contextManager = new ContextManager(testWorkspace); + console.log('โœ… Initialized test workspace at:', testWorkspace); + + try { + // Demo 1: Session Start and Context Updates + console.log('\n๐Ÿ“ Demo 1: Session Management and Context Updates'); + console.log('=================================================='); + + await contextManager.onSessionStart('demo-session-001', 'dev-agent'); + console.log('โœ… Started session: demo-session-001 with dev-agent'); + + await contextManager.updateSharedContext({ + currentFocus: 'Implementing context persistence framework for BMAD collaborative workspace system', + nextSteps: [ + 'Complete ContextManager class implementation', + 'Test integration with workspace utilities', + 'Create demo scenarios for all major features' + ] + }); + console.log('โœ… Updated shared context with development focus'); + + // Demo 2: Decision Logging + console.log('\n๐Ÿ“‹ Demo 2: Decision Logging'); + console.log('============================'); + + await contextManager.logDecision({ + title: 'Use file-based context persistence over database', + agent: 'dev-agent', + context: 'Story 1.2: Context Persistence Framework', + decision: 'Implement context persistence using structured markdown files instead of a database', + rationale: 'Files are cross-IDE compatible, human-readable, version-control friendly, and require no external dependencies', + alternatives: 'SQLite database, JSON files, in-memory store', + impact: 'Enables seamless context sharing across different development environments', + status: 'active' + }); + console.log('โœ… Logged architectural decision'); + + await contextManager.logDecision({ + title: 'Context compaction at 10MB threshold', + agent: 'dev-agent', + context: 'Context performance optimization', + decision: 'Automatically compact context files when they exceed 10MB', + rationale: 'Prevents workspace degradation while preserving critical information', + alternatives: 'No compaction, smaller threshold, manual compaction', + impact: 'Maintains workspace performance over long development cycles', + status: 'active' + }); + console.log('โœ… Logged performance decision'); + + // Demo 3: Progress Tracking + console.log('\n๐Ÿ“ˆ Demo 3: Progress Tracking'); + console.log('============================='); + + await contextManager.updateProgress({ + currentStory: 'Story 1.2: Context Persistence Framework', + completedTasks: [ + 'Create ContextManager class with core functionality', + 'Implement shared context file format and parsing', + 'Add decision logging with structured format', + 'Create progress tracking system' + ], + pendingTasks: [ + 'Add quality metrics integration', + 'Implement context compaction algorithm', + 'Create integration hooks for BMAD agents', + 'Add cross-IDE testing scenarios' + ], + blockers: [], + qualityScore: 'B+ (85/100) - Core functionality complete, testing needed' + }); + console.log('โœ… Updated progress tracking'); + + // Demo 4: Quality Metrics + console.log('\n๐Ÿ“Š Demo 4: Quality Metrics Tracking'); + console.log('===================================='); + + await contextManager.updateQualityMetrics({ + agent: 'qa-agent', + story: 'Story 1.2: Context Persistence Framework', + realityAuditScore: '85/100', + patternCompliance: '90/100', + technicalDebtScore: '80/100', + overallQuality: 'B+ (85/100)', + details: 'Core implementation solid. Needs integration testing and error handling improvements.' + }); + console.log('โœ… Recorded quality assessment'); + + // Demo 5: Context Retrieval + console.log('\n๐Ÿ” Demo 5: Context Retrieval and Status'); + console.log('======================================='); + + const status = await contextManager.getWorkspaceStatus(); + console.log('โœ… Retrieved workspace status:'); + console.log(` - Primary Agent: ${status.context.primaryAgent}`); + console.log(` - Current Focus: ${status.context.currentFocus.substring(0, 60)}...`); + console.log(` - Active Sessions: ${status.context.activeSessions.length}`); + console.log(` - Key Decisions: ${status.context.keyDecisions.length}`); + console.log(` - Next Steps: ${status.context.nextSteps.length}`); + console.log(` - Quality Score: ${status.progress.qualityScore}`); + console.log(` - Recent Decisions: ${status.recentDecisions.length}`); + + // Demo 6: Decision Retrieval with Filters + console.log('\n๐Ÿ”Ž Demo 6: Decision Retrieval with Filters'); + console.log('==========================================='); + + const allDecisions = await contextManager.getDecisions(); + console.log(`โœ… Retrieved ${allDecisions.length} total decisions`); + + const devDecisions = await contextManager.getDecisions({ agent: 'dev-agent' }); + console.log(`โœ… Retrieved ${devDecisions.length} decisions by dev-agent`); + + const activeDecisions = await contextManager.getDecisions({ status: 'active' }); + console.log(`โœ… Retrieved ${activeDecisions.length} active decisions`); + + // Demo 7: Context Export + console.log('\n๐Ÿ“ค Demo 7: Context Export'); + console.log('=========================='); + + const contextExport = await contextManager.exportContextSummary(); + console.log('โœ… Generated context export summary:'); + console.log(contextExport.substring(0, 300) + '...\n'); + + // Demo 8: Session End + console.log('๐Ÿ“ Demo 8: Session End'); + console.log('======================='); + + await contextManager.onSessionEnd('demo-session-001'); + console.log('โœ… Ended session: demo-session-001'); + + // Demo 9: File Structure Verification + console.log('\n๐Ÿ“ Demo 9: File Structure Verification'); + console.log('======================================='); + + const verifyFiles = [ + 'context/shared-context.md', + 'decisions/decisions-log.md', + 'progress/progress-summary.md', + 'quality/quality-metrics.md' + ]; + + for (const file of verifyFiles) { + const filePath = path.join(testWorkspace, file); + if (fs.existsSync(filePath)) { + const stats = fs.statSync(filePath); + console.log(`โœ… ${file} - ${stats.size} bytes`); + } else { + console.log(`โŒ ${file} - Not found`); + } + } + + console.log('\n๐ŸŽ‰ Context Persistence Demo Completed Successfully!'); + console.log('===================================================='); + console.log(`๐Ÿ“ Test workspace created at: ${testWorkspace}`); + console.log('๐Ÿ’ก All context files are human-readable markdown'); + console.log('๐Ÿ”ง Ready for integration with BMAD agents and IDE utilities'); + + } catch (error) { + console.error('\nโŒ Demo failed:', error.message); + console.error(error.stack); + } +} + +// Run the demo +if (require.main === module) { + demoContextPersistence(); +} + +module.exports = { demoContextPersistence }; \ No newline at end of file diff --git a/tools/demo-handoff-automation.js b/tools/demo-handoff-automation.js new file mode 100644 index 00000000..a621b01b --- /dev/null +++ b/tools/demo-handoff-automation.js @@ -0,0 +1,292 @@ +#!/usr/bin/env node + +// Demo script to test Agent Handoff Automation functionality +const path = require('path'); +const fs = require('fs'); + +// Import managers +const ContextManager = require('./installer/lib/context-manager'); +const HandoffManager = require('./installer/lib/handoff-manager'); + +async function demoHandoffAutomation() { + console.log('๐Ÿค BMAD Agent Handoff Automation Demo'); + console.log('=====================================\n'); + + // Create a test workspace in /tmp + const testWorkspace = '/tmp/bmad-handoff-test/.workspace'; + if (fs.existsSync(testWorkspace)) { + fs.rmSync(path.dirname(testWorkspace), { recursive: true, force: true }); + } + + const contextManager = new ContextManager(testWorkspace); + const handoffManager = new HandoffManager(testWorkspace); + console.log('โœ… Initialized test workspace at:', testWorkspace); + + try { + // Demo 1: Setup Rich Context for Handoffs + console.log('\n๐Ÿ“ Demo 1: Setting Up Rich Workspace Context'); + console.log('=============================================='); + + // Create rich context + await contextManager.onSessionStart('demo-dev-session', 'developer'); + await contextManager.updateSharedContext({ + currentFocus: 'Implementing user authentication system with OAuth2 integration and JWT token management', + nextSteps: [ + 'Complete OAuth2 provider integration with Google and GitHub', + 'Implement JWT token refresh mechanism', + 'Add user profile management endpoints', + 'Create comprehensive test suite for authentication flows' + ] + }); + console.log('โœ… Created shared context for authentication system development'); + + // Add several decisions + await contextManager.logDecision({ + title: 'Use JWT for session management', + agent: 'architect', + context: 'User Authentication System Design', + decision: 'Implement JWT tokens for stateless session management', + rationale: 'JWTs provide scalable, stateless authentication suitable for microservices architecture', + alternatives: 'Session cookies, server-side sessions, OAuth tokens only', + impact: 'Enables horizontal scaling and reduces server memory usage', + status: 'active' + }); + + await contextManager.logDecision({ + title: 'OAuth2 provider selection', + agent: 'developer', + context: 'Authentication integration requirements', + decision: 'Support Google, GitHub, and Microsoft OAuth2 providers', + rationale: 'Covers 90% of target users based on user research data', + alternatives: 'Single provider, more providers, custom authentication', + impact: 'Reduces friction for user onboarding and registration', + status: 'active' + }); + + await contextManager.logDecision({ + title: 'Database schema for user profiles', + agent: 'developer', + context: 'User data storage requirements', + decision: 'Use PostgreSQL with normalized user profile tables', + rationale: 'ACID compliance needed for user data integrity and security', + alternatives: 'MongoDB, SQLite, denormalized schema', + impact: 'Ensures data consistency and supports complex queries', + status: 'active' + }); + + // Add progress tracking + await contextManager.updateProgress({ + currentStory: 'Story 2.1: User Authentication System Implementation', + completedTasks: [ + 'Set up OAuth2 client registration with providers', + 'Implement basic JWT token generation and validation', + 'Create user profile database schema', + 'Add password hashing with bcrypt', + 'Implement basic login/logout endpoints' + ], + pendingTasks: [ + 'Add token refresh mechanism', + 'Implement OAuth2 callback handlers', + 'Create user profile management API', + 'Add comprehensive error handling', + 'Write integration tests for all auth flows' + ], + blockers: [ + 'Waiting for security team review of JWT implementation', + 'Need clarification on user profile field requirements' + ], + qualityScore: 'B+ (82/100) - Core functionality working, needs testing and security review' + }); + + // Add quality metrics + await contextManager.updateQualityMetrics({ + agent: 'qa-agent', + story: 'Story 2.1: User Authentication System Implementation', + realityAuditScore: '82/100', + patternCompliance: '85/100', + technicalDebtScore: '80/100', + overallQuality: 'B+ (82/100)', + details: 'Authentication flows working correctly. Security review pending. Test coverage at 75%.' + }); + + console.log('โœ… Rich context established with decisions, progress, and quality metrics'); + + // Demo 2: Developer to QA Handoff + console.log('\n๐Ÿ”„ Demo 2: Developer to QA Handoff (Agent-Specific Filtering)'); + console.log('=============================================================='); + + const devToQaHandoff = await handoffManager.createHandoff( + 'developer', + 'qa-engineer', + 'Authentication system core functionality complete. Ready for comprehensive testing including security validation, OAuth2 flow testing, and edge case scenarios.' + ); + + console.log('โœ… Created Dev โ†’ QA handoff with agent-specific filtering:'); + console.log(` - Handoff ID: ${devToQaHandoff.handoffId}`); + console.log(` - Target Agent Type: qa`); + console.log(` - Context filtered for testing and validation focus`); + + // Demo 3: QA to Architect Handoff + console.log('\n๐Ÿ”„ Demo 3: QA to Architect Handoff (Different Agent Filtering)'); + console.log('=============================================================='); + + const qaToArchHandoff = await handoffManager.createHandoff( + 'qa-engineer', + 'solution-architect', + 'Security testing identified potential scalability concerns with JWT token storage. Need architectural review for high-volume scenarios.' + ); + + console.log('โœ… Created QA โ†’ Architect handoff:'); + console.log(` - Handoff ID: ${qaToArchHandoff.handoffId}`); + console.log(` - Target Agent Type: architect`); + console.log(` - Context filtered for design and architecture decisions`); + + // Demo 4: Architect to PM Handoff + console.log('\n๐Ÿ”„ Demo 4: Architect to Project Manager Handoff'); + console.log('================================================'); + + const archToPmHandoff = await handoffManager.createHandoff( + 'solution-architect', + 'project-manager', + 'Architecture review complete. Recommended Redis implementation for session scaling will require additional 2 weeks and budget approval for infrastructure.' + ); + + console.log('โœ… Created Architect โ†’ PM handoff:'); + console.log(` - Handoff ID: ${archToPmHandoff.handoffId}`); + console.log(` - Target Agent Type: pm`); + console.log(` - Context filtered for business and stakeholder concerns`); + + // Demo 5: Handoff Validation and Quality + console.log('\n๐Ÿ“Š Demo 5: Handoff Validation and Quality Assessment'); + console.log('===================================================='); + + // Read and validate one of the handoff files + const handoffFile = fs.readFileSync(devToQaHandoff.filePath, 'utf8'); + console.log('โœ… Sample handoff content validation:'); + console.log(` - Context Summary: ${handoffFile.includes('Context Summary') ? 'Present' : 'Missing'}`); + console.log(` - Key Decisions: ${handoffFile.includes('Key Decisions Made') ? 'Present' : 'Missing'}`); + console.log(` - Next Actions: ${handoffFile.includes('Next Actions for') ? 'Present' : 'Missing'}`); + console.log(` - Agent-Specific Filtering: ${handoffFile.includes('Target Agent Type:') ? 'Applied' : 'Missing'}`); + console.log(` - File References: ${handoffFile.includes('Files and References') ? 'Present' : 'Missing'}`); + + // Demo 6: Handoff Registry and Status + console.log('\n๐Ÿ“‹ Demo 6: Handoff Registry and Status Management'); + console.log('================================================='); + + const pendingHandoffs = await handoffManager.getPendingHandoffs(); + console.log(`โœ… Total pending handoffs: ${pendingHandoffs.length}`); + + const qaHandoffs = await handoffManager.getPendingHandoffs('qa-engineer'); + console.log(`โœ… Pending handoffs for QA: ${qaHandoffs.length}`); + + const archHandoffs = await handoffManager.getPendingHandoffs('solution-architect'); + console.log(`โœ… Pending handoffs for Architect: ${archHandoffs.length}`); + + const stats = await handoffManager.getHandoffStats(); + console.log(`โœ… Handoff system statistics:`); + console.log(` - Total handoffs created: ${stats.total}`); + console.log(` - Average validation score: ${stats.avgScore.toFixed(1)}/100`); + console.log(` - Grade distribution: ${Object.entries(stats.gradeDistribution).map(([grade, count]) => `${grade}:${count}`).join(', ')}`); + + // Demo 7: Cross-IDE Compatibility Test + console.log('\n๐ŸŒ Demo 7: Cross-IDE Compatibility Verification'); + console.log('==============================================='); + + // Simulate handoffs between different IDE environments + const crossIdeHandoffs = [ + { from: 'claude-code-dev', to: 'cursor-qa', context: 'Handoff from Claude Code CLI to Cursor IDE' }, + { from: 'windsurf-architect', to: 'claude-code-dev', context: 'Handoff from Windsurf to Claude Code CLI' }, + { from: 'vscode-pm', to: 'gemini-qa', context: 'Handoff from VSCode to Gemini interface' } + ]; + + for (const handoff of crossIdeHandoffs) { + const result = await handoffManager.createHandoff(handoff.from, handoff.to, handoff.context); + console.log(`โœ… Cross-IDE handoff: ${handoff.from} โ†’ ${handoff.to}`); + } + + console.log('โœ… All cross-IDE handoffs created successfully - demonstrates universal compatibility'); + + // Demo 8: Handoff Content Analysis + console.log('\n๐Ÿ” Demo 8: Handoff Content Analysis (Agent-Specific Filtering Verification)'); + console.log('=========================================================================='); + + // Analyze Dev โ†’ QA handoff content + const devQaContent = fs.readFileSync(devToQaHandoff.filePath, 'utf8'); + const hasTestingFocus = devQaContent.toLowerCase().includes('testing') || + devQaContent.toLowerCase().includes('validation') || + devQaContent.toLowerCase().includes('quality'); + + console.log('โœ… Dev โ†’ QA handoff analysis:'); + console.log(` - Testing/Quality focus: ${hasTestingFocus ? 'YES' : 'NO'}`); + console.log(` - QA-specific actions: ${devQaContent.includes('Review acceptance criteria') ? 'YES' : 'NO'}`); + console.log(` - Implementation details filtered: ${!devQaContent.includes('code specifics') ? 'YES' : 'NO'}`); + + // Analyze Arch โ†’ PM handoff content + const archPmContent = fs.readFileSync(archToPmHandoff.filePath, 'utf8'); + const hasBusinessFocus = archPmContent.toLowerCase().includes('business') || + archPmContent.toLowerCase().includes('stakeholder') || + archPmContent.toLowerCase().includes('budget'); + + console.log('โœ… Architect โ†’ PM handoff analysis:'); + console.log(` - Business/Stakeholder focus: ${hasBusinessFocus ? 'YES' : 'NO'}`); + console.log(` - PM-specific actions: ${archPmContent.includes('Review project scope') ? 'YES' : 'NO'}`); + console.log(` - Technical details filtered: ${!archPmContent.includes('implementation specifics') ? 'YES' : 'NO'}`); + + // Demo 9: File Structure and Registry Verification + console.log('\n๐Ÿ“ Demo 9: File Structure and Registry Verification'); + console.log('==================================================='); + + const verifyFiles = [ + 'handoffs/handoff-registry.json', + 'handoffs/audit-trail.md' + ]; + + for (const file of verifyFiles) { + const filePath = path.join(testWorkspace, file); + if (fs.existsSync(filePath)) { + const stats = fs.statSync(filePath); + console.log(`โœ… ${file} - ${stats.size} bytes`); + + if (file.endsWith('.json')) { + const content = JSON.parse(fs.readFileSync(filePath, 'utf8')); + console.log(` - Registry entries: ${content.length}`); + } + } else { + console.log(`โŒ ${file} - Not found`); + } + } + + // Verify handoff files + const handoffFiles = fs.readdirSync(path.join(testWorkspace, 'handoffs')) + .filter(f => f.endsWith('.md') && f !== 'audit-trail.md'); + + console.log(`โœ… Individual handoff files created: ${handoffFiles.length}`); + handoffFiles.forEach((file, index) => { + console.log(` ${index + 1}. ${file}`); + }); + + console.log('\n๐ŸŽ‰ Agent Handoff Automation Demo Completed Successfully!'); + console.log('========================================================'); + console.log(`๐Ÿ“ Test workspace: ${testWorkspace}`); + console.log('๐Ÿค All handoff features demonstrated:'); + console.log(' โœ… Agent-specific context filtering'); + console.log(' โœ… Intelligent next action generation'); + console.log(' โœ… Context integration with decisions and progress'); + console.log(' โœ… Handoff validation and quality scoring'); + console.log(' โœ… Registry and audit trail management'); + console.log(' โœ… Cross-IDE compatibility'); + console.log(' โœ… Asynchronous handoff processing'); + console.log('๐Ÿ”ง Ready for integration with BMAD agents across all IDEs'); + + } catch (error) { + console.error('\nโŒ Demo failed:', error.message); + console.error(error.stack); + } +} + +// Run the demo +if (require.main === module) { + demoHandoffAutomation(); +} + +module.exports = { demoHandoffAutomation }; \ No newline at end of file diff --git a/tools/demo-multi-role-handoffs.js b/tools/demo-multi-role-handoffs.js new file mode 100644 index 00000000..23524900 --- /dev/null +++ b/tools/demo-multi-role-handoffs.js @@ -0,0 +1,263 @@ +#!/usr/bin/env node + +// Demo script to test Multi-Role Agent Handoff functionality +const path = require('path'); +const fs = require('fs'); + +// Import managers +const ContextManager = require('./installer/lib/context-manager'); +const HandoffManager = require('./installer/lib/handoff-manager'); + +async function demoMultiRoleHandoffs() { + console.log('๐Ÿง  BMAD Multi-Role Agent Handoff Demo'); + console.log('=====================================\n'); + + // Create a test workspace in /tmp + const testWorkspace = '/tmp/bmad-multi-role-test/.workspace'; + if (fs.existsSync(testWorkspace)) { + fs.rmSync(path.dirname(testWorkspace), { recursive: true, force: true }); + } + + const contextManager = new ContextManager(testWorkspace); + const handoffManager = new HandoffManager(testWorkspace); + console.log('โœ… Initialized test workspace at:', testWorkspace); + + try { + // Demo 1: Setup Complex Context Requiring Multi-Role Analysis + console.log('\n๐Ÿ“Š Demo 1: Setting Up Complex Performance Analysis Context'); + console.log('========================================================'); + + await contextManager.updateSharedContext({ + currentFocus: 'E-commerce platform experiencing performance degradation under high load with 40% slower checkout times', + nextSteps: [ + 'Analyze performance bottlenecks in payment processing system', + 'Research industry benchmarks for e-commerce performance', + 'Investigate user behavior patterns during high-traffic periods', + 'Design scalable architecture improvements', + 'Implement comprehensive performance monitoring' + ] + }); + + // Add technical decisions + await contextManager.logDecision({ + title: 'Database query optimization strategy', + agent: 'developer', + context: 'Performance optimization initiative', + decision: 'Implement query caching and index optimization for product catalog', + rationale: 'Analysis shows 60% of slow queries are product lookups during checkout', + impact: 'Expected 30% improvement in checkout response times', + status: 'active' + }); + + // Add analytical data + await contextManager.logDecision({ + title: 'Performance metrics baseline establishment', + agent: 'analyst', + context: 'Performance monitoring requirements', + decision: 'Track checkout completion time, cart abandonment rate, and server response times', + rationale: 'Need quantitative data to measure improvement impact and identify patterns', + impact: 'Enables data-driven optimization decisions', + status: 'active' + }); + + console.log('โœ… Complex performance context established'); + + // Demo 2: Developer-Analyst Handoff for Performance Optimization + console.log('\n๐Ÿ”„ Demo 2: Developer-Analyst Handoff (Performance Optimization)'); + console.log('=============================================================='); + + const devAnalystHandoff = await handoffManager.createHandoff( + 'senior-developer', + 'dev-analyst-specialist', + 'Performance optimization requires both implementation expertise and data analysis. Need to analyze current metrics, identify patterns, and implement data-driven improvements.' + ); + + console.log('โœ… Created Dev-Analyst handoff:'); + console.log(` - Handoff ID: ${devAnalystHandoff.handoffId}`); + console.log(` - Detected Type: ${handoffManager.getAgentType('dev-analyst-specialist')}`); + console.log(` - Multi-role filtering applied for development + analysis`); + + // Demo 3: QA-Research Handoff for Compliance Testing + console.log('\n๐Ÿ” Demo 3: QA-Research Handoff (Compliance & Standards)'); + console.log('======================================================'); + + const qaResearchHandoff = await handoffManager.createHandoff( + 'qa-engineer', + 'qa-research-specialist', + 'Payment processing compliance testing requires research into PCI DSS standards, industry testing practices, and regulatory requirements. Need comprehensive research-based testing strategy.' + ); + + console.log('โœ… Created QA-Research handoff:'); + console.log(` - Handoff ID: ${qaResearchHandoff.handoffId}`); + console.log(` - Detected Type: ${handoffManager.getAgentType('qa-research-specialist')}`); + console.log(` - Multi-role filtering applied for QA + research`); + + // Demo 4: Architect-Brainstorming Handoff for Innovation + console.log('\n๐Ÿ’ก Demo 4: Architect-Brainstorming Handoff (Creative Solutions)'); + console.log('==============================================================='); + + const archBrainstormHandoff = await handoffManager.createHandoff( + 'solution-architect', + 'architect-brainstorming-lead', + 'Traditional scaling approaches may not be sufficient. Need creative exploration of innovative architecture patterns, microservices alternatives, and cutting-edge performance solutions.' + ); + + console.log('โœ… Created Architect-Brainstorming handoff:'); + console.log(` - Handoff ID: ${archBrainstormHandoff.handoffId}`); + console.log(` - Detected Type: ${handoffManager.getAgentType('architect-brainstorming-lead')}`); + console.log(` - Multi-role filtering applied for architecture + brainstorming`); + + // Demo 5: PM-Analyst Handoff for Business Impact Analysis + console.log('\n๐Ÿ“ˆ Demo 5: PM-Analyst Handoff (Business Impact Analysis)'); + console.log('========================================================'); + + const pmAnalystHandoff = await handoffManager.createHandoff( + 'project-manager', + 'pm-analyst-consultant', + 'Need to analyze business impact of performance issues: revenue loss, customer satisfaction trends, competitive positioning. Require data-driven project prioritization and resource allocation.' + ); + + console.log('โœ… Created PM-Analyst handoff:'); + console.log(` - Handoff ID: ${pmAnalystHandoff.handoffId}`); + console.log(` - Detected Type: ${handoffManager.getAgentType('pm-analyst-consultant')}`); + console.log(` - Multi-role filtering applied for PM + analysis`); + + // Demo 6: UX-Research Handoff for User Experience Investigation + console.log('\n๐Ÿ‘ฅ Demo 6: UX-Research Handoff (User Experience Investigation)'); + console.log('============================================================='); + + const uxResearchHandoff = await handoffManager.createHandoff( + 'ux-designer', + 'ux-research-specialist', + 'Performance issues affect user experience. Need research into user behavior during slow checkout, accessibility implications, and evidence-based UX improvements for high-load scenarios.' + ); + + console.log('โœ… Created UX-Research handoff:'); + console.log(` - Handoff ID: ${uxResearchHandoff.handoffId}`); + console.log(` - Detected Type: ${handoffManager.getAgentType('ux-research-specialist')}`); + console.log(` - Multi-role filtering applied for UX + research`); + + // Demo 7: Pure Role Handoffs for Comparison + console.log('\n๐ŸŽฏ Demo 7: Pure Role Handoffs (Single-Role Comparison)'); + console.log('======================================================'); + + const pureAnalyst = await handoffManager.createHandoff('data-team', 'pure-analyst', 'Pure analyst role for comparison'); + const pureBrainstorming = await handoffManager.createHandoff('design-team', 'creative-brainstorming', 'Pure brainstorming role for comparison'); + const pureResearch = await handoffManager.createHandoff('research-team', 'research-specialist', 'Pure research role for comparison'); + + console.log('โœ… Created pure role handoffs for comparison:'); + console.log(` - Pure Analyst: ${handoffManager.getAgentType('pure-analyst')}`); + console.log(` - Pure Brainstorming: ${handoffManager.getAgentType('creative-brainstorming')}`); + console.log(` - Pure Research: ${handoffManager.getAgentType('research-specialist')}`); + + // Demo 8: Handoff Content Analysis - Multi-Role vs Single-Role + console.log('\n๐Ÿ” Demo 8: Multi-Role vs Single-Role Content Analysis'); + console.log('===================================================='); + + // Analyze Dev-Analyst handoff + const devAnalystContent = fs.readFileSync(devAnalystHandoff.filePath, 'utf8'); + console.log('โœ… Dev-Analyst handoff analysis:'); + console.log(` - Contains development focus: ${devAnalystContent.toLowerCase().includes('technical') ? 'YES' : 'NO'}`); + console.log(` - Contains analysis focus: ${devAnalystContent.toLowerCase().includes('analysis') || devAnalystContent.toLowerCase().includes('data') ? 'YES' : 'NO'}`); + console.log(` - Multi-role description: ${devAnalystContent.includes('multi') ? 'YES' : 'NO'}`); + + // Analyze QA-Research handoff + const qaResearchContent = fs.readFileSync(qaResearchHandoff.filePath, 'utf8'); + console.log('โœ… QA-Research handoff analysis:'); + console.log(` - Contains QA focus: ${qaResearchContent.toLowerCase().includes('testing') || qaResearchContent.toLowerCase().includes('quality') ? 'YES' : 'NO'}`); + console.log(` - Contains research focus: ${qaResearchContent.toLowerCase().includes('research') || qaResearchContent.toLowerCase().includes('investigate') ? 'YES' : 'NO'}`); + console.log(` - Research methodology actions: ${qaResearchContent.includes('Research industry') ? 'YES' : 'NO'}`); + + // Compare with pure role handoff + const pureAnalystContent = fs.readFileSync(pureAnalyst.filePath, 'utf8'); + console.log('โœ… Pure Analyst comparison:'); + console.log(` - Pure analyst actions only: ${pureAnalystContent.includes('Analyze available data') ? 'YES' : 'NO'}`); + console.log(` - No development specifics: ${!pureAnalystContent.toLowerCase().includes('code implementation') ? 'YES' : 'NO'}`); + + // Demo 9: Agent Type Detection Testing + console.log('\n๐Ÿค– Demo 9: Agent Type Detection Algorithm Testing'); + console.log('=================================================='); + + const testCases = [ + { name: 'dev-analyst-expert', expected: 'dev-analyst' }, + { name: 'qa-research-lead', expected: 'qa-research' }, + { name: 'architect-brainstorming-specialist', expected: 'architect-brainstorming' }, + { name: 'pm-analyst-consultant', expected: 'pm-analyst' }, + { name: 'ux-research-designer', expected: 'ux-research' }, + { name: 'senior-developer', expected: 'dev' }, + { name: 'data-analyst', expected: 'analyst' }, + { name: 'creative-brainstorming-facilitator', expected: 'brainstorming' }, + { name: 'research-scientist', expected: 'research' } + ]; + + console.log('โœ… Agent type detection results:'); + testCases.forEach(testCase => { + const detected = handoffManager.getAgentType(testCase.name); + const correct = detected === testCase.expected; + console.log(` ${correct ? 'โœ…' : 'โŒ'} ${testCase.name} โ†’ ${detected} (expected: ${testCase.expected})`); + }); + + // Demo 10: Multi-Role Registry Analysis + console.log('\n๐Ÿ“Š Demo 10: Multi-Role Registry and Analytics'); + console.log('=============================================='); + + const stats = await handoffManager.getHandoffStats(); + console.log('โœ… Enhanced handoff system statistics:'); + console.log(` - Total handoffs created: ${stats.total}`); + console.log(` - Average validation score: ${stats.avgScore.toFixed(1)}/100`); + console.log(` - Grade distribution: ${Object.entries(stats.gradeDistribution).map(([grade, count]) => `${grade}:${count}`).join(', ')}`); + + // Count multi-role vs single-role handoffs + const registryFile = path.join(testWorkspace, 'handoffs', 'handoff-registry.json'); + if (fs.existsSync(registryFile)) { + const registry = JSON.parse(fs.readFileSync(registryFile, 'utf8')); + const multiRoleCount = registry.filter(h => + handoffManager.multiRoleFilters[handoffManager.getAgentType(h.targetAgent)] + ).length; + + console.log(` - Multi-role handoffs: ${multiRoleCount}`); + console.log(` - Single-role handoffs: ${registry.length - multiRoleCount}`); + } + + // Demo 11: File Structure Verification + console.log('\n๐Ÿ“ Demo 11: Enhanced File Structure Verification'); + console.log('================================================='); + + const handoffFiles = fs.readdirSync(path.join(testWorkspace, 'handoffs')) + .filter(f => f.endsWith('.md') && f !== 'audit-trail.md'); + + console.log(`โœ… Handoff files created: ${handoffFiles.length}`); + + // Categorize by role type + const multiRoleFiles = handoffFiles.filter(file => { + const targetAgent = file.split('-to-')[1]?.split('-')[0] + '-' + file.split('-to-')[1]?.split('-')[1]; + return handoffManager.multiRoleFilters[handoffManager.getAgentType(targetAgent)]; + }); + + console.log(` - Multi-role handoff files: ${multiRoleFiles.length}`); + console.log(` - Single-role handoff files: ${handoffFiles.length - multiRoleFiles.length}`); + + console.log('\n๐ŸŽ‰ Multi-Role Agent Handoff Demo Completed Successfully!'); + console.log('======================================================='); + console.log(`๐Ÿ“ Test workspace: ${testWorkspace}`); + console.log('๐Ÿง  Enhanced multi-role capabilities demonstrated:'); + console.log(' โœ… 8 agent types supported (dev, qa, architect, pm, ux-expert, analyst, brainstorming, research)'); + console.log(' โœ… 5 multi-role combinations (dev-analyst, qa-research, architect-brainstorming, pm-analyst, ux-research)'); + console.log(' โœ… Intelligent agent type detection with multi-role pattern matching'); + console.log(' โœ… Combined context filtering for multi-role scenarios'); + console.log(' โœ… Role-specific and multi-role action generation'); + console.log(' โœ… Enhanced handoff validation for complex scenarios'); + console.log('๐Ÿš€ Ready for real-world multi-disciplinary AI agent collaboration!'); + + } catch (error) { + console.error('\nโŒ Demo failed:', error.message); + console.error(error.stack); + } +} + +// Run the demo +if (require.main === module) { + demoMultiRoleHandoffs(); +} + +module.exports = { demoMultiRoleHandoffs }; \ No newline at end of file diff --git a/tools/installer/lib/workspace-setup-old.js b/tools/installer/lib/workspace-setup-old.js new file mode 100644 index 00000000..346f76f8 --- /dev/null +++ b/tools/installer/lib/workspace-setup-old.js @@ -0,0 +1,721 @@ +const path = require("path"); +const fs = require("fs-extra"); +const chalk = require("chalk"); + +class WorkspaceSetup { + constructor() { + this.workspaceStructure = { + '.workspace': { + 'sessions': {}, + 'context': {}, + 'handoffs': {}, + 'decisions': {}, + 'progress': {}, + 'quality': {}, + 'archive': {} + } + }; + } + + async createWorkspaceDirectory(installDir, spinner) { + try { + spinner.text = 'Creating collaborative workspace structure...'; + + const workspacePath = path.join(installDir, '.workspace'); + + // Create main workspace directory + await fs.ensureDir(workspacePath); + + // Create subdirectories + const subdirs = ['sessions', 'context', 'handoffs', 'decisions', 'progress', 'quality', 'archive']; + + for (const subdir of subdirs) { + await fs.ensureDir(path.join(workspacePath, subdir)); + } + + // Create initial workspace configuration + const workspaceConfig = { + version: "1.0", + created: new Date().toISOString(), + structure: subdirs, + settings: { + maxContextSize: "10MB", + sessionTimeout: "2h", + archiveAfter: "30d", + maxConcurrentSessions: 5 + } + }; + + await fs.writeJSON( + path.join(workspacePath, 'workspace-config.json'), + workspaceConfig, + { spaces: 2 } + ); + + // Create initial README + const readmeContent = `# BMAD Collaborative Workspace + +This directory contains the collaborative workspace system for multi-session AI agent coordination. + +## Directory Structure + +- \`sessions/\` - Active session tracking +- \`context/\` - Shared context files and decisions +- \`handoffs/\` - Agent transition packages +- \`decisions/\` - Architectural and design decisions +- \`progress/\` - Story and task progress tracking +- \`quality/\` - Quality metrics and audit results +- \`archive/\` - Compressed historical context + +## Usage + +### Claude Code CLI Users +- Use \`*workspace-init\` to initialize a collaborative session +- Use \`*workspace-status\` to see active sessions and progress +- Use \`*workspace-cleanup\` for maintenance + +### Other IDE Users +- Run \`npm run workspace-init\` to initialize +- Run \`npm run workspace-status\` for status +- Run \`npm run workspace-cleanup\` for maintenance + +## Configuration + +Workspace settings can be modified in \`workspace-config.json\`. +`; + + await fs.writeFile(path.join(workspacePath, 'README.md'), readmeContent); + + return true; + } catch (error) { + console.error(chalk.red('Failed to create workspace directory:'), error.message); + return false; + } + } + + async createWorkspaceUtilities(installDir, selectedIDEs, spinner) { + try { + spinner.text = 'Installing workspace utilities...'; + + const utilsPath = path.join(installDir, 'workspace-utils'); + await fs.ensureDir(utilsPath); + + // Create utility scripts + await this.createInitScript(utilsPath); + await this.createStatusScript(utilsPath); + await this.createCleanupScript(utilsPath); + await this.createHandoffScript(utilsPath); + await this.createSyncScript(utilsPath); + + // Create package.json scripts if package.json exists + await this.addPackageJsonScripts(installDir); + + // Create IDE-specific documentation + await this.createIDEDocumentation(utilsPath, selectedIDEs); + + return true; + } catch (error) { + console.error(chalk.red('Failed to create workspace utilities:'), error.message); + return false; + } + } + + async createInitScript(utilsPath) { + const initScript = `#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +async function initWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found. Run \`npx bmad-method install\` first.'); + process.exit(1); + } + + // Generate session ID + const sessionId = crypto.randomBytes(8).toString('hex'); + const timestamp = new Date().toISOString(); + + // Create session file + const sessionData = { + id: sessionId, + created: timestamp, + lastHeartbeat: timestamp, + ide: process.env.IDE_TYPE || 'unknown', + pid: process.pid, + user: process.env.USER || process.env.USERNAME || 'unknown' + }; + + const sessionsPath = path.join(workspacePath, 'sessions'); + if (!fs.existsSync(sessionsPath)) { + fs.mkdirSync(sessionsPath, { recursive: true }); + } + + const sessionFile = path.join(sessionsPath, \`\${sessionId}.json\`); + fs.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2)); + + console.log('โœ… Workspace initialized successfully'); + console.log(\`๐Ÿ“ Session ID: \${sessionId}\`); + console.log(\`๐Ÿ• Created: \${timestamp}\`); + + return sessionId; + } catch (error) { + console.error('โŒ Failed to initialize workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + initWorkspace(); +} + +module.exports = { initWorkspace }; +`; + + await fs.writeFile(path.join(utilsPath, 'init.js'), initScript); + await fs.chmod(path.join(utilsPath, 'init.js'), 0o755); + } + + async createStatusScript(utilsPath) { + const statusScript = `#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +async function getWorkspaceStatus() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + // Read workspace config + const configPath = path.join(workspacePath, 'workspace-config.json'); + let config = {}; + if (fs.existsSync(configPath)) { + const configContent = fs.readFileSync(configPath, 'utf8'); + config = JSON.parse(configContent); + } + + // Get active sessions + const sessionsPath = path.join(workspacePath, 'sessions'); + let sessionFiles = []; + if (fs.existsSync(sessionsPath)) { + sessionFiles = fs.readdirSync(sessionsPath); + } + + const activeSessions = []; + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionContent = fs.readFileSync(sessionPath, 'utf8'); + const sessionData = JSON.parse(sessionContent); + activeSessions.push(sessionData); + } catch (e) { + // Skip corrupted session files + } + } + } + + // Display status + console.log('๐Ÿค BMAD Collaborative Workspace Status'); + console.log('====================================='); + console.log(\`๐Ÿ“ Workspace: \${workspacePath}\`); + console.log(\`โš™๏ธ Version: \${config.version || 'Unknown'}\`); + console.log(\`๐Ÿ• Created: \${config.created || 'Unknown'}\`); + console.log(\`๐Ÿ‘ฅ Active Sessions: \${activeSessions.length}\`); + + if (activeSessions.length > 0) { + console.log('\\n๐Ÿ“ Session Details:'); + activeSessions.forEach((session, index) => { + console.log(\` \${index + 1}. \${session.id} (\${session.ide}) - \${session.user}\`); + console.log(\` Created: \${new Date(session.created).toLocaleString()}\`); + console.log(\` Last Heartbeat: \${new Date(session.lastHeartbeat).toLocaleString()}\`); + }); + } + + // Check directory structure + const directories = ['context', 'handoffs', 'decisions', 'progress', 'quality', 'archive']; + const missingDirs = []; + + for (const dir of directories) { + if (!fs.existsSync(path.join(workspacePath, dir))) { + missingDirs.push(dir); + } + } + + if (missingDirs.length > 0) { + console.log(\`\\nโš ๏ธ Missing directories: \${missingDirs.join(', ')}\`); + console.log(' Run \`npm run workspace-cleanup\` to repair.'); + } else { + console.log('\\nโœ… Workspace structure is healthy'); + } + + } catch (error) { + console.error('โŒ Failed to get workspace status:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + getWorkspaceStatus(); +} + +module.exports = { getWorkspaceStatus }; +`; + + await fs.writeFile(path.join(utilsPath, 'status.js'), statusScript); + await fs.chmod(path.join(utilsPath, 'status.js'), 0o755); + } + + async createCleanupScript(utilsPath) { + const cleanupScript = `#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +function ensureDir(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +function removeFile(filePath) { + try { + fs.unlinkSync(filePath); + return true; + } catch (e) { + return false; + } +} + +function moveFile(sourcePath, targetPath) { + try { + const data = fs.readFileSync(sourcePath); + fs.writeFileSync(targetPath, data); + fs.unlinkSync(sourcePath); + return true; + } catch (e) { + return false; + } +} + +async function cleanupWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!await fs.pathExists(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + console.log('๐Ÿงน Starting workspace cleanup...'); + + // Repair directory structure + const directories = ['sessions', 'context', 'handoffs', 'decisions', 'progress', 'quality', 'archive']; + let repairedDirs = 0; + + for (const dir of directories) { + const dirPath = path.join(workspacePath, dir); + if (!await fs.pathExists(dirPath)) { + await fs.ensureDir(dirPath); + repairedDirs++; + } + } + + if (repairedDirs > 0) { + console.log(\`โœ… Repaired \${repairedDirs} missing directories\`); + } + + // Clean up expired sessions (older than 2 hours) + const sessionsPath = path.join(workspacePath, 'sessions'); + const sessionFiles = await fs.readdir(sessionsPath).catch(() => []); + const twoHoursAgo = Date.now() - (2 * 60 * 60 * 1000); + + let cleanedSessions = 0; + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionData = await fs.readJSON(sessionPath); + const lastHeartbeat = new Date(sessionData.lastHeartbeat).getTime(); + + if (lastHeartbeat < twoHoursAgo) { + await fs.remove(sessionPath); + cleanedSessions++; + } + } catch (e) { + // Remove corrupted session files + await fs.remove(path.join(sessionsPath, file)); + cleanedSessions++; + } + } + } + + if (cleanedSessions > 0) { + console.log(\`โœ… Cleaned up \${cleanedSessions} expired sessions\`); + } + + // Archive old context files (older than 30 days) + const contextPath = path.join(workspacePath, 'context'); + const archivePath = path.join(workspacePath, 'archive'); + const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000); + + if (await fs.pathExists(contextPath)) { + const contextFiles = await fs.readdir(contextPath).catch(() => []); + let archivedFiles = 0; + + for (const file of contextFiles) { + const filePath = path.join(contextPath, file); + const stats = await fs.stat(filePath).catch(() => null); + + if (stats && stats.mtime.getTime() < thirtyDaysAgo) { + const archiveFile = path.join(archivePath, \`archived-\${Date.now()}-\${file}\`); + await fs.move(filePath, archiveFile); + archivedFiles++; + } + } + + if (archivedFiles > 0) { + console.log(\`โœ… Archived \${archivedFiles} old context files\`); + } + } + + console.log('โœ… Workspace cleanup completed successfully'); + + } catch (error) { + console.error('โŒ Failed to cleanup workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + cleanupWorkspace(); +} + +module.exports = { cleanupWorkspace }; +`; + + await fs.writeFile(path.join(utilsPath, 'cleanup.js'), cleanupScript); + await fs.chmod(path.join(utilsPath, 'cleanup.js'), 0o755); + } + + async createHandoffScript(utilsPath) { + const handoffScript = `#!/usr/bin/env node +const fs = require('fs-extra'); +const path = require('path'); + +async function createHandoff(fromAgent, toAgent, context = '') { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + const handoffsPath = path.join(workspacePath, 'handoffs'); + + if (!await fs.pathExists(handoffsPath)) { + console.error('โŒ Workspace handoffs directory not found.'); + process.exit(1); + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const handoffId = \`\${fromAgent}-to-\${toAgent}-\${timestamp}\`; + const handoffFile = path.join(handoffsPath, \`\${handoffId}.md\`); + + const handoffContent = \`# Agent Handoff: \${fromAgent} โ†’ \${toAgent} + +**Created:** \${new Date().toISOString()} +**Handoff ID:** \${handoffId} +**Source Agent:** \${fromAgent} +**Target Agent:** \${toAgent} + +## Context Summary +\${context || 'No additional context provided.'} + +## Key Decisions Made +[To be filled by source agent] + +## Current Progress +[To be filled by source agent] + +## Next Actions for \${toAgent} +- [ ] [Action item 1] +- [ ] [Action item 2] +- [ ] [Action item 3] + +## Files and References +[List of relevant files and documentation] + +## Blockers and Dependencies +[Any blockers or dependencies the target agent should be aware of] + +## Handoff Validation +- [ ] Context completeness verified +- [ ] Decisions documented +- [ ] Next actions clearly defined +- [ ] References included +\`; + + await fs.writeFile(handoffFile, handoffContent); + + console.log('โœ… Handoff package created successfully'); + console.log(\`๐Ÿ“ฆ Handoff ID: \${handoffId}\`); + console.log(\`๐Ÿ“ File: \${handoffFile}\`); + + return handoffId; + } catch (error) { + console.error('โŒ Failed to create handoff:', error.message); + process.exit(1); + } +} + +// Command line usage +if (require.main === module) { + const args = process.argv.slice(2); + if (args.length < 2) { + console.log('Usage: node handoff.js [context]'); + process.exit(1); + } + + createHandoff(args[0], args[1], args[2] || ''); +} + +module.exports = { createHandoff }; +`; + + await fs.writeFile(path.join(utilsPath, 'handoff.js'), handoffScript); + await fs.chmod(path.join(utilsPath, 'handoff.js'), 0o755); + } + + async createSyncScript(utilsPath) { + const syncScript = `#!/usr/bin/env node +const fs = require('fs-extra'); +const path = require('path'); + +async function syncWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!await fs.pathExists(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + console.log('๐Ÿ”„ Synchronizing workspace context...'); + + // Update session heartbeat + const sessionsPath = path.join(workspacePath, 'sessions'); + const sessionFiles = await fs.readdir(sessionsPath).catch(() => []); + + // For simplicity, update the most recent session + let latestSession = null; + let latestTime = 0; + + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionData = await fs.readJSON(sessionPath); + const created = new Date(sessionData.created).getTime(); + + if (created > latestTime) { + latestTime = created; + latestSession = { path: sessionPath, data: sessionData }; + } + } catch (e) { + // Skip corrupted files + } + } + } + + if (latestSession) { + latestSession.data.lastHeartbeat = new Date().toISOString(); + await fs.writeJSON(latestSession.path, latestSession.data, { spaces: 2 }); + console.log(\`โœ… Updated session heartbeat: \${latestSession.data.id}\`); + } + + // Load and display recent context + const contextPath = path.join(workspacePath, 'context'); + const sharedContext = path.join(contextPath, 'shared-context.md'); + + if (await fs.pathExists(sharedContext)) { + const content = await fs.readFile(sharedContext, 'utf8'); + console.log('\\n๐Ÿ“„ Current Shared Context:'); + console.log('=' .repeat(50)); + console.log(content.substring(0, 500) + (content.length > 500 ? '...' : '')); + } else { + console.log('\\n๐Ÿ“„ No shared context available yet.'); + } + + console.log('\\nโœ… Workspace synchronization completed'); + + } catch (error) { + console.error('โŒ Failed to sync workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + syncWorkspace(); +} + +module.exports = { syncWorkspace }; +`; + + await fs.writeFile(path.join(utilsPath, 'sync.js'), syncScript); + await fs.chmod(path.join(utilsPath, 'sync.js'), 0o755); + } + + async addPackageJsonScripts(installDir) { + const packageJsonPath = path.join(installDir, 'package.json'); + + if (await fs.pathExists(packageJsonPath)) { + const packageJson = await fs.readJSON(packageJsonPath); + + if (!packageJson.scripts) { + packageJson.scripts = {}; + } + + // Add workspace scripts + packageJson.scripts['workspace-init'] = 'node workspace-utils/init.js'; + packageJson.scripts['workspace-status'] = 'node workspace-utils/status.js'; + packageJson.scripts['workspace-cleanup'] = 'node workspace-utils/cleanup.js'; + packageJson.scripts['workspace-handoff'] = 'node workspace-utils/handoff.js'; + packageJson.scripts['workspace-sync'] = 'node workspace-utils/sync.js'; + + await fs.writeJSON(packageJsonPath, packageJson, { spaces: 2 }); + } + } + + async createIDEDocumentation(utilsPath, selectedIDEs) { + const docsPath = path.join(utilsPath, 'docs'); + await fs.ensureDir(docsPath); + + const ideDocuments = { + 'cursor': `# Workspace Usage in Cursor + +## Getting Started +1. Open terminal in Cursor +2. Run \`npm run workspace-init\` to start collaborative session +3. Use \`npm run workspace-status\` to see active sessions + +## Best Practices +- Use @dev, @qa, @architect mentions to invoke BMAD agents +- Run \`npm run workspace-sync\` before major context switches +- Check \`npm run workspace-status\` to see other team members' progress +`, + 'windsurf': `# Workspace Usage in Windsurf + +## Getting Started +1. Open terminal in Windsurf +2. Run \`npm run workspace-init\` to start collaborative session +3. Use \`npm run workspace-status\` to see active sessions + +## Best Practices +- Use @agent-name to invoke BMAD agents +- Run \`npm run workspace-sync\` to stay synchronized +- Check workspace status regularly for team coordination +`, + 'claude-code': `# Workspace Usage in Claude Code CLI + +## Getting Started +Claude Code CLI users get enhanced workspace experience with native commands: + +- \`*workspace-init\` - Initialize collaborative session (automatic) +- \`*workspace-status\` - Show active sessions and progress +- \`*workspace-cleanup\` - Clean up and optimize workspace +- \`*workspace-handoff [agent]\` - Prepare handoff to another agent +- \`*workspace-sync\` - Synchronize with latest context + +## Native Integration +Workspace features are automatically integrated into your Claude Code CLI session: +- Automatic session registration and heartbeat +- Context-aware agent handoffs +- Intelligent workspace suggestions +`, + 'trae': `# Workspace Usage in Trae + +## Getting Started +1. Open terminal in Trae +2. Run \`npm run workspace-init\` to start collaborative session +3. Use \`npm run workspace-status\` to see active sessions + +## Integration +- Use @agent mentions to work with BMAD agents +- Workspace context automatically persists across sessions +- Use \`npm run workspace-handoff dev qa\` for explicit handoffs +` + }; + + for (const ide of selectedIDEs) { + if (ideDocuments[ide]) { + await fs.writeFile( + path.join(docsPath, `${ide}.md`), + ideDocuments[ide] + ); + } + } + } + + async setupClaudeCodeWorkspaceCommands(installDir, spinner) { + try { + spinner.text = 'Integrating workspace commands with Claude Code CLI agents...'; + + const bmadCorePath = path.join(installDir, '.bmad-core'); + const agentsPath = path.join(bmadCorePath, 'agents'); + + if (!await fs.pathExists(agentsPath)) { + console.warn('โš ๏ธ .bmad-core/agents directory not found. Skipping Claude Code integration.'); + return false; + } + + // Add workspace commands to key agents + const agentsToUpdate = ['dev.md', 'qa.md', 'sm.md']; + + for (const agentFile of agentsToUpdate) { + const agentPath = path.join(agentsPath, agentFile); + + if (await fs.pathExists(agentPath)) { + let content = await fs.readFile(agentPath, 'utf8'); + + // Check if workspace commands already exist + if (!content.includes('*workspace-init')) { + // Add workspace commands section + const workspaceCommands = ` + +## Workspace Commands + +You have access to collaborative workspace commands for multi-session coordination: + +- \`*workspace-init\` - Initialize collaborative workspace session +- \`*workspace-status\` - Show current workspace status and active sessions +- \`*workspace-cleanup\` - Clean up workspace files and optimize storage +- \`*workspace-handoff [target-agent]\` - Prepare context handoff to specified agent +- \`*workspace-sync\` - Synchronize with latest workspace context + +Use these commands to coordinate with other AI agents and maintain context across sessions. +`; + + // Insert before the last section (usually before final instructions) + const insertPoint = content.lastIndexOf('\n## '); + if (insertPoint > -1) { + content = content.slice(0, insertPoint) + workspaceCommands + '\n' + content.slice(insertPoint); + } else { + content += workspaceCommands; + } + + await fs.writeFile(agentPath, content); + } + } + } + + return true; + } catch (error) { + console.error(chalk.red('Failed to integrate Claude Code workspace commands:'), error.message); + return false; + } + } +} + +module.exports = WorkspaceSetup; \ No newline at end of file diff --git a/workspace-utils-enhanced/context.js b/workspace-utils-enhanced/context.js new file mode 100644 index 00000000..4ed45a78 --- /dev/null +++ b/workspace-utils-enhanced/context.js @@ -0,0 +1,384 @@ +#!/usr/bin/env node +const path = require('path'); +const fs = require('fs'); + +// Import context manager (copy functionality since we can't use external dependencies) +class ContextManager { + constructor(workspacePath = null) { + this.workspacePath = workspacePath || path.join(process.cwd(), '.workspace'); + this.contextPath = path.join(this.workspacePath, 'context'); + this.decisionsPath = path.join(this.workspacePath, 'decisions'); + this.progressPath = path.join(this.workspacePath, 'progress'); + this.qualityPath = path.join(this.workspacePath, 'quality'); + this.maxContextSize = 10 * 1024 * 1024; + this.initialize(); + } + + initialize() { + const dirs = [this.contextPath, this.decisionsPath, this.progressPath, this.qualityPath]; + for (const dir of dirs) { + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + } + } + + async loadSharedContext() { + try { + const contextFile = path.join(this.contextPath, 'shared-context.md'); + + if (!fs.existsSync(contextFile)) { + return this.getDefaultSharedContext(); + } + + const content = fs.readFileSync(contextFile, 'utf8'); + return this.parseSharedContext(content); + } catch (error) { + console.error('Failed to load shared context:', error.message); + return this.getDefaultSharedContext(); + } + } + + getDefaultSharedContext() { + return { + lastUpdated: new Date().toISOString(), + activeSessions: [], + primaryAgent: 'unknown', + currentFocus: 'No active development focus', + keyDecisions: [], + nextSteps: [], + sessionNotes: '' + }; + } + + parseSharedContext(content) { + const context = this.getDefaultSharedContext(); + + try { + const lastUpdatedMatch = content.match(/\*\*Last Updated:\*\* (.+)/); + if (lastUpdatedMatch) context.lastUpdated = lastUpdatedMatch[1]; + + const activeSessionsMatch = content.match(/\*\*Active Sessions:\*\* (.+)/); + if (activeSessionsMatch && activeSessionsMatch[1] !== 'None') { + context.activeSessions = activeSessionsMatch[1].split(', ').map(s => s.trim()); + } + + const primaryAgentMatch = content.match(/\*\*Primary Agent:\*\* (.+)/); + if (primaryAgentMatch) context.primaryAgent = primaryAgentMatch[1]; + + const currentFocusMatch = content.match(/## Current Focus\n([\s\S]*?)(?=\n## |$)/); + if (currentFocusMatch) context.currentFocus = currentFocusMatch[1].trim(); + + const keyDecisionsMatch = content.match(/## Key Decisions\n([\s\S]*?)(?=\n## |$)/); + if (keyDecisionsMatch) { + context.keyDecisions = keyDecisionsMatch[1] + .split('\n') + .filter(line => line.startsWith('- ')) + .map(line => line.substring(2).trim()) + .filter(decision => decision && !decision.includes('No decisions recorded')); + } + + const nextStepsMatch = content.match(/## Next Steps\n([\s\S]*?)(?=\n## |$)/); + if (nextStepsMatch) { + context.nextSteps = nextStepsMatch[1] + .split('\n') + .filter(line => line.startsWith('- ')) + .map(line => line.substring(2).trim()) + .filter(step => step && !step.includes('No next steps defined')); + } + + const sessionNotesMatch = content.match(/## Session Notes\n([\s\S]*?)$/); + if (sessionNotesMatch) context.sessionNotes = sessionNotesMatch[1].trim(); + + } catch (error) { + console.warn('Failed to parse shared context, using defaults:', error.message); + } + + return context; + } + + async loadProgress() { + try { + const progressFile = path.join(this.progressPath, 'progress-summary.md'); + + if (!fs.existsSync(progressFile)) { + return { + lastUpdated: new Date().toISOString(), + currentStory: 'No active story', + completedTasks: [], + pendingTasks: [], + blockers: [], + qualityScore: 'Not assessed' + }; + } + + const content = fs.readFileSync(progressFile, 'utf8'); + const progress = { + lastUpdated: new Date().toISOString(), + currentStory: 'No active story', + completedTasks: [], + pendingTasks: [], + blockers: [], + qualityScore: 'Not assessed' + }; + + const currentStoryMatch = content.match(/\*\*Current Story:\*\* (.+)/); + if (currentStoryMatch) progress.currentStory = currentStoryMatch[1]; + + const qualityScoreMatch = content.match(/\*\*Quality Score:\*\* (.+)/); + if (qualityScoreMatch) progress.qualityScore = qualityScoreMatch[1]; + + return progress; + } catch (error) { + console.error('Failed to load progress:', error.message); + return { + lastUpdated: new Date().toISOString(), + currentStory: 'No active story', + completedTasks: [], + pendingTasks: [], + blockers: [], + qualityScore: 'Not assessed' + }; + } + } + + async getLatestQualityMetrics() { + try { + const qualityFile = path.join(this.qualityPath, 'quality-metrics.md'); + + if (!fs.existsSync(qualityFile)) { + return null; + } + + const content = fs.readFileSync(qualityFile, 'utf8'); + const assessments = content.split('## Quality Assessment -'); + + if (assessments.length < 2) { + return null; + } + + return { + timestamp: assessments[1].split('\n')[0].trim(), + available: true + }; + } catch (error) { + console.error('Failed to get latest quality metrics:', error.message); + return null; + } + } +} + +// Context Management CLI +async function handleContextCommand() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found. Run `npx bmad-method install` first.'); + process.exit(1); + } + + const contextManager = new ContextManager(); + const args = process.argv.slice(2); + const command = args[0]; + + switch (command) { + case 'status': + await showContextStatus(contextManager); + break; + case 'load': + await loadContext(contextManager); + break; + case 'update': + await updateContext(contextManager, args.slice(1)); + break; + case 'decisions': + await showDecisions(contextManager); + break; + case 'progress': + await showProgress(contextManager); + break; + case 'export': + await exportContext(contextManager); + break; + default: + showUsage(); + } + } catch (error) { + console.error('โŒ Context command failed:', error.message); + process.exit(1); + } +} + +async function showContextStatus(contextManager) { + console.log('๐Ÿ“„ BMAD Context Status'); + console.log('======================'); + + const context = await contextManager.loadSharedContext(); + const progress = await contextManager.loadProgress(); + const quality = await contextManager.getLatestQualityMetrics(); + + console.log(`๐Ÿ“ Context: ${contextManager.contextPath}`); + console.log(`๐Ÿ• Last Updated: ${context.lastUpdated}`); + console.log(`๐Ÿ‘ค Primary Agent: ${context.primaryAgent}`); + console.log(`๐ŸŽฏ Current Focus: ${context.currentFocus}`); + console.log(`๐Ÿ“Š Quality Score: ${progress.qualityScore}`); + + if (context.activeSessions.length > 0) { + console.log(`\n๐Ÿ‘ฅ Active Sessions: ${context.activeSessions.length}`); + context.activeSessions.forEach((session, index) => { + console.log(` ${index + 1}. ${session}`); + }); + } + + if (context.keyDecisions.length > 0) { + console.log(`\n๐Ÿ”‘ Recent Key Decisions:`); + context.keyDecisions.slice(-3).forEach((decision, index) => { + console.log(` ${index + 1}. ${decision}`); + }); + } + + if (context.nextSteps.length > 0) { + console.log(`\nโญ๏ธ Next Steps:`); + context.nextSteps.forEach((step, index) => { + console.log(` ${index + 1}. ${step}`); + }); + } + + console.log(`\n๐Ÿ“ˆ Quality Metrics: ${quality ? 'Available' : 'Not available'}`); +} + +async function loadContext(contextManager) { + console.log('๐Ÿ“„ Loading workspace context...\n'); + + const context = await contextManager.loadSharedContext(); + + console.log('๐ŸŽฏ Current Focus:'); + console.log(context.currentFocus); + + if (context.keyDecisions.length > 0) { + console.log('\n๐Ÿ”‘ Key Decisions:'); + context.keyDecisions.forEach((decision, index) => { + console.log(` ${index + 1}. ${decision}`); + }); + } + + if (context.nextSteps.length > 0) { + console.log('\nโญ๏ธ Next Steps:'); + context.nextSteps.forEach((step, index) => { + console.log(` ${index + 1}. ${step}`); + }); + } + + if (context.sessionNotes) { + console.log('\n๐Ÿ“ Session Notes:'); + console.log(context.sessionNotes); + } +} + +async function showDecisions(contextManager) { + const decisionsFile = path.join(contextManager.decisionsPath, 'decisions-log.md'); + + if (!fs.existsSync(decisionsFile)) { + console.log('๐Ÿ“‹ No decisions recorded yet.'); + return; + } + + const content = fs.readFileSync(decisionsFile, 'utf8'); + const decisions = content.split('## Decision ').slice(1); + + console.log('๐Ÿ“‹ Architectural & Design Decisions'); + console.log('==================================='); + + decisions.slice(-5).forEach((decision, index) => { + const lines = decision.split('\n'); + const title = lines[0].replace(/^\d+:\s*/, ''); + const dateMatch = decision.match(/\*\*Date:\*\* (.+)/); + const agentMatch = decision.match(/\*\*Agent:\*\* (.+)/); + + console.log(`\n${decisions.length - 4 + index}. ${title}`); + if (dateMatch) console.log(` ๐Ÿ“… ${dateMatch[1]}`); + if (agentMatch) console.log(` ๐Ÿ‘ค ${agentMatch[1]}`); + }); +} + +async function showProgress(contextManager) { + const progress = await contextManager.loadProgress(); + + console.log('๐Ÿ“ˆ Development Progress'); + console.log('======================'); + console.log(`๐ŸŽฏ Current Story: ${progress.currentStory}`); + console.log(`๐Ÿ“Š Quality Score: ${progress.qualityScore}`); + console.log(`๐Ÿ• Last Updated: ${progress.lastUpdated}`); + + if (progress.completedTasks && progress.completedTasks.length > 0) { + console.log(`\nโœ… Completed Tasks: ${progress.completedTasks.length}`); + } + + if (progress.pendingTasks && progress.pendingTasks.length > 0) { + console.log(`โณ Pending Tasks: ${progress.pendingTasks.length}`); + } + + if (progress.blockers && progress.blockers.length > 0) { + console.log(`๐Ÿšซ Blockers: ${progress.blockers.length}`); + } +} + +async function exportContext(contextManager) { + try { + const context = await contextManager.loadSharedContext(); + const progress = await contextManager.loadProgress(); + + const exportContent = `# Workspace Context Export +**Generated:** ${new Date().toISOString()} + +## Current Status +- **Primary Agent:** ${context.primaryAgent} +- **Active Sessions:** ${context.activeSessions.join(', ') || 'None'} +- **Current Focus:** ${context.currentFocus} +- **Quality Score:** ${progress.qualityScore} + +## Key Decisions +${context.keyDecisions.map(d => `- ${d}`).join('\n') || '- No decisions recorded'} + +## Next Steps +${context.nextSteps.map(step => `- ${step}`).join('\n') || '- No next steps defined'} + +## Session Notes +${context.sessionNotes || 'No session notes available'} +`; + + const exportFile = path.join(process.cwd(), `context-export-${Date.now()}.md`); + fs.writeFileSync(exportFile, exportContent); + + console.log('โœ… Context exported successfully'); + console.log(`๐Ÿ“ Export file: ${exportFile}`); + } catch (error) { + console.error('โŒ Failed to export context:', error.message); + } +} + +function showUsage() { + console.log('๐Ÿ“„ BMAD Context Management'); + console.log('=========================='); + console.log(''); + console.log('Usage: node context.js '); + console.log(''); + console.log('Commands:'); + console.log(' status - Show current workspace context status'); + console.log(' load - Load and display shared context'); + console.log(' decisions - Show recent architectural decisions'); + console.log(' progress - Show development progress summary'); + console.log(' export - Export context to markdown file'); + console.log(''); + console.log('Examples:'); + console.log(' node context.js status'); + console.log(' node context.js load'); + console.log(' node context.js export'); +} + +if (require.main === module) { + handleContextCommand(); +} + +module.exports = { ContextManager }; \ No newline at end of file diff --git a/workspace-utils-fixed/cleanup.js b/workspace-utils-fixed/cleanup.js new file mode 100644 index 00000000..dffff88f --- /dev/null +++ b/workspace-utils-fixed/cleanup.js @@ -0,0 +1,141 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +function ensureDir(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +function removeFile(filePath) { + try { + fs.unlinkSync(filePath); + return true; + } catch (e) { + return false; + } +} + +function moveFile(sourcePath, targetPath) { + try { + const data = fs.readFileSync(sourcePath); + fs.writeFileSync(targetPath, data); + fs.unlinkSync(sourcePath); + return true; + } catch (e) { + return false; + } +} + +async function cleanupWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + console.log('๐Ÿงน Starting workspace cleanup...'); + + // Repair directory structure + const directories = ['sessions', 'context', 'handoffs', 'decisions', 'progress', 'quality', 'archive']; + let repairedDirs = 0; + + for (const dir of directories) { + const dirPath = path.join(workspacePath, dir); + if (!fs.existsSync(dirPath)) { + ensureDir(dirPath); + repairedDirs++; + } + } + + if (repairedDirs > 0) { + console.log(`โœ… Repaired ${repairedDirs} missing directories`); + } + + // Clean up expired sessions (older than 2 hours) + const sessionsPath = path.join(workspacePath, 'sessions'); + let sessionFiles = []; + if (fs.existsSync(sessionsPath)) { + sessionFiles = fs.readdirSync(sessionsPath); + } + const twoHoursAgo = Date.now() - (2 * 60 * 60 * 1000); + + let cleanedSessions = 0; + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionContent = fs.readFileSync(sessionPath, 'utf8'); + const sessionData = JSON.parse(sessionContent); + const lastHeartbeat = new Date(sessionData.lastHeartbeat).getTime(); + + if (lastHeartbeat < twoHoursAgo) { + if (removeFile(sessionPath)) { + cleanedSessions++; + } + } + } catch (e) { + // Remove corrupted session files + if (removeFile(path.join(sessionsPath, file))) { + cleanedSessions++; + } + } + } + } + + if (cleanedSessions > 0) { + console.log(`โœ… Cleaned up ${cleanedSessions} expired sessions`); + } + + // Archive old context files (older than 30 days) + const contextPath = path.join(workspacePath, 'context'); + const archivePath = path.join(workspacePath, 'archive'); + const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000); + + if (fs.existsSync(contextPath)) { + let contextFiles = []; + try { + contextFiles = fs.readdirSync(contextPath); + } catch (e) { + contextFiles = []; + } + + let archivedFiles = 0; + + for (const file of contextFiles) { + const filePath = path.join(contextPath, file); + try { + const stats = fs.statSync(filePath); + + if (stats.mtime.getTime() < thirtyDaysAgo) { + const archiveFile = path.join(archivePath, `archived-${Date.now()}-${file}`); + if (moveFile(filePath, archiveFile)) { + archivedFiles++; + } + } + } catch (e) { + // Skip files that can't be processed + } + } + + if (archivedFiles > 0) { + console.log(`โœ… Archived ${archivedFiles} old context files`); + } + } + + console.log('โœ… Workspace cleanup completed successfully'); + + } catch (error) { + console.error('โŒ Failed to cleanup workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + cleanupWorkspace(); +} + +module.exports = { cleanupWorkspace }; \ No newline at end of file diff --git a/workspace-utils-fixed/handoff.js b/workspace-utils-fixed/handoff.js new file mode 100644 index 00000000..b6a130c3 --- /dev/null +++ b/workspace-utils-fixed/handoff.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +async function createHandoff(fromAgent, toAgent, context = '') { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + const handoffsPath = path.join(workspacePath, 'handoffs'); + + if (!fs.existsSync(handoffsPath)) { + console.error('โŒ Workspace handoffs directory not found.'); + process.exit(1); + } + + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const handoffId = `${fromAgent}-to-${toAgent}-${timestamp}`; + const handoffFile = path.join(handoffsPath, `${handoffId}.md`); + + const handoffContent = `# Agent Handoff: ${fromAgent} โ†’ ${toAgent} + +**Created:** ${new Date().toISOString()} +**Handoff ID:** ${handoffId} +**Source Agent:** ${fromAgent} +**Target Agent:** ${toAgent} + +## Context Summary +${context || 'No additional context provided.'} + +## Key Decisions Made +[To be filled by source agent] + +## Current Progress +[To be filled by source agent] + +## Next Actions for ${toAgent} +- [ ] [Action item 1] +- [ ] [Action item 2] +- [ ] [Action item 3] + +## Files and References +[List of relevant files and documentation] + +## Blockers and Dependencies +[Any blockers or dependencies the target agent should be aware of] + +## Handoff Validation +- [ ] Context completeness verified +- [ ] Decisions documented +- [ ] Next actions clearly defined +- [ ] References included +`; + + fs.writeFileSync(handoffFile, handoffContent); + + console.log('โœ… Handoff package created successfully'); + console.log(`๐Ÿ“ฆ Handoff ID: ${handoffId}`); + console.log(`๐Ÿ“ File: ${handoffFile}`); + + return handoffId; + } catch (error) { + console.error('โŒ Failed to create handoff:', error.message); + process.exit(1); + } +} + +// Command line usage +if (require.main === module) { + const args = process.argv.slice(2); + if (args.length < 2) { + console.log('Usage: node handoff.js [context]'); + process.exit(1); + } + + createHandoff(args[0], args[1], args[2] || ''); +} + +module.exports = { createHandoff }; \ No newline at end of file diff --git a/workspace-utils-fixed/init.js b/workspace-utils-fixed/init.js new file mode 100644 index 00000000..8c2a0df8 --- /dev/null +++ b/workspace-utils-fixed/init.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); + +async function initWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found. Run `npx bmad-method install` first.'); + process.exit(1); + } + + // Generate session ID + const sessionId = crypto.randomBytes(8).toString('hex'); + const timestamp = new Date().toISOString(); + + // Create session file + const sessionData = { + id: sessionId, + created: timestamp, + lastHeartbeat: timestamp, + ide: process.env.IDE_TYPE || 'unknown', + pid: process.pid, + user: process.env.USER || process.env.USERNAME || 'unknown' + }; + + const sessionsPath = path.join(workspacePath, 'sessions'); + if (!fs.existsSync(sessionsPath)) { + fs.mkdirSync(sessionsPath, { recursive: true }); + } + + const sessionFile = path.join(sessionsPath, `${sessionId}.json`); + fs.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2)); + + console.log('โœ… Workspace initialized successfully'); + console.log(`๐Ÿ“ Session ID: ${sessionId}`); + console.log(`๐Ÿ• Created: ${timestamp}`); + + return sessionId; + } catch (error) { + console.error('โŒ Failed to initialize workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + initWorkspace(); +} + +module.exports = { initWorkspace }; \ No newline at end of file diff --git a/workspace-utils-fixed/status.js b/workspace-utils-fixed/status.js new file mode 100644 index 00000000..73b03eee --- /dev/null +++ b/workspace-utils-fixed/status.js @@ -0,0 +1,87 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +async function getWorkspaceStatus() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + // Read workspace config + const configPath = path.join(workspacePath, 'workspace-config.json'); + let config = {}; + if (fs.existsSync(configPath)) { + const configContent = fs.readFileSync(configPath, 'utf8'); + config = JSON.parse(configContent); + } + + // Get active sessions + const sessionsPath = path.join(workspacePath, 'sessions'); + let sessionFiles = []; + if (fs.existsSync(sessionsPath)) { + sessionFiles = fs.readdirSync(sessionsPath); + } + + const activeSessions = []; + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionContent = fs.readFileSync(sessionPath, 'utf8'); + const sessionData = JSON.parse(sessionContent); + activeSessions.push(sessionData); + } catch (e) { + // Skip corrupted session files + } + } + } + + // Display status + console.log('๐Ÿค BMAD Collaborative Workspace Status'); + console.log('====================================='); + console.log(`๐Ÿ“ Workspace: ${workspacePath}`); + console.log(`โš™๏ธ Version: ${config.version || 'Unknown'}`); + console.log(`๐Ÿ• Created: ${config.created || 'Unknown'}`); + console.log(`๐Ÿ‘ฅ Active Sessions: ${activeSessions.length}`); + + if (activeSessions.length > 0) { + console.log('\n๐Ÿ“ Session Details:'); + activeSessions.forEach((session, index) => { + console.log(` ${index + 1}. ${session.id} (${session.ide}) - ${session.user}`); + console.log(` Created: ${new Date(session.created).toLocaleString()}`); + console.log(` Last Heartbeat: ${new Date(session.lastHeartbeat).toLocaleString()}`); + }); + } + + // Check directory structure + const directories = ['context', 'handoffs', 'decisions', 'progress', 'quality', 'archive']; + const missingDirs = []; + + for (const dir of directories) { + if (!fs.existsSync(path.join(workspacePath, dir))) { + missingDirs.push(dir); + } + } + + if (missingDirs.length > 0) { + console.log(`\nโš ๏ธ Missing directories: ${missingDirs.join(', ')}`); + console.log(' Run `node workspace-utils/cleanup.js` to repair.'); + } else { + console.log('\nโœ… Workspace structure is healthy'); + } + + } catch (error) { + console.error('โŒ Failed to get workspace status:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + getWorkspaceStatus(); +} + +module.exports = { getWorkspaceStatus }; \ No newline at end of file diff --git a/workspace-utils-fixed/sync.js b/workspace-utils-fixed/sync.js new file mode 100644 index 00000000..37fb0357 --- /dev/null +++ b/workspace-utils-fixed/sync.js @@ -0,0 +1,84 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +async function syncWorkspace() { + try { + const workspacePath = path.join(process.cwd(), '.workspace'); + + if (!fs.existsSync(workspacePath)) { + console.error('โŒ Workspace directory not found.'); + process.exit(1); + } + + console.log('๐Ÿ”„ Synchronizing workspace context...'); + + // Update session heartbeat + const sessionsPath = path.join(workspacePath, 'sessions'); + let sessionFiles = []; + if (fs.existsSync(sessionsPath)) { + try { + sessionFiles = fs.readdirSync(sessionsPath); + } catch (e) { + sessionFiles = []; + } + } + + // For simplicity, update the most recent session + let latestSession = null; + let latestTime = 0; + + for (const file of sessionFiles) { + if (file.endsWith('.json')) { + try { + const sessionPath = path.join(sessionsPath, file); + const sessionContent = fs.readFileSync(sessionPath, 'utf8'); + const sessionData = JSON.parse(sessionContent); + const created = new Date(sessionData.created).getTime(); + + if (created > latestTime) { + latestTime = created; + latestSession = { path: sessionPath, data: sessionData }; + } + } catch (e) { + // Skip corrupted files + } + } + } + + if (latestSession) { + latestSession.data.lastHeartbeat = new Date().toISOString(); + fs.writeFileSync(latestSession.path, JSON.stringify(latestSession.data, null, 2)); + console.log(`โœ… Updated session heartbeat: ${latestSession.data.id}`); + } + + // Load and display recent context + const contextPath = path.join(workspacePath, 'context'); + const sharedContext = path.join(contextPath, 'shared-context.md'); + + if (fs.existsSync(sharedContext)) { + try { + const content = fs.readFileSync(sharedContext, 'utf8'); + console.log('\n๐Ÿ“„ Current Shared Context:'); + console.log('='.repeat(50)); + console.log(content.substring(0, 500) + (content.length > 500 ? '...' : '')); + } catch (e) { + console.log('\n๐Ÿ“„ Shared context file exists but could not be read.'); + } + } else { + console.log('\n๐Ÿ“„ No shared context available yet.'); + } + + console.log('\nโœ… Workspace synchronization completed'); + + } catch (error) { + console.error('โŒ Failed to sync workspace:', error.message); + process.exit(1); + } +} + +if (require.main === module) { + syncWorkspace(); +} + +module.exports = { syncWorkspace }; \ No newline at end of file