Skip to content

Latest commit

 

History

History
732 lines (602 loc) · 17.1 KB

File metadata and controls

732 lines (602 loc) · 17.1 KB

PRISM - Immediate Action Plan

Goal: Get to a working MVP in 1 week Status: 75% Complete → 95% Complete


Phase 1: Critical Fixes (Days 1-2)

Fix 1: TypeScript Compilation Errors (4 hours)

Problem:

src/vector-db/MemoryVectorDB.ts(334,26): error TS1003: Identifier expected.
# 50+ cascading syntax errors

Solution:

# Step 1: Locate the syntax error
cd /home/user/prism
grep -n "Example:" src/vector-db/MemoryVectorDB.ts | head -5

# Step 2: Fix template literal in comment (likely line 334)
# The issue is probably an unclosed string or template literal
# in a JSDoc comment or markdown-style documentation

# Step 3: Test compilation
npm run typecheck

# Step 4: Build if successful
npm run build:ts

Expected Outcome:

  • ✅ TypeScript compiles without errors
  • ✅ dist/ directory created
  • ✅ MCP server compiled to dist/mcp/cli.js

Fix 2: Implement Real File Indexing (8 hours)

Current State:

// daemon/server.js - Mock search only
simpleSearch(query) {
  return [{
    file: 'README.md',
    content: `Found search query: "${query}"`,
    score: 0.9,
    line: 1
  }];
}

Implementation Plan:

File: /home/user/prism/claude-code-plugin/daemon/file-indexer.js

const fs = require('fs').promises;
const path = require('path');

class FileIndexer {
  constructor(projectRoot, indexDir) {
    this.projectRoot = projectRoot;
    this.indexDir = indexDir;
    this.indexPath = path.join(indexDir, 'index.json');

    // Patterns to include
    this.includePatterns = [
      /\.(js|ts|jsx|tsx|py|go|rs|java|cs|php|rb)$/,
      /\.(md|json|yaml|yml)$/
    ];

    // Patterns to exclude
    this.excludePatterns = [
      /node_modules/,
      /\.git/,
      /dist/,
      /build/,
      /coverage/,
      /\.next/
    ];
  }

  /**
   * Index all files in the project
   */
  async indexProject() {
    console.log('[Indexer] Starting indexing...');
    const files = await this.scanDirectory(this.projectRoot);
    const index = {
      version: '1.0',
      indexed_at: new Date().toISOString(),
      project_root: this.projectRoot,
      file_count: files.length,
      files: files
    };

    await this.saveIndex(index);
    console.log(`[Indexer] Indexed ${files.length} files`);
    return index;
  }

  /**
   * Scan directory recursively
   */
  async scanDirectory(dir, files = []) {
    const entries = await fs.readdir(dir, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      const relativePath = path.relative(this.projectRoot, fullPath);

      // Check exclusions
      if (this.shouldExclude(relativePath)) {
        continue;
      }

      if (entry.isDirectory()) {
        await this.scanDirectory(fullPath, files);
      } else if (entry.isFile() && this.shouldInclude(entry.name)) {
        try {
          const content = await fs.readFile(fullPath, 'utf8');
          const stats = await fs.stat(fullPath);

          files.push({
            path: relativePath,
            name: entry.name,
            size: stats.size,
            modified: stats.mtime.toISOString(),
            lines: content.split('\n').length,
            content: content,
            extension: path.extname(entry.name),
            language: this.detectLanguage(entry.name)
          });
        } catch (error) {
          console.warn(`[Indexer] Failed to index ${relativePath}:`, error.message);
        }
      }
    }

    return files;
  }

  /**
   * Check if file should be included
   */
  shouldInclude(filename) {
    return this.includePatterns.some(pattern => pattern.test(filename));
  }

  /**
   * Check if path should be excluded
   */
  shouldExclude(relativePath) {
    return this.excludePatterns.some(pattern => pattern.test(relativePath));
  }

  /**
   * Detect programming language from filename
   */
  detectLanguage(filename) {
    const ext = path.extname(filename);
    const languageMap = {
      '.js': 'javascript',
      '.jsx': 'javascript',
      '.ts': 'typescript',
      '.tsx': 'typescript',
      '.py': 'python',
      '.go': 'go',
      '.rs': 'rust',
      '.java': 'java',
      '.cs': 'csharp',
      '.php': 'php',
      '.rb': 'ruby',
      '.md': 'markdown',
      '.json': 'json',
      '.yaml': 'yaml',
      '.yml': 'yaml'
    };
    return languageMap[ext] || 'unknown';
  }

