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>
106 lines
2.8 KiB
JavaScript
106 lines
2.8 KiB
JavaScript
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
import { join } from 'path';
|
|
import { homedir } from 'os';
|
|
const REGISTRY_DIR = join(homedir(), '.gsd');
|
|
const REGISTRY_PATH = join(REGISTRY_DIR, 'projects.json');
|
|
/**
|
|
* Ensure the registry directory exists
|
|
*/
|
|
function ensureRegistryDir() {
|
|
if (!existsSync(REGISTRY_DIR)) {
|
|
mkdirSync(REGISTRY_DIR, { recursive: true });
|
|
}
|
|
}
|
|
/**
|
|
* Read the project registry
|
|
*/
|
|
export function readRegistry() {
|
|
ensureRegistryDir();
|
|
if (!existsSync(REGISTRY_PATH)) {
|
|
return { version: '1.0.0', projects: [] };
|
|
}
|
|
try {
|
|
const content = readFileSync(REGISTRY_PATH, 'utf-8');
|
|
return JSON.parse(content);
|
|
}
|
|
catch {
|
|
return { version: '1.0.0', projects: [] };
|
|
}
|
|
}
|
|
/**
|
|
* Write the project registry
|
|
*/
|
|
export function writeRegistry(registry) {
|
|
ensureRegistryDir();
|
|
writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2));
|
|
}
|
|
/**
|
|
* Register a new project
|
|
*/
|
|
export function registerProject(project) {
|
|
const registry = readRegistry();
|
|
// Check if project already exists
|
|
const existingIndex = registry.projects.findIndex(p => p.path === project.path || p.name === project.name);
|
|
const newProject = {
|
|
...project,
|
|
registeredAt: new Date().toISOString()
|
|
};
|
|
if (existingIndex >= 0) {
|
|
// Update existing project
|
|
registry.projects[existingIndex] = {
|
|
...registry.projects[existingIndex],
|
|
...newProject,
|
|
registeredAt: registry.projects[existingIndex].registeredAt
|
|
};
|
|
}
|
|
else {
|
|
// Add new project
|
|
registry.projects.push(newProject);
|
|
}
|
|
writeRegistry(registry);
|
|
return newProject;
|
|
}
|
|
/**
|
|
* Update a project's last indexed time
|
|
*/
|
|
export function updateProjectIndexTime(name) {
|
|
const registry = readRegistry();
|
|
const project = registry.projects.find(p => p.name === name);
|
|
if (project) {
|
|
project.lastIndexed = new Date().toISOString();
|
|
writeRegistry(registry);
|
|
}
|
|
}
|
|
/**
|
|
* Get a project by name
|
|
*/
|
|
export function getProject(name) {
|
|
const registry = readRegistry();
|
|
return registry.projects.find(p => p.name === name);
|
|
}
|
|
/**
|
|
* Get all registered projects
|
|
*/
|
|
export function getAllProjects() {
|
|
return readRegistry().projects;
|
|
}
|
|
/**
|
|
* Remove a project from the registry
|
|
*/
|
|
export function unregisterProject(name) {
|
|
const registry = readRegistry();
|
|
const index = registry.projects.findIndex(p => p.name === name);
|
|
if (index >= 0) {
|
|
registry.projects.splice(index, 1);
|
|
writeRegistry(registry);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Get the registry path (for testing)
|
|
*/
|
|
export function getRegistryPath() {
|
|
return REGISTRY_PATH;
|
|
}
|
|
//# sourceMappingURL=registry.js.map
|