musubi-sdd 0.5.1 ā 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 +60 -2
- package/bin/musubi-analyze.js +11 -11
- package/bin/musubi-share.js +437 -0
- package/bin/musubi-sync.js +3 -3
- package/package.json +4 -3
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
|
|
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
|
|
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
|
|
package/bin/musubi-analyze.js
CHANGED
|
@@ -106,7 +106,7 @@ function calculateComplexity(code) {
|
|
|
106
106
|
/\bwhile\b/g,
|
|
107
107
|
/\bcase\b/g,
|
|
108
108
|
/\bcatch\b/g,
|
|
109
|
-
|
|
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 =
|
|
350
|
+
let report = '# Code Quality Analysis Report\n\n';
|
|
351
351
|
report += `**Generated**: ${new Date().toISOString()}\n`;
|
|
352
|
-
report +=
|
|
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 +=
|
|
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 +=
|
|
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 +=
|
|
371
|
+
report += '- ā
Code quality is excellent!\n';
|
|
372
372
|
}
|
|
373
|
-
report +=
|
|
373
|
+
report += '\n';
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
if (analysisData.dependencies) {
|
|
377
|
-
report +=
|
|
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 +=
|
|
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 +=
|
|
393
|
-
report +=
|
|
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;
|
package/bin/musubi-sync.js
CHANGED
|
@@ -400,7 +400,7 @@ async function applyChanges(changes, currentConfig, actualState) {
|
|
|
400
400
|
|
|
401
401
|
// Record change in memories
|
|
402
402
|
await recordChangeInMemory(changes);
|
|
403
|
-
console.log(chalk.gray(
|
|
403
|
+
console.log(chalk.gray(' Updated steering/memories/architecture_decisions.md'));
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
/**
|
|
@@ -438,7 +438,7 @@ async function updateProjectYml(changes, actualState) {
|
|
|
438
438
|
/**
|
|
439
439
|
* Update tech.md with new frameworks
|
|
440
440
|
*/
|
|
441
|
-
async function updateTechMd(changes,
|
|
441
|
+
async function updateTechMd(changes, _actualState) {
|
|
442
442
|
const newFrameworks = changes
|
|
443
443
|
.filter(c => c.type === 'new_frameworks')
|
|
444
444
|
.flatMap(c => c.added);
|
|
@@ -484,7 +484,7 @@ async function updateTechMd(changes, actualState) {
|
|
|
484
484
|
/**
|
|
485
485
|
* Update structure.md with new directories
|
|
486
486
|
*/
|
|
487
|
-
async function updateStructureMd(changes,
|
|
487
|
+
async function updateStructureMd(changes, _actualState) {
|
|
488
488
|
const newDirs = changes
|
|
489
489
|
.filter(c => c.type === 'new_directories')
|
|
490
490
|
.flatMap(c => c.added);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musubi-sdd",
|
|
3
|
-
"version": "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.
|
|
48
|
+
"glob": "^10.5.0",
|
|
48
49
|
"inquirer": "^9.0.0",
|
|
49
50
|
"js-yaml": "^4.1.0"
|
|
50
51
|
},
|