musubi-sdd 0.5.0 → 0.6.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/README.md CHANGED
@@ -22,7 +22,8 @@ MUSUBI is a comprehensive SDD (Specification Driven Development) framework that
22
22
  - 🧭 **Auto-Updating Project Memory** - Steering system maintains architecture, tech stack, and product context
23
23
  - šŸš€ **Automatic Onboarding** - `musubi-onboard` analyzes existing projects and generates steering docs (2-5 minutes)
24
24
  - šŸ”„ **Auto-Sync** - `musubi-sync` detects codebase changes and keeps steering docs current
25
- - šŸ” **Intelligent Code Analysis** - `musubi-analyze` provides quality metrics, complexity analysis, and technical debt detection (v0.5.0)
25
+ - šŸ” **Intelligent Code Analysis** - `musubi-analyze` provides quality metrics, complexity analysis, and technical debt detection
26
+ - šŸ¤ **Team Collaboration** - `musubi-share` enables memory sharing, import/export, and multi-platform sync (v0.6.0)
26
27
  - āœ… **Complete Traceability** - Requirements → Design → Code → Tests mapping
27
28
  - 🌐 **Bilingual Documentation** - All agent-generated documents created in both English and Japanese
28
29
 
@@ -94,6 +95,12 @@ musubi-analyze --type=quality # Quality metrics only
94
95
  musubi-analyze --type=dependencies # Dependencies only
95
96
  musubi-analyze --type=security # Security audit
96
97
  musubi-analyze --output=report.md # Save report
98
+
99
+ # Share project memories with team (v0.6.0)
100
+ musubi-share export # Export memories to JSON
101
+ musubi-share import memories.json # Import from teammate
102
+ musubi-share sync # Sync across AI platforms
103
+ musubi-share status # Show sharing status
97
104
  ```
98
105
 
99
106
  ### Project Types
@@ -285,6 +292,15 @@ musubi-analyze --type=dependencies # Dependency analysis
285
292
  musubi-analyze --type=security # Security vulnerabilities
286
293
  musubi-analyze --output=report.md # Save report to file
287
294
  musubi-analyze --json # JSON output
295
+
296
+ # Share project memories with team (v0.6.0+)
297
+ musubi-share export # Export memories to JSON/YAML
298
+ musubi-share export --output=memories.yaml # YAML format
299
+ musubi-share import memories.json # Import and merge
300
+ musubi-share import memories.json --strategy=theirs # Auto-accept
301
+ musubi-share sync # Sync across AI platforms
302
+ musubi-share sync --platform=cursor # Sync specific platform
303
+ musubi-share status # Show sharing status
288
304
  ```
289
305
 
290
306
  #### musubi-onboard
@@ -365,7 +381,49 @@ musubi-sync --dry-run
365
381
  musubi-sync --auto-approve
