← back to ClawCoder
src/app/api/files/route.ts
99 lines
import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
import { authenticateRequest } from '@/lib/auth'
export interface ClaudeMdEntry {
path: string
project: string
}
export const CLAUDE_MD_PATHS: ClaudeMdEntry[] = [
{ path: '/root/.claude/CLAUDE.md', project: 'Global Configuration' },
{ path: '/root/CLAUDE.md', project: 'Project Router' },
{ path: '/root/DW-Agents/CLAUDE.md', project: 'DW-Agents' },
{ path: '/root/Projects/Designer-Wallcoverings/CLAUDE.md', project: 'Designer Wallcoverings' },
{ path: '/root/Projects/dw-war-room/CLAUDE.md', project: '3D War Room' },
{ path: '/root/Projects/watches/CLAUDE.md', project: 'Omega Watches' },
{ path: '/root/Projects/dear-bubbe-nextjs/CLAUDE.md', project: 'Bubbe AI' },
{ path: '/root/Projects/wine-finder/CLAUDE.md', project: 'Wine Finder' },
{ path: '/root/Projects/Letsbegin/CLAUDE.md', project: 'Letsbegin / Ralph' },
{ path: '/root/Projects/Norma/CLAUDE.md', project: 'Norma Intelligence' },
{ path: '/root/Projects/jill-website/CLAUDE.md', project: 'Jill Website (Nosara)' },
{ path: '/root/Projects/Sascha/CLAUDE.md', project: 'Sascha Portfolio' },
{ path: '/root/Projects/SmokeShop/CLAUDE.md', project: 'SmokeShop' },
{ path: '/root/Projects/Grant/CLAUDE.md', project: 'Grant Fundraiser' },
{ path: '/root/Projects/Freddy/CLAUDE.md', project: 'Freddy Fundraiser' },
{ path: '/root/Projects/Patty/CLAUDE.md', project: 'Patty Petitions' },
{ path: '/root/Projects/angels-flowers/CLAUDE.md', project: 'Angels Flowers' },
{ path: '/root/Projects/wine-finder-next/CLAUDE.md', project: 'Wine Finder Next' },
{ path: '/root/Projects/B_Version_1/.claude/CLAUDE.md', project: 'Dear Bubbe v1' },
{ path: '/root/Projects/Letsbegin/.claude/CLAUDE.md', project: 'GoodQuestion Ralph' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Agents/CLAUDE.md', project: 'DW-Agents (sub)' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Websites/CLAUDE.md', project: 'DW Websites' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Programming/CLAUDE.md', project: 'DW Programming' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Programming/shopifyskutoblog/CLAUDE.md', project: 'DW SKU to Blog' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Programming/ImportNewSkufromURL/CLAUDE.md', project: 'DW URL Import' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-MCP/CLAUDE.md', project: 'DW MCP' },
{ path: '/root/Projects/Designer-Wallcoverings/DW-Agents/dw-agents/CLAUDE.md', project: 'DW-Agents Core' },
{ path: '/root/WebsitesMisc/WineFinder/CLAUDE.md', project: 'Wine Finder Misc' },
{ path: '/root/Projects/ClawCoder/docs/superpowers/specs/2026-03-23-clawcoder-profiles-analyzer-design.md', project: 'ClawCoder Spec' },
]
/** Set of allowed paths for security validation */
export const ALLOWED_PATHS = new Set(CLAUDE_MD_PATHS.map(e => e.path))
export interface ClaudeMdFile {
id: string
path: string
project: string
lines: number
hasRules: boolean
lastModified: string
preview: string
exists: boolean
}
function getFileInfo(entry: ClaudeMdEntry): ClaudeMdFile {
const id = Buffer.from(entry.path).toString('base64')
const result: ClaudeMdFile = {
id,
path: entry.path,
project: entry.project,
lines: 0,
hasRules: false,
lastModified: '',
preview: '',
exists: false,
}
try {
const content = fs.readFileSync(entry.path, 'utf-8')
const stat = fs.statSync(entry.path)
result.exists = true
result.lines = content.split('\n').length
result.lastModified = stat.mtime.toISOString()
result.preview = content.substring(0, 100).replace(/\n/g, ' ')
// Check for .claude/rules/ near the file
const dir = path.dirname(entry.path)
const rulesDir = entry.path.includes('.claude/CLAUDE.md')
? path.join(dir, 'rules')
: path.join(dir, '.claude', 'rules')
result.hasRules = fs.existsSync(rulesDir) && fs.statSync(rulesDir).isDirectory()
} catch {
// File doesn't exist or can't be read
result.exists = false
}
return result
}
export async function GET(request: NextRequest) {
const auth = authenticateRequest(request)
if (auth instanceof NextResponse) return auth
const files = CLAUDE_MD_PATHS.map(getFileInfo)
return NextResponse.json(files)
}