Implements MCP server for semantic search across GSD projects: MCP Tools: - gsd_memory_search: Search across all registered projects - gsd_memory_decisions: Find decisions from SUMMARY/PROJECT.md - gsd_memory_patterns: Find patterns from SUMMARY.md - gsd_memory_pitfalls: Find pitfalls from RESEARCH.md - gsd_memory_stack: Find tech stack entries - gsd_memory_register: Register project with memory - gsd_memory_index: Trigger indexing/update - gsd_memory_status: Show system status Architecture: - QMD wrapper with grep fallback when QMD unavailable - YAML frontmatter extractors for GSD document types - Project registry at ~/.gsd/projects.json - Auto-install via bin/install.js for Claude Code Workflow integrations: - new-project: Registers project with memory - plan-phase: Indexes after planning - execute-phase: Indexes after execution - complete-milestone: Indexes milestone completion - Researcher agents query memory before Context7 Tests: 117 passing (66 unit + 51 integration) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
105 lines
3.1 KiB
JavaScript
105 lines
3.1 KiB
JavaScript
import { registerProject, getProject, getAllProjects, unregisterProject } from '../registry.js';
|
|
import { qmd } from '../qmd.js';
|
|
import { existsSync } from 'fs';
|
|
import { join, basename } from 'path';
|
|
/**
|
|
* Register a project with GSD memory
|
|
*/
|
|
export async function register(options) {
|
|
// Validate path exists
|
|
if (!existsSync(options.path)) {
|
|
return {
|
|
success: false,
|
|
error: `Path does not exist: ${options.path}`
|
|
};
|
|
}
|
|
// Validate .planning directory exists
|
|
const planningPath = join(options.path, '.planning');
|
|
if (!existsSync(planningPath)) {
|
|
return {
|
|
success: false,
|
|
error: `No .planning directory found at: ${planningPath}`
|
|
};
|
|
}
|
|
// Determine project name
|
|
const name = options.name || basename(options.path);
|
|
// Check if QMD is available for semantic search
|
|
const qmdAvailable = await qmd.isAvailable();
|
|
let qmdCollection;
|
|
if (qmdAvailable) {
|
|
// Create QMD collection
|
|
qmdCollection = `gsd-${name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
const qmdResult = await qmd.createCollection({
|
|
name: qmdCollection,
|
|
paths: [planningPath],
|
|
context: `GSD project: ${name}`
|
|
});
|
|
if (!qmdResult.success) {
|
|
// QMD collection creation failed, but we can still register
|
|
qmdCollection = undefined;
|
|
}
|
|
}
|
|
// Register in local registry
|
|
const project = registerProject({
|
|
name,
|
|
path: options.path,
|
|
qmdCollection
|
|
});
|
|
return {
|
|
success: true,
|
|
project,
|
|
qmdCollection
|
|
};
|
|
}
|
|
/**
|
|
* Get status of all registered projects
|
|
*/
|
|
export async function status() {
|
|
const qmdAvailable = await qmd.isAvailable();
|
|
const projects = getAllProjects();
|
|
const enrichedProjects = await Promise.all(projects.map(async (project) => {
|
|
let documentCount;
|
|
if (qmdAvailable && project.qmdCollection) {
|
|
const qmdStatus = await qmd.status(project.qmdCollection);
|
|
documentCount = qmdStatus.documentCount;
|
|
}
|
|
return {
|
|
name: project.name,
|
|
path: project.path,
|
|
qmdCollection: project.qmdCollection,
|
|
registeredAt: project.registeredAt,
|
|
lastIndexed: project.lastIndexed,
|
|
documentCount
|
|
};
|
|
}));
|
|
return {
|
|
qmdAvailable,
|
|
projects: enrichedProjects
|
|
};
|
|
}
|
|
/**
|
|
* Unregister a project
|
|
*/
|
|
export async function unregister(name) {
|
|
const project = getProject(name);
|
|
if (!project) {
|
|
return {
|
|
success: false,
|
|
error: `Project not found: ${name}`
|
|
};
|
|
}
|
|
// Remove from QMD if it has a collection
|
|
if (project.qmdCollection && await qmd.isAvailable()) {
|
|
try {
|
|
// QMD doesn't have a direct remove command, so we just skip
|
|
// The collection will be orphaned but harmless
|
|
}
|
|
catch {
|
|
// Ignore QMD errors
|
|
}
|
|
}
|
|
// Remove from registry
|
|
unregisterProject(name);
|
|
return { success: true };
|
|
}
|
|
//# sourceMappingURL=register.js.map
|