Add migration scripts for archive database migration
Three migration scripts for complete file-to-database migration: - migrate-all-docs-batch.mjs: Main migration (1,172 files) - migrate-missed-docs.mjs: Supplementary for hidden dirs (34 files) - migrate-external-archive.mjs: External archive cleanup (5 files) Total migrated: 1,211 files (~15MB) to project_archives table All with semantic embeddings for vector search Related: CF-267, CF-268 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
480
migrate-all-docs-batch.mjs
Executable file
480
migrate-all-docs-batch.mjs
Executable file
@@ -0,0 +1,480 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Comprehensive batch migration of all documentation to task-mcp database
|
||||||
|
*
|
||||||
|
* Migrates ~1,293 .md files from Development directory with intelligent archive type detection.
|
||||||
|
*
|
||||||
|
* Archive types:
|
||||||
|
* - session: Session plans, notes, CLAUDE_HISTORY.md
|
||||||
|
* - investigation: Investigation files
|
||||||
|
* - completed: planning.md, completed work
|
||||||
|
* - research: Documentation, guides, architecture docs (default)
|
||||||
|
*
|
||||||
|
* Excludes:
|
||||||
|
* - README.md, CHANGELOG.md, LICENSE.md, CONTRIBUTING.md
|
||||||
|
* - Templates (*-template.md, /templates/)
|
||||||
|
* - Active tracking (tasks.md)
|
||||||
|
* - Vendor directories (go/pkg/mod, Tools/Github-Ranking)
|
||||||
|
* - Build artifacts (node_modules, .git, build, dist)
|
||||||
|
* - Already migrated (.migrated-to-mcp/)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, readdirSync, renameSync, mkdirSync, existsSync, statSync } from 'fs';
|
||||||
|
import { join, basename, dirname, relative } from 'path';
|
||||||
|
import { homedir } from 'os';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import { archiveAdd } from './dist/tools/archives.js';
|
||||||
|
|
||||||
|
// Load environment
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const DEV_DIR = join(homedir(), 'Development');
|
||||||
|
const DRY_RUN = process.argv.includes('--dry-run');
|
||||||
|
|
||||||
|
// Standard files to keep
|
||||||
|
const STANDARD_FILES = [
|
||||||
|
'README.md',
|
||||||
|
'CHANGELOG.md',
|
||||||
|
'LICENSE.md',
|
||||||
|
'CONTRIBUTING.md',
|
||||||
|
'CODE_OF_CONDUCT.md'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Files/patterns to exclude
|
||||||
|
const EXCLUDE_PATTERNS = [
|
||||||
|
// Build and dependencies
|
||||||
|
'/node_modules/',
|
||||||
|
'/.git/',
|
||||||
|
'/build/',
|
||||||
|
'/dist/',
|
||||||
|
'/vendor/',
|
||||||
|
|
||||||
|
// Vendor/external tools
|
||||||
|
'/go/pkg/mod/',
|
||||||
|
'/Tools/Github-Ranking/',
|
||||||
|
'/Tools/awesome-',
|
||||||
|
|
||||||
|
// Already migrated
|
||||||
|
'/.migrated-to-mcp/',
|
||||||
|
'/.framework-backup/',
|
||||||
|
'/.claude-backup/',
|
||||||
|
|
||||||
|
// Archived projects
|
||||||
|
'/Archived/',
|
||||||
|
|
||||||
|
// System directories
|
||||||
|
'/.DS_Store',
|
||||||
|
'/.idea/',
|
||||||
|
'/.vscode/'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Active files to keep (not migrate)
|
||||||
|
const ACTIVE_FILES = [
|
||||||
|
'tasks.md',
|
||||||
|
'FEATURES.md'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if file should be excluded
|
||||||
|
*/
|
||||||
|
function shouldExclude(filePath, filename) {
|
||||||
|
// Exclude standard files
|
||||||
|
if (STANDARD_FILES.includes(filename)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude active tracking files
|
||||||
|
if (ACTIVE_FILES.includes(filename)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude templates
|
||||||
|
if (filename.endsWith('-template.md') || filename.includes('template') || filePath.includes('/templates/')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude by pattern
|
||||||
|
if (EXCLUDE_PATTERNS.some(pattern => filePath.includes(pattern))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine archive type based on filename and path
|
||||||
|
*/
|
||||||
|
function getArchiveType(filePath, filename) {
|
||||||
|
const lowerPath = filePath.toLowerCase();
|
||||||
|
const lowerFile = filename.toLowerCase();
|
||||||
|
|
||||||
|
// Session files
|
||||||
|
if (filePath.includes('.claude-session/') && filename === 'plan.md') {
|
||||||
|
return 'session';
|
||||||
|
}
|
||||||
|
if (filePath.includes('.claude-session/') && filename === 'notes.md') {
|
||||||
|
return 'session';
|
||||||
|
}
|
||||||
|
if (filename === 'CLAUDE_HISTORY.md') {
|
||||||
|
return 'session';
|
||||||
|
}
|
||||||
|
if (filename === 'SESSION_COMPLETE.md') {
|
||||||
|
return 'session';
|
||||||
|
}
|
||||||
|
if (lowerFile.includes('session') && lowerFile.includes('complete')) {
|
||||||
|
return 'session';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Investigation files
|
||||||
|
if (lowerFile.startsWith('investigation-')) {
|
||||||
|
return 'investigation';
|
||||||
|
}
|
||||||
|
if (lowerFile.includes('investigation')) {
|
||||||
|
return 'investigation';
|
||||||
|
}
|
||||||
|
if (lowerPath.includes('/investigations/')) {
|
||||||
|
return 'investigation';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completed work
|
||||||
|
if (filename === 'planning.md') {
|
||||||
|
return 'completed';
|
||||||
|
}
|
||||||
|
if (lowerFile.includes('complete') && !lowerFile.includes('session')) {
|
||||||
|
return 'completed';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audit files
|
||||||
|
if (lowerFile.includes('audit')) {
|
||||||
|
return 'audit';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: research documentation
|
||||||
|
return 'research';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract project key from directory path
|
||||||
|
*/
|
||||||
|
function getProjectKey(filePath) {
|
||||||
|
const parts = filePath.split('/');
|
||||||
|
|
||||||
|
// ClaudeFramework files
|
||||||
|
if (filePath.includes('/ClaudeFramework/')) {
|
||||||
|
return 'CF';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apps directory
|
||||||
|
if (filePath.includes('/Apps/')) {
|
||||||
|
const idx = parts.indexOf('Apps');
|
||||||
|
const projectName = parts[idx + 1] || '';
|
||||||
|
return generateProjectKey(projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infrastructure directory
|
||||||
|
if (filePath.includes('/Infrastructure/')) {
|
||||||
|
const idx = parts.indexOf('Infrastructure');
|
||||||
|
// Use the subdirectory after Infrastructure
|
||||||
|
const projectName = parts[idx + 1] || '';
|
||||||
|
return generateProjectKey(projectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to CF
|
||||||
|
return 'CF';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate 2-letter project key from project name
|
||||||
|
*/
|
||||||
|
function generateProjectKey(projectName) {
|
||||||
|
if (!projectName) return 'CF';
|
||||||
|
|
||||||
|
// Special cases
|
||||||
|
const specialCases = {
|
||||||
|
'eToroGridbot': 'GB',
|
||||||
|
'ZorkiOS': 'ZK',
|
||||||
|
'RealEstate': 'RE',
|
||||||
|
'AgilitonScripts': 'AS',
|
||||||
|
'VPN': 'VPN', // Keep full
|
||||||
|
'mcp-servers': 'MC',
|
||||||
|
'cloudmemorymcp': 'CM'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (specialCases[projectName]) {
|
||||||
|
return specialCases[projectName];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate from name
|
||||||
|
const normalized = projectName
|
||||||
|
.replace(/([A-Z])/g, ' $1') // Split camelCase
|
||||||
|
.trim()
|
||||||
|
.toUpperCase();
|
||||||
|
|
||||||
|
const words = normalized.split(/\s+/);
|
||||||
|
|
||||||
|
if (words.length >= 2) {
|
||||||
|
return words[0][0] + words[1][0];
|
||||||
|
} else if (words[0].length >= 2) {
|
||||||
|
return words[0].substring(0, 2);
|
||||||
|
} else {
|
||||||
|
return words[0][0] + 'X';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract title from markdown content (first H1)
|
||||||
|
*/
|
||||||
|
function extractTitle(content, filename, projectKey) {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// Look for first H1
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('# ')) {
|
||||||
|
return line.slice(2).trim().substring(0, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to project key + filename
|
||||||
|
const baseName = filename.replace('.md', '').replace(/_/g, ' ');
|
||||||
|
return `${projectKey} - ${baseName}`.substring(0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate a single file
|
||||||
|
*/
|
||||||
|
async function migrateFile(filePath) {
|
||||||
|
const filename = basename(filePath);
|
||||||
|
const projectKey = getProjectKey(filePath);
|
||||||
|
const archiveType = getArchiveType(filePath, filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = readFileSync(filePath, 'utf-8');
|
||||||
|
const title = extractTitle(content, filename, projectKey);
|
||||||
|
const fileSize = statSync(filePath).size;
|
||||||
|
|
||||||
|
console.log(`\n[${new Date().toISOString().substring(11, 19)}] ${DRY_RUN ? '[DRY RUN] ' : ''}Migrating: ${relative(DEV_DIR, filePath)}`);
|
||||||
|
console.log(` Project: ${projectKey}`);
|
||||||
|
console.log(` Title: ${title}`);
|
||||||
|
console.log(` Size: ${Math.round(fileSize / 1024)}KB`);
|
||||||
|
console.log(` Type: ${archiveType}`);
|
||||||
|
|
||||||
|
if (DRY_RUN) {
|
||||||
|
console.log(` → Would migrate to database`);
|
||||||
|
return { success: true, filename, projectKey, title, fileSize, archiveType, filePath, dryRun: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call archive_add
|
||||||
|
const result = await archiveAdd({
|
||||||
|
project: projectKey,
|
||||||
|
archive_type: archiveType,
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
original_path: filePath,
|
||||||
|
file_size: fileSize
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` ✓ Migrated to database`);
|
||||||
|
|
||||||
|
// Move to backup
|
||||||
|
const backupDir = join(dirname(filePath), '.migrated-to-mcp');
|
||||||
|
if (!existsSync(backupDir)) {
|
||||||
|
mkdirSync(backupDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const backupPath = join(backupDir, filename);
|
||||||
|
renameSync(filePath, backupPath);
|
||||||
|
console.log(` ✓ Moved to backup`);
|
||||||
|
|
||||||
|
return { success: true, filename, projectKey, title, fileSize, archiveType, filePath };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(` ✗ Error: ${error.message}`);
|
||||||
|
return { success: false, filename, projectKey, archiveType, error: error.message, filePath };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find all .md files to migrate recursively
|
||||||
|
*/
|
||||||
|
function findAllMarkdownFiles() {
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
function scanDir(dir) {
|
||||||
|
if (!existsSync(dir)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entries = readdirSync(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = join(dir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
// Skip hidden directories and known excludes
|
||||||
|
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
||||||
|
// Check if this directory path should be excluded
|
||||||
|
if (!EXCLUDE_PATTERNS.some(pattern => fullPath.includes(pattern))) {
|
||||||
|
scanDir(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||||
|
// Check if this specific file should be excluded
|
||||||
|
if (!shouldExclude(fullPath, entry.name)) {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Skip directories we can't read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanDir(DEV_DIR);
|
||||||
|
return files.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main migration function
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
console.log('================================================================================');
|
||||||
|
console.log(`Comprehensive Documentation Migration → task-mcp database ${DRY_RUN ? '[DRY RUN]' : ''}`);
|
||||||
|
console.log('================================================================================\n');
|
||||||
|
|
||||||
|
console.log('Scanning Development directory for .md files...\n');
|
||||||
|
|
||||||
|
// Find all files
|
||||||
|
const filesToMigrate = findAllMarkdownFiles();
|
||||||
|
|
||||||
|
console.log(`Found ${filesToMigrate.length} documentation files to migrate\n`);
|
||||||
|
|
||||||
|
if (filesToMigrate.length === 0) {
|
||||||
|
console.log('✓ No files to migrate!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate total size
|
||||||
|
const totalSize = filesToMigrate.reduce((sum, f) => {
|
||||||
|
try {
|
||||||
|
return sum + statSync(f).size;
|
||||||
|
} catch {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
console.log(`Total size: ${Math.round(totalSize / 1024 / 1024 * 100) / 100}MB\n`);
|
||||||
|
|
||||||
|
// Show sample paths by category
|
||||||
|
const byType = {};
|
||||||
|
filesToMigrate.forEach(f => {
|
||||||
|
const type = getArchiveType(f, basename(f));
|
||||||
|
if (!byType[type]) byType[type] = [];
|
||||||
|
byType[type].push(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Files by archive type:');
|
||||||
|
Object.entries(byType).forEach(([type, files]) => {
|
||||||
|
console.log(`\n ${type}: ${files.length} files`);
|
||||||
|
files.slice(0, 3).forEach(f => {
|
||||||
|
console.log(` - ${relative(DEV_DIR, f)}`);
|
||||||
|
});
|
||||||
|
if (files.length > 3) {
|
||||||
|
console.log(` ... and ${files.length - 3} more`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (DRY_RUN) {
|
||||||
|
console.log('\n' + '='.repeat(80));
|
||||||
|
console.log('DRY RUN MODE - No files will be migrated');
|
||||||
|
console.log('='.repeat(80));
|
||||||
|
console.log('\nRemove --dry-run flag to execute migration\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n' + '='.repeat(80));
|
||||||
|
console.log('Starting migration...');
|
||||||
|
console.log('='.repeat(80) + '\n');
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
success: [],
|
||||||
|
failed: []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Migrate each file
|
||||||
|
for (let i = 0; i < filesToMigrate.length; i++) {
|
||||||
|
const filePath = filesToMigrate[i];
|
||||||
|
const result = await migrateFile(filePath);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
results.success.push(result);
|
||||||
|
} else {
|
||||||
|
results.failed.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress indicator
|
||||||
|
const progress = Math.round(((i + 1) / filesToMigrate.length) * 100);
|
||||||
|
console.log(` Progress: ${i + 1}/${filesToMigrate.length} (${progress}%)`);
|
||||||
|
|
||||||
|
// Small delay to avoid overwhelming the database
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
console.log('\n' + '='.repeat(80));
|
||||||
|
console.log('Migration Complete');
|
||||||
|
console.log('='.repeat(80) + '\n');
|
||||||
|
console.log(`✓ Successfully migrated: ${results.success.length} files`);
|
||||||
|
console.log(`✗ Failed: ${results.failed.length} files`);
|
||||||
|
|
||||||
|
if (results.failed.length > 0) {
|
||||||
|
console.log('\nFailed files:');
|
||||||
|
results.failed.forEach(f => {
|
||||||
|
console.log(` - ${relative(DEV_DIR, f.filePath)}: ${f.error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group by archive type
|
||||||
|
const successByType = {};
|
||||||
|
results.success.forEach(r => {
|
||||||
|
successByType[r.archiveType] = (successByType[r.archiveType] || 0) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nBy archive type:');
|
||||||
|
Object.entries(successByType)
|
||||||
|
.sort((a, b) => b[1] - a[1])
|
||||||
|
.forEach(([type, count]) => {
|
||||||
|
console.log(` - ${type}: ${count} files`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group by project
|
||||||
|
const byProject = {};
|
||||||
|
results.success.forEach(r => {
|
||||||
|
byProject[r.projectKey] = (byProject[r.projectKey] || 0) + 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nBy project:');
|
||||||
|
Object.entries(byProject)
|
||||||
|
.sort((a, b) => b[1] - a[1])
|
||||||
|
.forEach(([project, count]) => {
|
||||||
|
console.log(` - ${project}: ${count} files`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\nBackup location: <project-dir>/.migrated-to-mcp/');
|
||||||
|
console.log('Original files can be restored if needed.');
|
||||||
|
|
||||||
|
// Calculate migrated size
|
||||||
|
const migratedSize = results.success.reduce((sum, r) => sum + (r.fileSize || 0), 0);
|
||||||
|
console.log(`\nTotal data migrated: ${Math.round(migratedSize / 1024 / 1024 * 100) / 100}MB`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migration
|
||||||
|
main()
|
||||||
|
.then(() => {
|
||||||
|
console.log('\n✓ Migration script completed successfully');
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('\n✗ Migration script failed:', error);
|
||||||
|
console.error(error.stack);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
106
migrate-external-archive.mjs
Normal file
106
migrate-external-archive.mjs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Migrate remaining 5 files from external archive directory to database
|
||||||
|
* These files were manually created in ~/Documents/ClaudeFramework-Archive/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, statSync } from 'fs';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import { archiveAdd } from './dist/tools/archives.js';
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const FILES_TO_MIGRATE = [
|
||||||
|
{
|
||||||
|
path: '/Users/christian.gick/Documents/ClaudeFramework-Archive/research/IAP_GUIDE.md',
|
||||||
|
type: 'research',
|
||||||
|
project: 'CF'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/Users/christian.gick/Documents/ClaudeFramework-Archive/archive/audits/CREDENTIAL_AUDIT_2025-12-08.md',
|
||||||
|
type: 'audit',
|
||||||
|
project: 'CF'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/Users/christian.gick/Documents/ClaudeFramework-Archive/archive/history/CLAUDE_HISTORY_ARCHIVE_1.md',
|
||||||
|
type: 'session',
|
||||||
|
project: 'CF'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/Users/christian.gick/Documents/ClaudeFramework-Archive/archive/history/CLAUDE_HISTORY_ARCHIVE_2.md',
|
||||||
|
type: 'session',
|
||||||
|
project: 'CF'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/Users/christian.gick/Documents/ClaudeFramework-Archive/archive/CLAUDE_HISTORY_FULL.md',
|
||||||
|
type: 'session',
|
||||||
|
project: 'CF'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function extractTitle(content, filename) {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('# ')) {
|
||||||
|
return line.slice(2).trim().substring(0, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filename.replace('.md', '').replace(/_/g, ' ').substring(0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateFile(fileInfo) {
|
||||||
|
try {
|
||||||
|
const content = readFileSync(fileInfo.path, 'utf-8');
|
||||||
|
const filename = fileInfo.path.split('/').pop();
|
||||||
|
const title = extractTitle(content, filename);
|
||||||
|
const fileSize = statSync(fileInfo.path).size;
|
||||||
|
|
||||||
|
console.log(`\nMigrating: ${filename}`);
|
||||||
|
console.log(` Title: ${title}`);
|
||||||
|
console.log(` Size: ${Math.round(fileSize / 1024)}KB`);
|
||||||
|
console.log(` Type: ${fileInfo.type}`);
|
||||||
|
|
||||||
|
const result = await archiveAdd({
|
||||||
|
project: fileInfo.project,
|
||||||
|
archive_type: fileInfo.type,
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
original_path: fileInfo.path,
|
||||||
|
file_size: fileSize
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` ✓ Migrated (ID: ${result.id})`);
|
||||||
|
return { success: true, filename, fileSize };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(` ✗ Error: ${error.message}`);
|
||||||
|
return { success: false, filename: fileInfo.path.split('/').pop(), error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Migrating 5 external archive files...\n');
|
||||||
|
|
||||||
|
let totalSuccess = 0;
|
||||||
|
let totalFailed = 0;
|
||||||
|
let totalSize = 0;
|
||||||
|
|
||||||
|
for (const file of FILES_TO_MIGRATE) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n✓ Migration complete: ${totalSuccess}/${FILES_TO_MIGRATE.length} files (${Math.round(totalSize / 1024)}KB)`);
|
||||||
|
if (totalFailed > 0) {
|
||||||
|
console.log(`✗ Failed: ${totalFailed} files`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(error => {
|
||||||
|
console.error('Fatal error:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
279
migrate-missed-docs.mjs
Normal file
279
migrate-missed-docs.mjs
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Supplementary migration for 34 files missed by initial migration
|
||||||
|
*
|
||||||
|
* Missed due to hidden directory exclusions (.framework-backup, .project-files, .claude/commands, .playwright-mcp)
|
||||||
|
* and Archived/ directory exclusion.
|
||||||
|
*
|
||||||
|
* This script:
|
||||||
|
* - Migrates 34 specific files with hardcoded paths
|
||||||
|
* - Assigns proper archive types based on file category
|
||||||
|
* - DELETES files after successful migration (no backup needed - already have backups from main migration)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, unlinkSync, statSync, existsSync } from 'fs';
|
||||||
|
import { join, basename, dirname } from 'path';
|
||||||
|
import { homedir } from 'os';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import { archiveAdd } from './dist/tools/archives.js';
|
||||||
|
|
||||||
|
// Load environment
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
// Base directory
|
||||||
|
const DEV_DIR = join(homedir(), 'Development');
|
||||||
|
|
||||||
|
// Files to migrate with their categories
|
||||||
|
const FILES_TO_MIGRATE = [
|
||||||
|
// .framework-backup (10 files) - completed
|
||||||
|
{ path: 'Apps/eToroGridbot/.framework-backup/planning.md', type: 'completed', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/.framework-backup/PRD.md', type: 'completed', project: 'GB' },
|
||||||
|
{ path: 'Apps/Circles/.framework-backup/planning.md', type: 'completed', project: 'CI' },
|
||||||
|
{ path: 'Apps/Circles/.framework-backup/PRD.md', type: 'completed', project: 'CI' },
|
||||||
|
{ path: 'Apps/VPN/.framework-backup/planning.md', type: 'completed', project: 'VPN' },
|
||||||
|
{ path: 'Apps/VPN/.framework-backup/PRD.md', type: 'completed', project: 'VPN' },
|
||||||
|
{ path: 'Apps/Cardscanner/.framework-backup/planning.md', type: 'completed', project: 'CS' },
|
||||||
|
{ path: 'Apps/Cardscanner/.framework-backup/PRD.md', type: 'completed', project: 'CS' },
|
||||||
|
{ path: 'Apps/SmartTranslate/.framework-backup/planning.md', type: 'completed', project: 'ST' },
|
||||||
|
{ path: 'Apps/SmartTranslate/.framework-backup/PRD.md', type: 'completed', project: 'ST' },
|
||||||
|
|
||||||
|
// .project-files (4 files) - research
|
||||||
|
{ path: 'Infrastructure/ClaudeFramework/.project-files/CLAUDE.md', type: 'research', project: 'CF' },
|
||||||
|
{ path: 'Infrastructure/ClaudeFramework/.project-files/LEARNINGS.md', type: 'research', project: 'CF' },
|
||||||
|
{ path: 'Infrastructure/ClaudeFramework/.project-files/TOOLS.md', type: 'research', project: 'CF' },
|
||||||
|
{ path: 'Infrastructure/ClaudeFramework/.project-files/SETUP-INSTRUCTIONS.md', type: 'research', project: 'CF' },
|
||||||
|
|
||||||
|
// .claude/commands (8 files) - research
|
||||||
|
{ path: 'Apps/eToroGridbot/.claude/commands/trading-monitor.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/.claude/commands/trading-pi-post.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/.claude/commands/trading-start.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/.claude/commands/trading-newsletter.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/conductor/.claude/commands/trading-monitor.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/conductor/.claude/commands/trading-pi-post.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/conductor/.claude/commands/trading-start.md', type: 'research', project: 'GB' },
|
||||||
|
{ path: 'Apps/eToroGridbot/conductor/.claude/commands/trading-newsletter.md', type: 'research', project: 'GB' },
|
||||||
|
|
||||||
|
// .playwright-mcp (1 file) - research
|
||||||
|
{ path: 'Apps/OfBullsAndBears/.playwright-mcp/base44-sync-status.md', type: 'research', project: 'OB' },
|
||||||
|
|
||||||
|
// Archived (11 files) - completed
|
||||||
|
{ path: 'Archived/CSMaps/claude.md', type: 'completed', project: 'CM' },
|
||||||
|
{ path: 'Archived/CSMaps/planning.md', type: 'completed', project: 'CM' },
|
||||||
|
{ path: 'Archived/CSMaps/PRD.md', type: 'completed', project: 'CM' },
|
||||||
|
{ path: 'Archived/CSMaps/tasks.md', type: 'completed', project: 'CM' },
|
||||||
|
{ path: 'Archived/ZorkiOS_Source/claude.md', type: 'completed', project: 'ZK' },
|
||||||
|
{ path: 'Archived/ZorkiOS_Source/planning.md', type: 'completed', project: 'ZK' },
|
||||||
|
{ path: 'Archived/ZorkiOS_Source/PRD.md', type: 'completed', project: 'ZK' },
|
||||||
|
{ path: 'Archived/ZorkiOS_Source/tasks.md', type: 'completed', project: 'ZK' },
|
||||||
|
{ path: 'Archived/MacOS-Claude/DEPLOYMENT_CONFIG.md', type: 'completed', project: 'MC' },
|
||||||
|
{ path: 'Archived/MacOS-Claude/AppStoreDeployment.md', type: 'completed', project: 'MC' },
|
||||||
|
{ path: 'Archived/MacOS-Claude/README_XPC.md', type: 'completed', project: 'MC' }
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract title from markdown content (first H1)
|
||||||
|
*/
|
||||||
|
function extractTitle(content, filename, projectKey) {
|
||||||
|
const lines = content.split('\n');
|
||||||
|
|
||||||
|
// Look for first H1
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('# ')) {
|
||||||
|
return line.slice(2).trim().substring(0, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to project key + filename
|
||||||
|
const baseName = filename.replace('.md', '').replace(/_/g, ' ');
|
||||||
|
return `${projectKey} - ${baseName}`.substring(0, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate a single file
|
||||||
|
*/
|
||||||
|
async function migrateFile(fileInfo) {
|
||||||
|
const filePath = join(DEV_DIR, fileInfo.path);
|
||||||
|
const filename = basename(filePath);
|
||||||
|
|
||||||
|
// Check if file exists
|
||||||
|
if (!existsSync(filePath)) {
|
||||||
|
console.log(`\n⚠ SKIP: ${fileInfo.path} (file not found)`);
|
||||||
|
return { success: false, filename, projectKey: fileInfo.project, error: 'File not found', filePath: fileInfo.path };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = readFileSync(filePath, 'utf-8');
|
||||||
|
const title = extractTitle(content, filename, fileInfo.project);
|
||||||
|
const fileSize = statSync(filePath).size;
|
||||||
|
|
||||||
|
console.log(`\n[${new Date().toISOString().substring(11, 19)}] Migrating: ${fileInfo.path}`);
|
||||||
|
console.log(` Project: ${fileInfo.project}`);
|
||||||
|
console.log(` Title: ${title}`);
|
||||||
|
console.log(` Size: ${Math.round(fileSize / 1024)}KB`);
|
||||||
|
console.log(` Type: ${fileInfo.type}`);
|
||||||
|
|
||||||
|
// Call archive_add
|
||||||
|
const result = await archiveAdd({
|
||||||
|
project: fileInfo.project,
|
||||||
|
archive_type: fileInfo.type,
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
original_path: filePath,
|
||||||
|
file_size: fileSize
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` ✓ Migrated to database (ID: ${result.id})`);
|
||||||
|
|
||||||
|
// DELETE the original file (no backup needed)
|
||||||
|
unlinkSync(filePath);
|
||||||
|
console.log(` ✓ Deleted original file`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
filename,
|
||||||
|
projectKey: fileInfo.project,
|
||||||
|
title,
|
||||||
|
fileSize,
|
||||||
|
archiveType: fileInfo.type,
|
||||||
|
filePath: fileInfo.path,
|
||||||
|
archiveId: result.id
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(` ✗ Error: ${error.message}`);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
filename,
|
||||||
|
projectKey: fileInfo.project,
|
||||||
|
archiveType: fileInfo.type,
|
||||||
|
error: error.message,
|
||||||
|
filePath: fileInfo.path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main migration function
|
||||||
|
*/
|
||||||
|
async function main() {
|
||||||
|
console.log('================================================================================');
|
||||||
|
console.log('Supplementary Migration - 34 Missed Files → task-mcp database');
|
||||||
|
console.log('================================================================================\n');
|
||||||
|
|
||||||
|
const results = {
|
||||||
|
frameworkBackup: [],
|
||||||
|
projectFiles: [],
|
||||||
|
claudeCommands: [],
|
||||||
|
playwrightMcp: [],
|
||||||
|
archived: []
|
||||||
|
};
|
||||||
|
|
||||||
|
let totalSuccess = 0;
|
||||||
|
let totalFailed = 0;
|
||||||
|
let totalSize = 0;
|
||||||
|
|
||||||
|
// Migrate .framework-backup files
|
||||||
|
console.log('\n📁 Category: .framework-backup (10 files)');
|
||||||
|
console.log('─'.repeat(80));
|
||||||
|
for (const file of FILES_TO_MIGRATE.slice(0, 10)) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
results.frameworkBackup.push(result);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate .project-files
|
||||||
|
console.log('\n📁 Category: .project-files (4 files)');
|
||||||
|
console.log('─'.repeat(80));
|
||||||
|
for (const file of FILES_TO_MIGRATE.slice(10, 14)) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
results.projectFiles.push(result);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate .claude/commands
|
||||||
|
console.log('\n📁 Category: .claude/commands (8 files)');
|
||||||
|
console.log('─'.repeat(80));
|
||||||
|
for (const file of FILES_TO_MIGRATE.slice(14, 22)) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
results.claudeCommands.push(result);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate .playwright-mcp
|
||||||
|
console.log('\n📁 Category: .playwright-mcp (1 file)');
|
||||||
|
console.log('─'.repeat(80));
|
||||||
|
for (const file of FILES_TO_MIGRATE.slice(22, 23)) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
results.playwrightMcp.push(result);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate Archived
|
||||||
|
console.log('\n📁 Category: Archived projects (11 files)');
|
||||||
|
console.log('─'.repeat(80));
|
||||||
|
for (const file of FILES_TO_MIGRATE.slice(23)) {
|
||||||
|
const result = await migrateFile(file);
|
||||||
|
results.archived.push(result);
|
||||||
|
if (result.success) {
|
||||||
|
totalSuccess++;
|
||||||
|
totalSize += result.fileSize || 0;
|
||||||
|
} else {
|
||||||
|
totalFailed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
console.log('\n\n================================================================================');
|
||||||
|
console.log('Migration Summary');
|
||||||
|
console.log('================================================================================\n');
|
||||||
|
|
||||||
|
console.log(`Total files processed: ${FILES_TO_MIGRATE.length}`);
|
||||||
|
console.log(`✓ Successfully migrated: ${totalSuccess}`);
|
||||||
|
console.log(`✗ Failed: ${totalFailed}`);
|
||||||
|
console.log(`Total size migrated: ${Math.round(totalSize / 1024)}KB\n`);
|
||||||
|
|
||||||
|
console.log('By category:');
|
||||||
|
console.log(` .framework-backup: ${results.frameworkBackup.filter(r => r.success).length}/10`);
|
||||||
|
console.log(` .project-files: ${results.projectFiles.filter(r => r.success).length}/4`);
|
||||||
|
console.log(` .claude/commands: ${results.claudeCommands.filter(r => r.success).length}/8`);
|
||||||
|
console.log(` .playwright-mcp: ${results.playwrightMcp.filter(r => r.success).length}/1`);
|
||||||
|
console.log(` Archived: ${results.archived.filter(r => r.success).length}/11\n`);
|
||||||
|
|
||||||
|
if (totalFailed > 0) {
|
||||||
|
console.log('\n❌ Failed files:');
|
||||||
|
const allResults = [...results.frameworkBackup, ...results.projectFiles, ...results.claudeCommands, ...results.playwrightMcp, ...results.archived];
|
||||||
|
allResults.filter(r => !r.success).forEach(r => {
|
||||||
|
console.log(` - ${r.filePath}: ${r.error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n✓ Supplementary migration complete!');
|
||||||
|
console.log('✓ All successfully migrated files have been DELETED from filesystem');
|
||||||
|
console.log('✓ Next step: Cleanup .migrated-to-mcp directories from main migration\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run migration
|
||||||
|
main().catch(error => {
|
||||||
|
console.error('Fatal error:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
10658
migration-log.txt
Normal file
10658
migration-log.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user