  /**
   * Save index to disk
   */
  async saveIndex(index) {
    await fs.mkdir(this.indexDir, { recursive: true });
    await fs.writeFile(this.indexPath, JSON.stringify(index, null, 2));
  }

  /**
   * Load index from disk
   */
  async loadIndex() {
    try {
      const data = await fs.readFile(this.indexPath, 'utf8');
      return JSON.parse(data);
    } catch (error) {
      return null;
    }
  }

  /**
   * Search indexed files
   */
  searchIndex(query, limit = 10) {
    const index = this.loadedIndex;
    if (!index || !index.files) {
      return [];
    }

    const results = [];
    const queryLower = query.toLowerCase();

    for (const file of index.files) {
      const content = file.content || '';
      const contentLower = content.toLowerCase();

      // Find all matches
      const lines = content.split('\n');
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        const lineLower = line.toLowerCase();

        if (lineLower.includes(queryLower)) {
          // Calculate relevance score
          const score = this.calculateScore(line, query, file);

          results.push({
            file: file.path,
            line: i + 1,
            content: line.trim(),
            score: score,
            language: file.language,
            context: this.getContext(lines, i)
          });
        }
      }
    }

    // Sort by score and return top results
    results.sort((a, b) => b.score - a.score);
    return results.slice(0, limit);
  }

  /**
   * Calculate relevance score
   */
  calculateScore(line, query, file) {
    let score = 0;

    // Exact match bonus
    if (line.toLowerCase().includes(query.toLowerCase())) {
      score += 0.5;
    }

    // File name match bonus
    if (file.name.toLowerCase().includes(query.toLowerCase())) {
      score += 0.2;
    }

    // Path match bonus
    if (file.path.toLowerCase().includes(query.toLowerCase())) {
      score += 0.1;
    }

    // Length penalty (prefer shorter, more focused matches)
    score += 0.2 / (1 + line.length / 100);

    return Math.min(1.0, score);
  }

  /**
   * Get surrounding context for a line
   */
  getContext(lines, lineIndex, contextLines = 2) {
    const start = Math.max(0, lineIndex - contextLines);
    const end = Math.min(lines.length, lineIndex + contextLines + 1);
    return lines.slice(start, end).join('\n');
  }
}

module.exports = FileIndexer;

Update: /home/user/prism/claude-code-plugin/daemon/server.js

// Add at top:
const FileIndexer = require('./file-indexer');

// In constructor:
this.indexer = new FileIndexer(
  this.config.projectRoot,
  path.join(this.config.projectRoot, '.prism')
);
this.indexLoaded = false;

// Update initialize():
async initialize() {
  try {
    await this.discoverProject();

    // Load or create index
    const existingIndex = await this.indexer.loadIndex();
    if (existingIndex) {
      this.indexer.loadedIndex = existingIndex;
      this.indexLoaded = true;
      console.log(`[PRISM] Loaded index with ${existingIndex.file_count} files`);
    } else {
      console.log('[PRISM] No index found, indexing project...');
      await this.indexer.indexProject();
      this.indexer.loadedIndex = await this.indexer.loadIndex();
      this.indexLoaded = true;
    }

    console.log(`[PRISM] Project: ${this.projectInfo?.name || 'Unknown'} (${this.projectInfo?.language || 'unknown'})`);
  } catch (error) {
    console.error('[PRISM] Initialization failed:', error.message);
    throw error;
  }
}

// Replace simpleSearch():
simpleSearch(query) {
  if (!this.indexLoaded) {
    return [{
      file: 'ERROR',
      content: 'Index not loaded. Please wait for indexing to complete.',
      score: 0,
      line: 1
    }];
  }

  return this.indexer.searchIndex(query);
}

// Add new endpoint for reindexing:
} else if (method === 'POST' && url === '/index') {
  this.handleReindex(req, res);
}

handleReindex(req, res) {
  res.writeHead(202);
  res.end(JSON.stringify({
    status: 'indexing',
    message: 'Reindexing started in background'
  }));

  // Index in background
  this.indexer.indexProject().then(() => {
    this.indexer.loadedIndex = await this.indexer.loadIndex();
    this.indexLoaded = true;
    console.log('[PRISM] Reindexing complete');
  }).catch(error => {
    console.error('[PRISM] Reindexing failed:', error);
  });
}