366
382
  ```
367
383
 
368
- #### musubi status
384
+ #### musubi-share
385
+
386
+ Share and merge project memories across team members and AI platforms (v0.6.0+):
387
+
388
+ ```
389
+ šŸ“¤ MUSUBI Memory Export
390
+
391
+ Export Summary:
392
+ File: team-memories.json
393
+ Format: json
394
+ Size: 1098.28 KB
395
+ Memories: 7 files
396
+ Agents: 1 platforms
397
+ ```
398
+
399
+ **Features**:
400
+ - **Export**: Share memories as JSON/YAML
401
+ - **Import**: Merge memories from teammates
402
+ - **Sync**: Synchronize across AI platforms
403
+ - **Conflict Resolution**: Interactive, auto-accept, keep-local, or merge with markers
404
+ - **Status**: Show installed platforms and memory counts
405
+
406
+ **Usage**:
407
+ ```bash
408
+ # Export memories
409
+ musubi-share export
410
+ musubi-share export --output=memories.yaml
411
+
412
+ # Import and merge
413
+ musubi-share import colleague-memories.json
414
+ musubi-share import memories.json --strategy=theirs # Auto-accept
415
+ musubi-share import memories.json --strategy=ours # Keep local
416
+ musubi-share import memories.json --strategy=merge # With markers
417
+
418
+ # Platform sync
419
+ musubi-share sync
420
+ musubi-share sync --platform=cursor
421
+
422
+ # Status check
423
+ musubi-share status
424
+ ```
425
+
426
+ #### musubi status
369
427
 
370
428
  Shows the current state of your MUSUBI project:
371
429
 
@@ -106,7 +106,7 @@ function calculateComplexity(code) {
106
106
  /\bwhile\b/g,
107
107
  /\bcase\b/g,
108
108
  /\bcatch\b/g,
109
- /\&\&/g,
109
+ /&&/g,
110
110
  /\|\|/g,
111
111
  /\?/g // Ternary operator
112
112
  ];
@@ -347,20 +347,20 @@ async function generateReport(analysisData) {
347
347
  const timestamp = new Date().toISOString().split('T')[0];
348
348
  const reportPath = options.output || `steering/memories/code_quality_report_${timestamp}.md`;
349
349
 
350
- let report = `# Code Quality Analysis Report\n\n`;
350
+ let report = '# Code Quality Analysis Report\n\n';
351
351
  report += `**Generated**: ${new Date().toISOString()}\n`;
352
- report += `**Version**: MUSUBI v0.5.0\n\n`;
352
+ report += '**Version**: MUSUBI v0.5.0\n\n';
353
353
 
354
354
  if (analysisData.quality && analysisData.quality.summary) {
355
355
  const { summary } = analysisData.quality;
356
- report += `## Quality Metrics\n\n`;
356
+ report += '## Quality Metrics\n\n';
357
357
  report += `- Total Files: ${summary.totalFiles}\n`;
358
358
  report += `- Average Complexity: ${summary.averageComplexity}\n`;
359
359
  report += `- Average Maintainability: ${summary.averageMaintainability}\n`;
360
360
  report += `- High Complexity Files: ${summary.highComplexityFiles}\n`;
361
361
  report += `- Low Maintainability Files: ${summary.lowMaintainabilityFiles}\n\n`;
362
362
 
363
- report += `### Recommendations\n\n`;
363
+ report += '### Recommendations\n\n';
364
364
  if (summary.highComplexityFiles > 0) {
365
365
  report += `- āš ļø ${summary.highComplexityFiles} files have high complexity. Consider refactoring.\n`;
366
366
  }
@@ -368,20 +368,20 @@ async function generateReport(analysisData) {
368
368
  report += `- āš ļø ${summary.lowMaintainabilityFiles} files have low maintainability. Add comments and simplify logic.\n`;
369
369
  }
370
370
  if (summary.highComplexityFiles === 0 && summary.lowMaintainabilityFiles === 0) {
371
- report += `- āœ… Code quality is excellent!\n`;
371
+ report += '- āœ… Code quality is excellent!\n';
372
372
  }
373
- report += `\n`;
373
+ report += '\n';
374
374
  }
375
375
 
376
376
  if (analysisData.dependencies) {
377
- report += `## Dependencies\n\n`;
377
+ report += '## Dependencies\n\n';
378
378
  report += `- Production: ${analysisData.dependencies.dependencies.length}\n`;
379
379
  report += `- Development: ${analysisData.dependencies.devDependencies.length}\n\n`;
380
380
  }
381
381
 
