@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
77 lines (64 loc) • 1.99 kB
JavaScript
/**
* Doc Ingest Hook
*
* Fires on PostToolUse when WebFetch is used. If the fetched URL looks like
* documentation (docs.*, /api/, /reference/, /guide/), auto-ingests it into
* the project wiki via stackmemory wiki ingest.
*
* Lightweight: only runs if .stackmemory/config.yaml has obsidian.vaultPath.
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// Doc URL patterns
const DOC_PATTERNS = [
/^https?:\/\/docs\./i,
/^https?:\/\/[^/]+\/docs\//i,
/^https?:\/\/[^/]+\/api\//i,
/^https?:\/\/[^/]+\/reference\//i,
/^https?:\/\/[^/]+\/guide/i,
/^https?:\/\/[^/]+\/tutorial/i,
/^https?:\/\/developer\./i,
];
// Rate limit: max 1 ingest per URL per session
const seen = new Set();
function main() {
try {
// Only fire on WebFetch tool
const toolName = process.env.TOOL_NAME || '';
if (toolName !== 'WebFetch') return;
// Check wiki is configured
const configPath = path.join(process.cwd(), '.stackmemory', 'config.yaml');
if (!fs.existsSync(configPath)) return;
const config = fs.readFileSync(configPath, 'utf-8');
if (!config.includes('vaultPath:')) return;
// Extract URL from tool input
const input = process.env.TOOL_INPUT || '';
let url;
try {
const parsed = JSON.parse(input);
url = parsed.url;
} catch {
// Try regex fallback
const match = input.match(/https?:\/\/[^\s"']+/);
url = match ? match[0] : null;
}
if (!url) return;
// Check if URL matches doc patterns
const isDoc = DOC_PATTERNS.some((p) => p.test(url));
if (!isDoc) return;
// Dedupe per session
const host = new URL(url).hostname;
if (seen.has(host)) return;
seen.add(host);
// Run ingest (fire-and-forget, max 1 page for auto-ingest)
execSync(`stackmemory wiki ingest "${url}" -n 5`, {
timeout: 15000,
stdio: 'ignore',
});
} catch {
// Silent fail
}
}
main();