UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

90 lines 3.2 kB
/** * Semantic enrichment sidecar store. * * Storage: * .aiwg/index/semantic/<sanitized-id>.json * * Sanitization: replace `/` with `__` and any non-[a-zA-Z0-9._-] with `_`. * Preserves `.md`, `.ts`, etc. extensions readably while keeping filesystem-safe. * * @implements #1204 */ import { createHash, } from 'node:crypto'; import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from 'node:fs'; import { join } from 'node:path'; const SEMANTIC_ROOT_DEFAULT = '.aiwg/index/semantic'; export function resolveSemanticRoot(cwd = process.cwd()) { return join(cwd, SEMANTIC_ROOT_DEFAULT); } export function sanitizeId(id) { return id.replace(/\//g, '__').replace(/[^a-zA-Z0-9._\-]/g, '_'); } function entryPath(root, id) { return join(root, `${sanitizeId(id)}.json`); } export function has(root, id) { return existsSync(entryPath(root, id)); } export function get(root, id) { const file = entryPath(root, id); if (!existsSync(file)) throw new Error(`No enrichment for: ${id}`); return JSON.parse(readFileSync(file, 'utf-8')); } export function put(root, id, fields) { mkdirSync(root, { recursive: true }); const file = entryPath(root, id); writeFileSync(file, JSON.stringify(fields, null, 2), 'utf-8'); return file; } export function remove(root, id) { const file = entryPath(root, id); if (!existsSync(file)) return false; rmSync(file); return true; } /** Drop ALL enrichment data — implements `aiwg index enrich --reset`. */ export function reset(root) { if (!existsSync(root)) return { removed: 0 }; const before = readdirSync(root).filter((f) => f.endsWith('.json')); rmSync(root, { recursive: true, force: true }); return { removed: before.length }; } /** Compute the content hash that would be stored as `enrichedHash`. */ export function computeContentHash(content) { return createHash('sha256').update(content).digest('hex'); } /** List all enrichment entries with staleness flag (caller supplies current hashes). */ export function list(root, currentHashes, now = new Date()) { if (!existsSync(root)) return []; const out = []; for (const file of readdirSync(root)) { if (!file.endsWith('.json')) continue; let fields; try { fields = JSON.parse(readFileSync(join(root, file), 'utf-8')); } catch { continue; } const id = file.replace(/\.json$/, '').replace(/__/g, '/'); const ageDays = Math.max(0, Math.floor((now.getTime() - new Date(fields.enrichedAt).getTime()) / 86_400_000)); const currentHash = currentHashes[id]; const isStale = currentHash !== undefined && currentHash !== fields.enrichedHash; out.push({ artifactId: id, enrichedAt: fields.enrichedAt, enrichedHash: fields.enrichedHash, symbolCount: fields.declaredSymbols.length, citationCount: fields.citations.length, ageDays, isStale, }); } return out.sort((a, b) => a.artifactId.localeCompare(b.artifactId)); } //# sourceMappingURL=store.js.map