@shirokuma-library/mcp-knowledge-base
Version:
MCP server for AI-powered knowledge management with semantic search, graph analysis, and automatic enrichment
173 lines (172 loc) • 8.04 kB
JavaScript
import chalk from 'chalk';
import fs from 'fs';
import { AppDataSource } from '../../data-source.js';
import { Item } from '../../entities/Item.js';
import { SystemState } from '../../entities/SystemState.js';
import { Status } from '../../entities/Status.js';
import { Tag } from '../../entities/Tag.js';
import { ItemTag } from '../../entities/ItemTag.js';
import { ItemRelation } from '../../entities/ItemRelation.js';
import { ImportManagerTypeORM } from '../../services/import-manager.js';
export async function importData(options) {
const manager = new ImportManagerTypeORM();
try {
const result = await manager.importPath(options.file, options);
if (!result.success && result.errors && result.errors.length > 0) {
console.error(chalk.red('Import had errors:'));
for (const error of result.errors) {
console.error(chalk.red(` - ${error.message}`));
}
process.exit(1);
}
}
catch (error) {
console.error(chalk.red('Import failed:'), error);
process.exit(1);
}
}
export async function importDataJSON(options) {
try {
if (!fs.existsSync(options.file)) {
console.error(chalk.red(`File not found: ${options.file}`));
process.exit(1);
}
const data = JSON.parse(fs.readFileSync(options.file, 'utf-8'));
console.log(chalk.bold.cyan('📥 Data Import\n'));
console.log(chalk.gray(`File: ${options.file}`));
console.log(chalk.gray(`Items to import: ${data.items?.length || 0}`));
if (!AppDataSource.isInitialized) {
await AppDataSource.initialize();
}
console.log(chalk.gray('Ensuring database schema...'));
await AppDataSource.runMigrations();
if (options.clear) {
console.log(chalk.yellow('\n⚠️ Clearing existing data...'));
await AppDataSource.getRepository(ItemRelation).clear();
await AppDataSource.getRepository(ItemTag).clear();
await AppDataSource.getRepository(Item).clear();
await AppDataSource.getRepository(Tag).clear();
await AppDataSource.getRepository(Status).clear();
await AppDataSource.getRepository(SystemState).clear();
console.log(chalk.gray('Existing data cleared'));
}
const statusRepo = AppDataSource.getRepository(Status);
const defaultStatuses = [
'Open', 'Specification', 'Waiting', 'Ready',
'In Progress', 'Review', 'Testing', 'Pending',
'Completed', 'Closed', 'Canceled', 'Rejected'
];
const statusMap = new Map();
for (let i = 0; i < defaultStatuses.length; i++) {
let status = await statusRepo.findOne({ where: { name: defaultStatuses[i] } });
if (!status) {
status = await statusRepo.save({
name: defaultStatuses[i],
isClosable: i >= 8,
sortOrder: i
});
}
statusMap.set(defaultStatuses[i], status.id);
}
const itemRepo = AppDataSource.getRepository(Item);
const tagRepo = AppDataSource.getRepository(Tag);
const itemTagRepo = AppDataSource.getRepository(ItemTag);
const relationRepo = AppDataSource.getRepository(ItemRelation);
const idMap = new Map();
let importedCount = 0;
for (const itemData of data.items || []) {
let statusId = statusMap.get(itemData.status || 'Open');
if (!statusId) {
console.log(chalk.yellow(`Warning: Status '${itemData.status}' not found, using 'Open' instead`));
statusId = statusMap.get('Open');
if (!statusId) {
console.error(chalk.red('Error: Default "Open" status not found'));
process.exit(1);
}
}
const item = await itemRepo.save({
type: itemData.type,
title: itemData.title,
description: itemData.description || '',
content: itemData.content || '',
statusId,
priority: itemData.priority || 'MEDIUM',
category: itemData.category,
startDate: itemData.startDate ? new Date(itemData.startDate) : undefined,
endDate: itemData.endDate ? new Date(itemData.endDate) : undefined,
version: itemData.version,
aiSummary: itemData.aiSummary,
searchIndex: itemData.searchIndex,
entities: itemData.entities,
embedding: itemData.embedding ? Buffer.from(itemData.embedding, 'base64') : undefined,
});
idMap.set(itemData.id, item.id);
if (itemData.tags && Array.isArray(itemData.tags)) {
for (const tagName of itemData.tags) {
let tag = await tagRepo.findOne({ where: { name: tagName } });
if (!tag) {
tag = await tagRepo.save({ name: tagName });
}
await itemTagRepo.save({ itemId: item.id, tagId: tag.id });
}
}
importedCount++;
if (importedCount % 10 === 0) {
console.log(chalk.gray(`Imported ${importedCount} items...`));
}
}
console.log(chalk.gray('\nImporting relations...'));
for (const itemData of data.items || []) {
if (itemData.related && Array.isArray(itemData.related)) {
const sourceId = idMap.get(itemData.id);
if (!sourceId)
continue;
for (const oldTargetId of itemData.related) {
const targetId = idMap.get(oldTargetId);
if (targetId) {
const existing = await relationRepo.findOne({
where: { sourceId, targetId }
});
if (!existing) {
await relationRepo.save({ sourceId, targetId });
}
}
}
}
}
if (data.systemState) {
const stateRepo = AppDataSource.getRepository(SystemState);
await stateRepo.save({
version: data.systemState.version,
content: data.systemState.content,
summary: data.systemState.summary,
metrics: typeof data.systemState.metrics === 'object'
? JSON.stringify(data.systemState.metrics)
: data.systemState.metrics,
context: typeof data.systemState.context === 'object'
? JSON.stringify(data.systemState.context)
: data.systemState.context,
checkpoint: typeof data.systemState.checkpoint === 'object'
? JSON.stringify(data.systemState.checkpoint)
: data.systemState.checkpoint,
relatedItems: Array.isArray(data.systemState.relatedItems)
? JSON.stringify(data.systemState.relatedItems)
: data.systemState.relatedItems || '[]',
tags: Array.isArray(data.systemState.tags)
? JSON.stringify(data.systemState.tags)
: data.systemState.tags || '[]',
metadata: typeof data.systemState.metadata === 'object'
? JSON.stringify(data.systemState.metadata)
: data.systemState.metadata,
isActive: true
});
console.log(chalk.gray('System state imported'));
}
console.log(chalk.bold.green(`\n✨ Import completed! Imported ${importedCount} items`));
await AppDataSource.destroy();
}
catch (error) {
console.error(chalk.red('Import failed:'), error);
process.exit(1);
}
}