382
382
  if (analysisData.security) {
383
383
  const total = Object.values(analysisData.security).reduce((sum, count) => sum + count, 0);
384
- report += `## Security\n\n`;
384
+ report += '## Security\n\n';
385
385
  report += `- Total Vulnerabilities: ${total}\n`;
386
386
  report += `- Critical: ${analysisData.security.critical || 0}\n`;
387
387
  report += `- High: ${analysisData.security.high || 0}\n`;
@@ -389,8 +389,8 @@ async function generateReport(analysisData) {
389
389
  report += `- Low: ${analysisData.security.low || 0}\n\n`;
390
390
  }
391
391
 
392
- report += `---\n\n`;
393
- report += `*Generated by MUSUBI v0.5.0*\n`;
392
+ report += '---\n\n';
393
+ report += '*Generated by MUSUBI v0.5.0*\n';
394
394
 
395
395
  fs.ensureDirSync(path.dirname(reportPath));
396
396
  fs.writeFileSync(reportPath, report);
@@ -0,0 +1,437 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MUSUBI Share Script
5
+ *
6
+ * Share and merge project memories across team members and AI platforms:
7
+ * - Export project memories to shareable format
8
+ * - Import memories from other team members
9
+ * - Merge memories with conflict resolution
10
+ * - Sync across different AI platforms (Claude Code, Copilot, Cursor, etc.)
11
+ *
12
+ * Usage:
13
+ * musubi-share export [--output <file>]
14
+ * musubi-share import <file> [--strategy <strategy>]
15
+ * musubi-share sync [--platform <platform>]
16
+ * musubi-share status
17
+ */
18
+
19
+ const fs = require('fs-extra');
20
+ const path = require('path');
21
+ const chalk = require('chalk');
22
+ const { glob } = require('glob');
23
+ const yaml = require('js-yaml');
24
+ const { program } = require('commander');
25
+
26
+ // Configuration
27
+ const CONFIG = {
28
+ steeringDir: 'steering',
29
+ memoriesDir: 'steering/memories',
30
+ agentDirs: ['.claude', '.github/copilot', '.cursor', '.windsurf'],
31
+ exportFormats: ['json', 'yaml'],
32
+ mergeStrategies: ['ours', 'theirs', 'merge', 'interactive'],
33
+ };
34
+
35
+ /**
36
+ * Export project memories to shareable format
37
+ */
38
+ async function exportMemories(options = {}) {
39
+ console.log(chalk.blue.bold('\nšŸ“¤ MUSUBI Memory Export\n'));
40
+
41
+ const outputFile = options.output || `musubi-memories-${Date.now()}.json`;
42
+ const format = path.extname(outputFile).slice(1) || 'json';
43
+
44
+ if (!CONFIG.exportFormats.includes(format)) {
45
+ console.error(chalk.red(`āŒ Unsupported format: ${format}`));
46
+ console.log(chalk.gray(`Supported formats: ${CONFIG.exportFormats.join(', ')}`));
47
+ process.exit(1);
48
+ }
49
+
50
+ console.log(chalk.cyan('šŸ“‹ Step 1: Collecting project memories...\n'));
51
+
52
+ const memories = {
53
+ metadata: {
54
+ exportedAt: new Date().toISOString(),
55
+ exportedBy: process.env.USER || 'unknown',
56
+ musubiVersion: require('../package.json').version,
57
+ },
58
+ project: {},
59
+ memories: {},
60
+ agents: {},
61
+ };
62
+
63
+ // Load project configuration
64
+ if (await fs.pathExists('steering/project.yml')) {
65
+ const projectYml = await fs.readFile('steering/project.yml', 'utf8');
66
+ memories.project = yaml.load(projectYml);
67
+ console.log(chalk.gray(' āœ“ Loaded project.yml'));
68
+ }
69
+
70
+ // Load steering documents
71
+ const steeringFiles = ['product.md', 'structure.md', 'tech.md'];
72
+ for (const file of steeringFiles) {
73
+ const filePath = `steering/${file}`;
74
+ if (await fs.pathExists(filePath)) {
75
+ memories.project[file] = await fs.readFile(filePath, 'utf8');
76
+ console.log(chalk.gray(` āœ“ Loaded ${file}`));
77
+ }
78
+ }
79
+
80
+ // Load memory files
81
+ if (await fs.pathExists(CONFIG.memoriesDir)) {
82
+ const memoryFiles = await glob('*.md', { cwd: CONFIG.memoriesDir });
83
+ const memoryArray = Array.isArray(memoryFiles) ? memoryFiles : [];
84
+ for (const file of memoryArray) {
85
+ const filePath = path.join(CONFIG.memoriesDir, file);
86
+ memories.memories[file] = await fs.readFile(filePath, 'utf8');
87
+ console.log(chalk.gray(` āœ“ Loaded memory: ${file}`));
88
+ }
89
+ }
90
+
91
+ // Load agent configurations
92
+ for (const agentDir of CONFIG.agentDirs) {
93
+ if (await fs.pathExists(agentDir)) {
94
+ const agentName = path.basename(agentDir);
95
+ memories.agents[agentName] = {};
96
+
97
+ const agentFiles = await glob('**/*.md', { cwd: agentDir });
98
+ const agentArray = Array.isArray(agentFiles) ? agentFiles : [];
99
+ for (const file of agentArray) {
100
+ const filePath = path.join(agentDir, file);
101
+ memories.agents[agentName][file] = await fs.readFile(filePath, 'utf8');
102
+ }
103
+ console.log(chalk.gray(` āœ“ Loaded agent config: ${agentName} (${agentArray.length} files)`));
104
+ }
105
+ }
106
+
107
+ console.log(chalk.cyan('\nšŸ“‹ Step 2: Writing export file...\n'));
108
+
109
+ // Write export file
110
+ let exportContent;
111
+ if (format === 'json') {
112
+ exportContent = JSON.stringify(memories, null, 2);
113
+ } else if (format === 'yaml') {
114
+ exportContent = yaml.dump(memories, { indent: 2, lineWidth: 100 });
115
+ }
116
+
117
+ await fs.writeFile(outputFile, exportContent);
118
+
119
+ const stats = await fs.stat(outputFile);
120
+ const fileSizeKB = (stats.size / 1024).toFixed(2);
121
+
122
+ console.log(chalk.green.bold('āœ… Export complete!\n'));
123
+ console.log(chalk.white('Export Summary:'));
124
+ console.log(chalk.gray(` File: ${outputFile}`));
125
+ console.log(chalk.gray(` Format: ${format}`));
126
+ console.log(chalk.gray(` Size: ${fileSizeKB} KB`));
127
+ console.log(chalk.gray(` Memories: ${Object.keys(memories.memories).length} files`));
128
+ console.log(chalk.gray(` Agents: ${Object.keys(memories.agents).length} platforms`));
129
+ console.log(chalk.white('\nShare this file with your team:\n'));
130
+ console.log(chalk.cyan(` scp ${outputFile} teammate@host:/path/`));
131
+ console.log(chalk.cyan(' # or commit to shared repository\n'));
132
+ }
133
+
134
+ /**
135
+ * Import and merge memories from file
136
+ */
137
+ async function importMemories(importFile, options = {}) {
138
+ console.log(chalk.blue.bold('\nšŸ“„ MUSUBI Memory Import\n'));
139
+
140
+ if (!await fs.pathExists(importFile)) {
141
+ console.error(chalk.red(`āŒ Import file not found: ${importFile}`));
142
+ process.exit(1);
143
+ }
144
+
145
+ const strategy = options.strategy || 'interactive';
146
+ if (!CONFIG.mergeStrategies.includes(strategy)) {
147
+ console.error(chalk.red(`āŒ Invalid merge strategy: ${strategy}`));
148
+ console.log(chalk.gray(`Supported strategies: ${CONFIG.mergeStrategies.join(', ')}`));
149
+ process.exit(1);
150
+ }
151
+
152
+ console.log(chalk.cyan('šŸ“‹ Step 1: Loading import file...\n'));
153
+
154
+ const importContent = await fs.readFile(importFile, 'utf8');
155
+ const format = path.extname(importFile).slice(1);
156
+
157
+ let importedMemories;
158
+ try {
159
+ if (format === 'json') {
160
+ importedMemories = JSON.parse(importContent);
161
+ } else if (format === 'yaml' || format === 'yml') {
162
+ importedMemories = yaml.load(importContent);
163
+ } else {
164
+ throw new Error(`Unsupported format: ${format}`);
165
+ }
166
+ } catch (error) {
167
+ console.error(chalk.red(`āŒ Failed to parse import file: ${error.message}`));
168
+ process.exit(1);
169
+ }
170
+
171
+ console.log(chalk.gray(` Exported by: ${importedMemories.metadata.exportedBy}`));
172
+ console.log(chalk.gray(` Exported at: ${importedMemories.metadata.exportedAt}`));
173
+ console.log(chalk.gray(` MUSUBI version: ${importedMemories.metadata.musubiVersion}`));
174
+
175
+ console.log(chalk.cyan('\nšŸ“‹ Step 2: Detecting conflicts...\n'));
176
+
177
+ const conflicts = [];
178
+ const currentVersion = require('../package.json').version;
179
+
180
+ // Check version compatibility
181
+ if (importedMemories.metadata.musubiVersion !== currentVersion) {
182
+ console.log(chalk.yellow(` āš ļø Version mismatch: ${importedMemories.metadata.musubiVersion} → ${currentVersion}`));
183
+ }
184
+
185
+ // Detect memory conflicts
186
+ for (const [file, content] of Object.entries(importedMemories.memories)) {
187
+ const localPath = path.join(CONFIG.memoriesDir, file);
188
+ if (await fs.pathExists(localPath)) {
189
+ const localContent = await fs.readFile(localPath, 'utf8');
190
+ if (localContent !== content) {
191
+ conflicts.push({
192
+ type: 'memory',
193
+ file,
194
+ localPath,
195
+ importedContent: content,
196
+ localContent,
197
+ });
198
+ console.log(chalk.yellow(` āš ļø Conflict: ${file}`));
199
+ }
200
+ }
201
+ }
202
+
203
+ if (conflicts.length === 0) {
204
+ console.log(chalk.green(' āœ“ No conflicts detected'));
205
+ } else {
206
+ console.log(chalk.yellow(`\n Found ${conflicts.length} conflict(s)`));
207
+ }
208
+
209
+ console.log(chalk.cyan('\nšŸ“‹ Step 3: Merging memories...\n'));
210
+
211
+ let mergeCount = 0;
212
+
213
+ // Apply merge strategy
214
+ if (strategy === 'ours') {
215
+ console.log(chalk.gray(' Strategy: Keep local changes (ours)'));
216
+ console.log(chalk.gray(' Skipping conflicted files...\n'));
217
+ } else if (strategy === 'theirs') {
218
+ console.log(chalk.gray(' Strategy: Accept imported changes (theirs)'));
219
+
220
+ for (const conflict of conflicts) {
221
+ await fs.writeFile(conflict.localPath, conflict.importedContent);
222
+ console.log(chalk.gray(` āœ“ Updated: ${conflict.file}`));
223
+ mergeCount++;
224
+ }
225
+ } else if (strategy === 'merge') {
226
+ console.log(chalk.gray(' Strategy: Auto-merge with markers'));
227
+
228
+ for (const conflict of conflicts) {
229
+ const mergedContent = `${conflict.localContent}\n\n<<<<<<< LOCAL\n${conflict.localContent}\n=======\n${conflict.importedContent}\n>>>>>>> IMPORTED\n`;
230
+ await fs.writeFile(conflict.localPath, mergedContent);
231
+ console.log(chalk.gray(` āœ“ Merged with markers: ${conflict.file}`));
232
+ mergeCount++;
233
+ }
234
+ } else if (strategy === 'interactive') {
235
+ console.log(chalk.gray(' Strategy: Interactive resolution'));
236
+ console.log(chalk.yellow('\n āš ļø Interactive mode requires manual review'));
237
+ console.log(chalk.gray(' Re-run with --strategy=theirs to auto-accept\n'));
238
+ }
239
+
240
+ // Import new memories (no conflicts)
241
+ for (const [file, content] of Object.entries(importedMemories.memories)) {
242
+ const localPath = path.join(CONFIG.memoriesDir, file);
243
+ if (!await fs.pathExists(localPath)) {
244
+ await fs.ensureDir(CONFIG.memoriesDir);
245
+ await fs.writeFile(localPath, content);
246
+ console.log(chalk.gray(` āœ“ Added new memory: ${file}`));
247
+ mergeCount++;
248
+ }
249
+ }
250
+
251
+ console.log(chalk.green.bold('\nāœ… Import complete!\n'));
252
+ console.log(chalk.white('Import Summary:'));
253
+ console.log(chalk.gray(` Strategy: ${strategy}`));
254
+ console.log(chalk.gray(` Conflicts: ${conflicts.length}`));
255
+ console.log(chalk.gray(` Merged: ${mergeCount} files`));
256
+
257
+ if (conflicts.length > 0 && strategy === 'interactive') {
258
+ console.log(chalk.yellow('\nNext steps:'));
259
+ console.log(chalk.gray(' 1. Review conflicted files manually'));
260
+ console.log(chalk.gray(' 2. Re-run with --strategy=theirs to accept all'));
261
+ console.log(chalk.gray(' 3. Or edit files individually\n'));
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Sync memories across AI platforms
267
+ */
268
+ async function syncPlatforms(options = {}) {
269
+ console.log(chalk.blue.bold('\nšŸ”„ MUSUBI Platform Sync\n'));
270
+
271
+ const targetPlatform = options.platform;
272
+ const availablePlatforms = [
273
+ 'claude-code',
274
+ 'github-copilot',
275
+ 'cursor',
276
+ 'windsurf',
277
+ 'codex',
278
+ 'qwen-code',
279
+ 'gemini-cli'
280
+ ];
281
+
282
+ if (targetPlatform && !availablePlatforms.includes(targetPlatform)) {
283
+ console.error(chalk.red(`āŒ Unknown platform: ${targetPlatform}`));
284
+ console.log(chalk.gray(`Available: ${availablePlatforms.join(', ')}`));
285
+ process.exit(1);
286
+ }
287
+
288
+ console.log(chalk.cyan('šŸ“‹ Step 1: Detecting installed platforms...\n'));
289
+
290
+ const installedPlatforms = [];
291
+ const platformDirs = {
292
+ 'claude-code': '.claude',
293
+ 'github-copilot': '.github/copilot',
294
+ 'cursor': '.cursor',
295
+ 'windsurf': '.windsurf',
296
+ };
297
+
298
+ for (const [platform, dir] of Object.entries(platformDirs)) {
299
+ if (await fs.pathExists(dir)) {
300
+ installedPlatforms.push(platform);
301
+ console.log(chalk.gray(` āœ“ Found: ${platform} (${dir})`));
302
+ }
303
+ }
304
+
305
+ if (installedPlatforms.length === 0) {
306
+ console.log(chalk.yellow(' āš ļø No AI platforms detected'));
307
+ console.log(chalk.gray(' Run musubi-init to set up an AI platform\n'));
308
+ return;
309
+ }
310
+
311
+ console.log(chalk.cyan('\nšŸ“‹ Step 2: Syncing shared memories...\n'));
312
+
313
+ // Sync steering documents to all platforms
314
+ let syncCount = 0;
315
+
316
+ for (const platform of installedPlatforms) {
317
+ if (targetPlatform && platform !== targetPlatform) continue;
318
+
319
+ const platformDir = platformDirs[platform];
320
+
321
+ // Sync AGENTS.md
322
+ const agentsSource = 'AGENTS.md';
323
+ const agentsTarget = path.join(platformDir, 'AGENTS.md');
324
+
325
+ if (await fs.pathExists(agentsSource)) {
326
+ await fs.copy(agentsSource, agentsTarget);
327
+ console.log(chalk.gray(` āœ“ ${platform}: Synced AGENTS.md`));
328
+ syncCount++;
329
+ }
330
+ }
331
+
332
+ console.log(chalk.green.bold('\nāœ… Sync complete!\n'));
333
+ console.log(chalk.white('Sync Summary:'));
334
+ console.log(chalk.gray(` Platforms: ${installedPlatforms.length}`));
335
+ console.log(chalk.gray(` Synced files: ${syncCount}`));
336
+ console.log(chalk.white('\nAll platforms now have consistent memories\n'));
337
+ }
338
+
339
+ /**
340
+ * Show sharing status
341
+ */
342
+ async function showStatus() {
343
+ console.log(chalk.blue.bold('\nšŸ“Š MUSUBI Sharing Status\n'));
344
+
345
+ // Check project setup
346
+ console.log(chalk.white('Project Setup:'));
347
+ const hasProject = await fs.pathExists('steering/project.yml');
348
+ console.log(chalk.gray(` project.yml: ${hasProject ? 'āœ“' : 'āœ—'}`));
349
+
350
+ // Check memories
351
+ if (await fs.pathExists(CONFIG.memoriesDir)) {
352
+ const memoryFiles = await glob('*.md', { cwd: CONFIG.memoriesDir });
353
+ const memoryArray = Array.isArray(memoryFiles) ? memoryFiles : [];
354
+ console.log(chalk.gray(` Memories: ${memoryArray.length} files`));
355
+ } else {
356
+ console.log(chalk.gray(' Memories: 0 files'));
357
+ }
358
+
359
+ // Check platforms
360
+ console.log(chalk.white('\nInstalled Platforms:'));
361
+ const platformDirs = {
362
+ 'Claude Code': '.claude',
363
+ 'GitHub Copilot': '.github/copilot',
364
+ 'Cursor': '.cursor',
365
+ 'Windsurf': '.windsurf',
366
+ 'Codex': '.codex',
367
+ 'Qwen Code': '.qwen',
368
+ };
369
+
370
+ let installedCount = 0;
371
+ for (const [name, dir] of Object.entries(platformDirs)) {
372
+ const exists = await fs.pathExists(dir);
373
+ if (exists) {
374
+ console.log(chalk.green(` āœ“ ${name}`));
375
+ installedCount++;
376
+ } else {
377
+ console.log(chalk.gray(` āœ— ${name}`));
378
+ }
379
+ }
380
+
381
+ console.log(chalk.white(`\nTotal: ${installedCount} platform(s) configured\n`));
382
+
383
+ // Export recommendation
384
+ if (hasProject) {
385
+ console.log(chalk.cyan('Ready to share:'));
386
+ console.log(chalk.gray(' Run: musubi-share export'));
387
+ console.log(chalk.gray(' Then share the generated file with your team\n'));
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Main function
393
+ */
394
+ async function main() {
395
+ program
396
+ .name('musubi-share')
397
+ .description('Share and merge project memories across team members and AI platforms')
398
+ .version(require('../package.json').version);
399
+
400
+ program
401
+ .command('export')
402
+ .description('Export project memories to shareable format')
403
+ .option('-o, --output <file>', 'Output file path', `musubi-memories-${Date.now()}.json`)
404
+ .action(exportMemories);
405
+
406
+ program
407
+ .command('import <file>')
408
+ .description('Import and merge memories from file')
409
+ .option('-s, --strategy <strategy>', 'Merge strategy: ours, theirs, merge, interactive', 'interactive')
410
+ .action(importMemories);
411
+
412
+ program
413
+ .command('sync')
414
+ .description('Sync memories across AI platforms')
415
+ .option('-p, --platform <platform>', 'Target platform to sync')
416
+ .action(syncPlatforms);
417
+
418
+ program
419
+ .command('status')
420
+ .description('Show sharing status')
421
+ .action(showStatus);
422
+
423
+ program.parse();
424
+ }
425
+
426
+ // Run if called directly
427
+ if (require.main === module) {
428
+ main().catch(error => {
429
+ console.error(chalk.red(`\nāŒ Share failed: ${error.message}\n`));
430
+ if (process.env.DEBUG) {
431
+ console.error(error.stack);
432
+ }
433
+ process.exit(1);
434
+ });
435
+ }
436
+
437
+ module.exports = main;
@@ -208,7 +208,8 @@ async function analyzeCodebase() {
208
208
  nodir: true,
209
209
  });
210
210
 
211
- const extensions = new Set(files.map(f => path.extname(f)).filter(Boolean));
211
+ const fileArray = Array.isArray(files) ? files : [];
212
+ const extensions = new Set(fileArray.map(f => path.extname(f)).filter(Boolean));
212
213
  const langMap = {
213
214
  '.js': 'javascript',
214
215
  '.ts': 'typescript',
@@ -229,7 +230,8 @@ async function analyzeCodebase() {
229
230
  const dirs = await glob('*/', {
230
231
  ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**', '.vscode/**'],
231
232
  });
232
- state.directories = dirs.map(d => d.replace(/\/$/, '')).slice(0, 10);
233
+ const dirArray = Array.isArray(dirs) ? dirs : [];
234
+ state.directories = dirArray.map(d => d.replace(/\/$/, '')).slice(0, 10);
233
235
 
234
236
  return state;
235
237
  }
@@ -398,7 +400,7 @@ async function applyChanges(changes, currentConfig, actualState) {
398
400
 
399
401
  // Record change in memories
400
402
  await recordChangeInMemory(changes);
401
- console.log(chalk.gray(` Updated steering/memories/architecture_decisions.md`));
403
+ console.log(chalk.gray(' Updated steering/memories/architecture_decisions.md'));
402
404
  }
403
405
 
404
406
  /**
@@ -436,7 +438,7 @@ async function updateProjectYml(changes, actualState) {
436
438
  /**
437
439
  * Update tech.md with new frameworks
438
440
  */
439
- async function updateTechMd(changes, actualState) {
441
+ async function updateTechMd(changes, _actualState) {
440
442
  const newFrameworks = changes
441
443
  .filter(c => c.type === 'new_frameworks')
442
444
  .flatMap(c => c.added);
@@ -482,7 +484,7 @@ async function updateTechMd(changes, actualState) {
482
484
  /**
483
485
  * Update structure.md with new directories
484
486
  */
485
- async function updateStructureMd(changes, actualState) {
487
+ async function updateStructureMd(changes, _actualState) {
486
488
  const newDirs = changes
487
489
  .filter(c => c.type === 'new_directories')
488
490
  .flatMap(c => c.added);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubi-sdd",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Ultimate Specification Driven Development Tool with 25 Agents for 7 AI Coding Platforms (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "musubi-init": "bin/musubi-init.js",
10
10
  "musubi-onboard": "bin/musubi-onboard.js",
11
11
  "musubi-sync": "bin/musubi-sync.js",
12
- "musubi-analyze": "bin/musubi-analyze.js"
12
+ "musubi-analyze": "bin/musubi-analyze.js",
13
+ "musubi-share": "bin/musubi-share.js"
13
14
  },
14
15
  "scripts": {
15
16
  "test": "jest",
@@ -44,7 +45,7 @@
44
45
  "chalk": "^4.1.2",
45
46
  "commander": "^11.0.0",
46
47
  "fs-extra": "^11.0.0",
47
- "glob": "^10.0.0",
48
+ "glob": "^10.5.0",
48
49
  "inquirer": "^9.0.0",
49
50
  "js-yaml": "^4.1.0"
50
51
  },