Test:

# Start server
cd /home/user/prism/claude-code-plugin
node daemon/server.js

# Test indexing (in another terminal)
curl http://localhost:8080/project
curl -X POST http://localhost:8080/index
curl -X POST http://localhost:8080/search -d '{"query":"function"}' | jq '.'

Expected Outcome:

  • ✅ Files indexed to .prism/index.json
  • ✅ Search returns real results from project
  • ✅ Results ranked by relevance

Phase 2: MCP Integration (Days 3-4)

Fix 3: Implement MCP Tools (6 hours)

Add to: /home/user/prism/claude-code-plugin/daemon/server.js

/**
 * Handle MCP tools/list request
 */
handleToolsList(req, res) {
  const tools = [
    {
      name: 'search_repo',
      description: 'Search the indexed codebase for relevant code chunks',
      inputSchema: {
        type: 'object',
        properties: {
          query: {
            type: 'string',
            description: 'The search query (keywords or natural language)'
          },
          limit: {
            type: 'number',
            description: 'Maximum number of results to return',
            default: 10
          }
        },
        required: ['query']
      }
    },
    {
      name: 'get_file',
      description: 'Retrieve the full contents of a specific file',
      inputSchema: {
        type: 'object',
        properties: {
          path: {
            type: 'string',
            description: 'Relative path to the file'
          }
        },
        required: ['path']
      }
    },
    {
      name: 'list_files',
      description: 'List all indexed files in the project',
      inputSchema: {
        type: 'object',
        properties: {
          language: {
            type: 'string',
            description: 'Filter by programming language (optional)'
          }
        }
      }
    }
  ];

  res.writeHead(200);
  res.end(JSON.stringify({ tools }));
}

/**
 * Handle MCP tools/call request
 */
async handleToolsCall(req, res) {
  let data = '';
  req.on('data', chunk => data += chunk);
  req.on('end', async () => {
    try {
      const { name, arguments: args } = JSON.parse(data);

      let result;
      switch (name) {
        case 'search_repo':
          result = this.indexer.searchIndex(args.query, args.limit || 10);
          break;

        case 'get_file':
          const file = this.indexer.loadedIndex?.files.find(f => f.path === args.path);
          result = file ? file.content : 'File not found';
          break;

        case 'list_files':
          const files = this.indexer.loadedIndex?.files || [];
          result = args.language
            ? files.filter(f => f.language === args.language)
            : files;
          result = result.map(f => ({ path: f.path, language: f.language, lines: f.lines }));
          break;

        default:
          throw new Error(`Unknown tool: ${name}`);
      }

      res.writeHead(200);
      res.end(JSON.stringify({
        content: [{
          type: 'text',
          text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
        }]
      }));
    } catch (error) {
      res.writeHead(500);
      res.end(JSON.stringify({ error: error.message }));
    }
  });
}

// Update routing:
} else if (method === 'GET' && url === '/tools/list') {
  this.handleToolsList(res);
} else if (method === 'POST' && url === '/tools/call') {
  await this.handleToolsCall(req, res);
}

Test:

# Test MCP tools
curl http://localhost:8080/tools/list | jq '.'
curl -X POST http://localhost:8080/tools/call -d '{"name":"search_repo","arguments":{"query":"error","limit":5}}' | jq '.'

Expected Outcome:

  • ✅ MCP protocol working
  • ✅ Claude Code can discover tools
  • ✅ Tools return real results

Phase 3: Polish & Testing (Day 5)

Fix 4: Add Persistence & Auto-reload (2 hours)

Features:

  • ✅ Index saved on shutdown
  • ✅ Index loaded on startup
  • ✅ Incremental updates (optional)

Already implemented in file-indexer.js above!


Fix 5: Testing & Validation (4 hours)

Test Suite:

# Create test script
cd /home/user/prism/claude-code-plugin
cat > test-integration.sh <<'EOF'
#!/bin/bash

echo "=== PRISM Integration Test Suite ==="

# Start server in background
node daemon/server.js > /tmp/prism-test.log 2>&1 &
SERVER_PID=$!
sleep 3

# Test 1: Health check
echo "Test 1: Health check"
curl -s http://localhost:8080/health | jq '.' || echo "FAIL"

# Test 2: Project info
echo "Test 2: Project info"
curl -s http://localhost:8080/project | jq '.' || echo "FAIL"

