kiro-spec-engine 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/README.md +172 -0
- package/bin/kiro-spec-engine.js +62 -0
- package/docs/adoption-guide.md +506 -0
- package/docs/agent-hooks-analysis.md +815 -0
- package/docs/architecture.md +706 -0
- package/docs/cross-tool-guide.md +554 -0
- package/docs/developer-guide.md +615 -0
- package/docs/manual-workflows-guide.md +417 -0
- package/docs/steering-strategy-guide.md +196 -0
- package/docs/upgrade-guide.md +632 -0
- package/lib/adoption/detection-engine.js +14 -4
- package/lib/commands/adopt.js +117 -3
- package/lib/commands/context.js +99 -0
- package/lib/commands/prompt.js +105 -0
- package/lib/commands/status.js +225 -0
- package/lib/commands/task.js +199 -0
- package/lib/commands/watch.js +569 -0
- package/lib/commands/workflows.js +240 -0
- package/lib/commands/workspace.js +189 -0
- package/lib/context/context-exporter.js +378 -0
- package/lib/context/prompt-generator.js +482 -0
- package/lib/steering/adoption-config.js +164 -0
- package/lib/steering/steering-manager.js +289 -0
- package/lib/task/task-claimer.js +430 -0
- package/lib/utils/tool-detector.js +383 -0
- package/lib/watch/action-executor.js +458 -0
- package/lib/watch/event-debouncer.js +323 -0
- package/lib/watch/execution-logger.js +550 -0
- package/lib/watch/file-watcher.js +499 -0
- package/lib/watch/presets.js +266 -0
- package/lib/watch/watch-manager.js +533 -0
- package/lib/workspace/workspace-manager.js +370 -0
- package/lib/workspace/workspace-sync.js +356 -0
- package/package.json +4 -1
- package/template/.kiro/tools/backup_manager.py +295 -0
- package/template/.kiro/tools/configuration_manager.py +218 -0
- package/template/.kiro/tools/document_evaluator.py +550 -0
- package/template/.kiro/tools/enhancement_logger.py +168 -0
- package/template/.kiro/tools/error_handler.py +335 -0
- package/template/.kiro/tools/improvement_identifier.py +444 -0
- package/template/.kiro/tools/modification_applicator.py +737 -0
- package/template/.kiro/tools/quality_gate_enforcer.py +207 -0
- package/template/.kiro/tools/quality_scorer.py +305 -0
- package/template/.kiro/tools/report_generator.py +154 -0
- package/template/.kiro/tools/ultrawork_enhancer_refactored.py +0 -0
- package/template/.kiro/tools/ultrawork_enhancer_v2.py +463 -0
- package/template/.kiro/tools/ultrawork_enhancer_v3.py +606 -0
- package/template/.kiro/tools/workflow_quality_gate.py +100 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflows Command
|
|
3
|
+
*
|
|
4
|
+
* Lists and displays manual workflow documentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Available workflows
|
|
13
|
+
*/
|
|
14
|
+
const WORKFLOWS = {
|
|
15
|
+
'task-sync': {
|
|
16
|
+
name: 'Task Sync Workflow',
|
|
17
|
+
description: 'Keep workspace synchronized with task progress',
|
|
18
|
+
timeEstimate: '30-60 seconds',
|
|
19
|
+
file: 'manual-workflows-guide.md',
|
|
20
|
+
section: 'task-sync-workflow'
|
|
21
|
+
},
|
|
22
|
+
'context-export': {
|
|
23
|
+
name: 'Context Export Workflow',
|
|
24
|
+
description: 'Export spec context for sharing',
|
|
25
|
+
timeEstimate: '15-45 seconds',
|
|
26
|
+
file: 'manual-workflows-guide.md',
|
|
27
|
+
section: 'context-export-workflow'
|
|
28
|
+
},
|
|
29
|
+
'prompt-generation': {
|
|
30
|
+
name: 'Prompt Generation Workflow',
|
|
31
|
+
description: 'Generate AI prompts for specific tasks',
|
|
32
|
+
timeEstimate: '20-30 seconds',
|
|
33
|
+
file: 'manual-workflows-guide.md',
|
|
34
|
+
section: 'prompt-generation-workflow'
|
|
35
|
+
},
|
|
36
|
+
'daily': {
|
|
37
|
+
name: 'Daily Workflow Checklist',
|
|
38
|
+
description: 'Complete daily workflow checklist',
|
|
39
|
+
timeEstimate: '2-3 minutes',
|
|
40
|
+
file: 'manual-workflows-guide.md',
|
|
41
|
+
section: 'daily-workflow-checklist'
|
|
42
|
+
},
|
|
43
|
+
'task-completion': {
|
|
44
|
+
name: 'Task Completion Checklist',
|
|
45
|
+
description: 'Checklist for completing a task',
|
|
46
|
+
timeEstimate: 'Varies',
|
|
47
|
+
file: 'manual-workflows-guide.md',
|
|
48
|
+
section: 'task-completion-checklist'
|
|
49
|
+
},
|
|
50
|
+
'spec-creation': {
|
|
51
|
+
name: 'Spec Creation Checklist',
|
|
52
|
+
description: 'Checklist for creating a new spec',
|
|
53
|
+
timeEstimate: '10-15 minutes',
|
|
54
|
+
file: 'manual-workflows-guide.md',
|
|
55
|
+
section: 'spec-creation-checklist'
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* List all available workflows
|
|
61
|
+
*/
|
|
62
|
+
async function listWorkflows() {
|
|
63
|
+
console.log(chalk.blue('📋 Available Workflows'));
|
|
64
|
+
console.log();
|
|
65
|
+
|
|
66
|
+
Object.entries(WORKFLOWS).forEach(([id, workflow]) => {
|
|
67
|
+
console.log(chalk.cyan(` ${id}`));
|
|
68
|
+
console.log(` ${workflow.name}`);
|
|
69
|
+
console.log(chalk.gray(` ${workflow.description}`));
|
|
70
|
+
console.log(chalk.gray(` Time: ${workflow.timeEstimate}`));
|
|
71
|
+
console.log();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log(chalk.gray('Run'), chalk.cyan('kse workflows show <workflow-id>'), chalk.gray('to view details'));
|
|
75
|
+
console.log(chalk.gray('Run'), chalk.cyan('kse workflows guide'), chalk.gray('to open full guide'));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Show specific workflow details
|
|
80
|
+
*
|
|
81
|
+
* @param {string} workflowId - Workflow ID
|
|
82
|
+
*/
|
|
83
|
+
async function showWorkflow(workflowId) {
|
|
84
|
+
const workflow = WORKFLOWS[workflowId];
|
|
85
|
+
|
|
86
|
+
if (!workflow) {
|
|
87
|
+
console.log(chalk.red('❌ Unknown workflow:'), workflowId);
|
|
88
|
+
console.log();
|
|
89
|
+
console.log(chalk.gray('Available workflows:'));
|
|
90
|
+
Object.keys(WORKFLOWS).forEach(id => {
|
|
91
|
+
console.log(chalk.gray(` - ${id}`));
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(chalk.blue('📋'), chalk.bold(workflow.name));
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(chalk.gray('Description:'), workflow.description);
|
|
99
|
+
console.log(chalk.gray('Time Estimate:'), workflow.timeEstimate);
|
|
100
|
+
console.log();
|
|
101
|
+
|
|
102
|
+
// Try to read and display the workflow section
|
|
103
|
+
try {
|
|
104
|
+
const docsPath = path.join(__dirname, '../../docs', workflow.file);
|
|
105
|
+
|
|
106
|
+
if (await fs.pathExists(docsPath)) {
|
|
107
|
+
console.log(chalk.blue('📖 Full documentation:'));
|
|
108
|
+
console.log(chalk.cyan(` docs/${workflow.file}#${workflow.section}`));
|
|
109
|
+
console.log();
|
|
110
|
+
console.log(chalk.gray('Tip: Open the file in your editor for complete instructions'));
|
|
111
|
+
} else {
|
|
112
|
+
console.log(chalk.yellow('⚠️ Documentation file not found'));
|
|
113
|
+
console.log(chalk.gray(` Expected: docs/${workflow.file}`));
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.log(chalk.yellow('⚠️ Could not read documentation'));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Open full workflows guide
|
|
122
|
+
*/
|
|
123
|
+
async function openGuide() {
|
|
124
|
+
const guidePath = path.join(__dirname, '../../docs/manual-workflows-guide.md');
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
if (await fs.pathExists(guidePath)) {
|
|
128
|
+
console.log(chalk.blue('📖 Manual Workflows Guide'));
|
|
129
|
+
console.log();
|
|
130
|
+
console.log(chalk.cyan(' docs/manual-workflows-guide.md'));
|
|
131
|
+
console.log();
|
|
132
|
+
console.log(chalk.gray('Open this file in your editor for complete workflow documentation'));
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(chalk.blue('Contents:'));
|
|
135
|
+
console.log(chalk.gray(' - Task Sync Workflow'));
|
|
136
|
+
console.log(chalk.gray(' - Context Export Workflow'));
|
|
137
|
+
console.log(chalk.gray(' - Prompt Generation Workflow'));
|
|
138
|
+
console.log(chalk.gray(' - Workflow Checklists'));
|
|
139
|
+
console.log(chalk.gray(' - Time Estimates'));
|
|
140
|
+
console.log(chalk.gray(' - Troubleshooting'));
|
|
141
|
+
} else {
|
|
142
|
+
console.log(chalk.red('❌ Guide not found:'), 'docs/manual-workflows-guide.md');
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Track workflow completion
|
|
151
|
+
*
|
|
152
|
+
* @param {string} workflowId - Workflow ID
|
|
153
|
+
*/
|
|
154
|
+
async function trackCompletion(workflowId) {
|
|
155
|
+
const workflow = WORKFLOWS[workflowId];
|
|
156
|
+
|
|
157
|
+
if (!workflow) {
|
|
158
|
+
console.log(chalk.red('❌ Unknown workflow:'), workflowId);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(chalk.green('✅'), chalk.bold(`${workflow.name} completed!`));
|
|
163
|
+
console.log();
|
|
164
|
+
console.log(chalk.gray('Time estimate:'), workflow.timeEstimate);
|
|
165
|
+
console.log();
|
|
166
|
+
console.log(chalk.blue('💡 Next steps:'));
|
|
167
|
+
|
|
168
|
+
// Suggest next workflow based on current one
|
|
169
|
+
const suggestions = {
|
|
170
|
+
'task-sync': ['context-export', 'prompt-generation'],
|
|
171
|
+
'context-export': ['prompt-generation', 'task-completion'],
|
|
172
|
+
'prompt-generation': ['task-completion'],
|
|
173
|
+
'daily': ['task-sync', 'context-export'],
|
|
174
|
+
'task-completion': ['task-sync'],
|
|
175
|
+
'spec-creation': ['task-sync', 'prompt-generation']
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const nextWorkflows = suggestions[workflowId] || [];
|
|
179
|
+
|
|
180
|
+
if (nextWorkflows.length > 0) {
|
|
181
|
+
nextWorkflows.forEach(nextId => {
|
|
182
|
+
const next = WORKFLOWS[nextId];
|
|
183
|
+
console.log(chalk.cyan(` kse workflows show ${nextId}`), chalk.gray(`- ${next.name}`));
|
|
184
|
+
});
|
|
185
|
+
} else {
|
|
186
|
+
console.log(chalk.gray(' Continue with your work!'));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Main workflows command
|
|
192
|
+
*
|
|
193
|
+
* @param {string} action - Action to perform (list, show, guide, complete)
|
|
194
|
+
* @param {string} workflowId - Workflow ID (for show/complete actions)
|
|
195
|
+
*/
|
|
196
|
+
async function workflowsCommand(action = 'list', workflowId = null) {
|
|
197
|
+
try {
|
|
198
|
+
switch (action) {
|
|
199
|
+
case 'list':
|
|
200
|
+
await listWorkflows();
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
case 'show':
|
|
204
|
+
if (!workflowId) {
|
|
205
|
+
console.log(chalk.red('❌ Workflow ID required'));
|
|
206
|
+
console.log(chalk.gray('Usage:'), chalk.cyan('kse workflows show <workflow-id>'));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
await showWorkflow(workflowId);
|
|
210
|
+
break;
|
|
211
|
+
|
|
212
|
+
case 'guide':
|
|
213
|
+
await openGuide();
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case 'complete':
|
|
217
|
+
if (!workflowId) {
|
|
218
|
+
console.log(chalk.red('❌ Workflow ID required'));
|
|
219
|
+
console.log(chalk.gray('Usage:'), chalk.cyan('kse workflows complete <workflow-id>'));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
await trackCompletion(workflowId);
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
default:
|
|
226
|
+
console.log(chalk.red('❌ Unknown action:'), action);
|
|
227
|
+
console.log();
|
|
228
|
+
console.log(chalk.gray('Available actions:'));
|
|
229
|
+
console.log(chalk.gray(' - list: List all workflows'));
|
|
230
|
+
console.log(chalk.gray(' - show <id>: Show workflow details'));
|
|
231
|
+
console.log(chalk.gray(' - guide: Open full guide'));
|
|
232
|
+
console.log(chalk.gray(' - complete <id>: Mark workflow as complete'));
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = workflowsCommand;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Command Group
|
|
3
|
+
*
|
|
4
|
+
* Manages personal workspaces for multi-user collaboration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const chalk = require('chalk');
|
|
8
|
+
const WorkspaceManager = require('../workspace/workspace-manager');
|
|
9
|
+
const WorkspaceSync = require('../workspace/workspace-sync');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Initialize personal workspace
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} options - Command options
|
|
15
|
+
* @param {string} options.user - Override username
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
async function initWorkspace(options = {}) {
|
|
19
|
+
const projectPath = process.cwd();
|
|
20
|
+
const workspaceManager = new WorkspaceManager();
|
|
21
|
+
|
|
22
|
+
console.log(chalk.red('🔥') + ' Initializing Personal Workspace');
|
|
23
|
+
console.log();
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const username = options.user || await workspaceManager.detectUsername();
|
|
27
|
+
|
|
28
|
+
if (!username) {
|
|
29
|
+
console.log(chalk.red('❌ Could not detect username'));
|
|
30
|
+
console.log();
|
|
31
|
+
console.log('Please configure git:');
|
|
32
|
+
console.log(chalk.cyan(' git config --global user.name "Your Name"'));
|
|
33
|
+
console.log();
|
|
34
|
+
console.log('Or specify username manually:');
|
|
35
|
+
console.log(chalk.cyan(' kse workspace init --user=yourname'));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(`User: ${chalk.cyan(username)}`);
|
|
40
|
+
console.log();
|
|
41
|
+
|
|
42
|
+
const result = await workspaceManager.initWorkspace(projectPath, username);
|
|
43
|
+
|
|
44
|
+
if (result.success) {
|
|
45
|
+
console.log(chalk.green('✅ Workspace initialized successfully'));
|
|
46
|
+
console.log();
|
|
47
|
+
console.log(`Workspace path: ${chalk.gray(result.workspacePath)}`);
|
|
48
|
+
console.log();
|
|
49
|
+
console.log('Files created:');
|
|
50
|
+
console.log(` ${chalk.gray('CURRENT_CONTEXT.md')} - Personal context`);
|
|
51
|
+
console.log(` ${chalk.gray('task-state.json')} - Task tracking`);
|
|
52
|
+
console.log();
|
|
53
|
+
console.log('Next steps:');
|
|
54
|
+
console.log(` ${chalk.cyan('kse task claim <spec-name> <task-id>')} - Claim a task`);
|
|
55
|
+
console.log(` ${chalk.cyan('kse workspace sync')} - Sync with team`);
|
|
56
|
+
} else {
|
|
57
|
+
console.log(chalk.red('❌ Failed to initialize workspace'));
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(`Error: ${result.error}`);
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Synchronize workspace with shared state
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} options - Command options
|
|
70
|
+
* @param {string} options.user - Override username
|
|
71
|
+
* @param {string} options.strategy - Conflict resolution strategy
|
|
72
|
+
* @returns {Promise<void>}
|
|
73
|
+
*/
|
|
74
|
+
async function syncWorkspace(options = {}) {
|
|
75
|
+
const projectPath = process.cwd();
|
|
76
|
+
const workspaceManager = new WorkspaceManager();
|
|
77
|
+
const workspaceSync = new WorkspaceSync();
|
|
78
|
+
|
|
79
|
+
console.log(chalk.red('🔥') + ' Synchronizing Workspace');
|
|
80
|
+
console.log();
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const username = options.user || await workspaceManager.detectUsername();
|
|
84
|
+
|
|
85
|
+
if (!username) {
|
|
86
|
+
console.log(chalk.red('❌ Could not detect username'));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(`User: ${chalk.cyan(username)}`);
|
|
91
|
+
console.log();
|
|
92
|
+
|
|
93
|
+
// Check if workspace exists
|
|
94
|
+
const workspacePath = await workspaceManager.getWorkspacePath(projectPath, username);
|
|
95
|
+
const workspaceExists = await workspaceManager.workspaceExists(projectPath, username);
|
|
96
|
+
|
|
97
|
+
if (!workspaceExists) {
|
|
98
|
+
console.log(chalk.yellow('⚠️ Workspace not initialized'));
|
|
99
|
+
console.log();
|
|
100
|
+
console.log('Run ' + chalk.cyan('kse workspace init') + ' first');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log('Syncing workspace...');
|
|
105
|
+
console.log();
|
|
106
|
+
|
|
107
|
+
const result = await workspaceSync.syncWorkspace(
|
|
108
|
+
projectPath,
|
|
109
|
+
username,
|
|
110
|
+
{ strategy: options.strategy }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (result.success) {
|
|
114
|
+
console.log(chalk.green('✅ Workspace synchronized'));
|
|
115
|
+
console.log();
|
|
116
|
+
|
|
117
|
+
if (result.conflicts && result.conflicts.length > 0) {
|
|
118
|
+
console.log(chalk.yellow(`⚠️ ${result.conflicts.length} conflict(s) resolved`));
|
|
119
|
+
console.log();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (result.changes && result.changes.length > 0) {
|
|
123
|
+
console.log('Changes:');
|
|
124
|
+
for (const change of result.changes) {
|
|
125
|
+
console.log(` ${chalk.gray('•')} ${change}`);
|
|
126
|
+
}
|
|
127
|
+
console.log();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log(`Sync log: ${chalk.gray(result.logPath)}`);
|
|
131
|
+
} else {
|
|
132
|
+
console.log(chalk.red('❌ Sync failed'));
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(`Error: ${result.error}`);
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* List all workspaces
|
|
143
|
+
*
|
|
144
|
+
* @param {Object} options - Command options
|
|
145
|
+
* @returns {Promise<void>}
|
|
146
|
+
*/
|
|
147
|
+
async function listWorkspaces(options = {}) {
|
|
148
|
+
const projectPath = process.cwd();
|
|
149
|
+
const workspaceManager = new WorkspaceManager();
|
|
150
|
+
|
|
151
|
+
console.log(chalk.red('🔥') + ' Workspaces');
|
|
152
|
+
console.log();
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const isMultiUser = await workspaceManager.isMultiUserMode(projectPath);
|
|
156
|
+
|
|
157
|
+
if (!isMultiUser) {
|
|
158
|
+
console.log(chalk.gray('No workspaces found'));
|
|
159
|
+
console.log();
|
|
160
|
+
console.log('This project is in single-user mode.');
|
|
161
|
+
console.log('Run ' + chalk.cyan('kse workspace init') + ' to enable multi-user mode.');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const workspaces = await workspaceManager.listWorkspaces(projectPath);
|
|
166
|
+
|
|
167
|
+
if (workspaces.length === 0) {
|
|
168
|
+
console.log(chalk.gray('No workspaces found'));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
console.log(`Found ${chalk.cyan(workspaces.length)} workspace(s):`);
|
|
173
|
+
console.log();
|
|
174
|
+
|
|
175
|
+
for (const username of workspaces) {
|
|
176
|
+
const workspacePath = await workspaceManager.getWorkspacePath(projectPath, username);
|
|
177
|
+
console.log(` ${chalk.cyan('•')} ${username}`);
|
|
178
|
+
console.log(` ${chalk.gray(workspacePath)}`);
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = {
|
|
186
|
+
initWorkspace,
|
|
187
|
+
syncWorkspace,
|
|
188
|
+
listWorkspaces
|
|
189
|
+
};
|