# Test 3: Search
echo "Test 3: Search"
curl -s -X POST http://localhost:8080/search -d '{"query":"function"}' | jq '.' || echo "FAIL"

# Test 4: MCP tools list
echo "Test 4: MCP tools list"
curl -s http://localhost:8080/tools/list | jq '.' || echo "FAIL"

# Test 5: MCP tools call
echo "Test 5: MCP tools call"
curl -s -X POST http://localhost:8080/tools/call -d '{"name":"search_repo","arguments":{"query":"error"}}' | jq '.' || echo "FAIL"

# Cleanup
kill $SERVER_PID
echo "=== Tests Complete ==="
EOF

chmod +x test-integration.sh
./test-integration.sh

Phase 4: Documentation (Day 6-7)

Fix 6: Update Documentation (4 hours)

Update Files:

  1. README.md - Add "Known Limitations" section
  2. QUICK-START.md - Update with current reality
  3. API.md - Document HTTP endpoints
  4. TROUBLESHOOTING.md - Common issues and fixes

Known Limitations Section:

## Known Limitations (v1.0)**What Works:**
- Basic keyword search
- Project detection
- File indexing
- HTTP API
- MCP protocol

⚠️ **What's Limited:**
- Search is keyword-based (not semantic)
- No vector embeddings (planned for v2.0)
- No file watcher yet (manual reindex)
- Single project only

❌ **What's Not Implemented:**
- WASM chunking
- Incremental indexing
- Team collaboration

Success Criteria

Minimum Viable Product (MVP)

Must Have:

  • HTTP server running ✅
  • Real file indexing
  • Keyword search working
  • MCP tools exposed
  • Basic persistence
  • Integration tests passing

Nice to Have:

  • File watcher
  • Semantic search (v2.0)
  • Incremental indexing
  • Performance metrics

Definition of Done

When can we say "it works"?

  1. ✅ Server starts without errors
  2. ✅ Project auto-detected
  3. ⏳ Files indexed automatically
  4. ⏳ Search returns relevant results
  5. ⏳ MCP tools work in Claude Code
  6. ⏳ Index persists across restarts
  7. ⏳ All integration tests pass

Current Status: 2/7 complete (28%) Target: 7/7 complete (100%) ETA: 5 working days


Timeline

Day Tasks Hours Status
Day 1 Fix TypeScript errors 4h ⏳ Pending
Day 2 Implement file indexer 8h ⏳ Pending
Day 3 Replace mock search 4h ⏳ Pending
Day 3 Add MCP tools 4h ⏳ Pending
Day 4 Add persistence 2h ⏳ Pending
Day 4 Write tests 4h ⏳ Pending
Day 5 Run tests + fix bugs 6h ⏳ Pending
Day 6-7 Update docs 4h ⏳ Pending
Total 36h 0% Done

Risk Mitigation

If TypeScript Fix Takes Longer

Plan B: Skip TypeScript compilation for now

  • Use JavaScript-only plugin
  • Add semantic search later as v2.0
  • Focus on working MVP first

If Integration is Complex

Plan B: Start with HTTP API only

  • Document as "HTTP API mode"
  • Add MCP tools as optional enhancement
  • Users can call HTTP endpoints directly

If Performance is Poor

Plan B: Add indexing limits

  • Only index files <100KB
  • Only index most common file types
  • Add configuration for max files

Next Actions

Right Now (Next 30 minutes)

  1. Backup current state:

    cd /home/user/prism
    git add -A
    git commit -m "docs: add comprehensive audit and action plan"
    git push
  2. Start Fix #1 (TypeScript):

    # Find the syntax error
    cd /home/user/prism
    cat src/vector-db/MemoryVectorDB.ts | head -350 | tail -20
  3. Create file-indexer.js:

    # Copy the implementation above
    cd claude-code-plugin/daemon
    # Create file-indexer.js with the code provided

This Week

  • Monday: Fix TypeScript + start indexer
  • Tuesday: Complete indexer + test
  • Wednesday: MCP tools + persistence
  • Thursday: Testing + bug fixes
  • Friday: Documentation + demo

Next Week

  • Polish UI/UX
  • Performance optimization
  • Beta testing with real users
  • Prepare for marketplace submission

Plan Created: January 15, 2026 Target Completion: January 22, 2026 Owner: Development Team Status: 📋 READY TO EXECUTE