mirror of
https://github.com/kochetkov-ma/claude-brewcode.git
synced 2026-09-14 20:16:41 +08:00
ea944a32fe
- New /brewdoc:docsync skill: user-run generator installs project-local hooks (track/watch/gate) + config; tracks stale docs by last_updated date; modes init/status/sync/reread/frontmatter/uninstall; confirm-before-sync gate - Removed auto-sync skill, bd-auto-sync-processor agent, auto-sync:* frontmatter tags (~44 files), and skill-creator template injection - Docs updated at all levels (README, MDX, navigation, guide catalog, CLAUDE.md)
69 lines
2.1 KiB
JavaScript
69 lines
2.1 KiB
JavaScript
import { createHash } from 'node:crypto';
|
|
import { appendFileSync, chmodSync, mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
const STATE_DIR = process.env.CODEX_BREWCODE_HOOK_STATE_DIR
|
|
|| process.env.PLUGIN_DATA
|
|
|| path.join(os.tmpdir(), 'codex-brewcode-hooks');
|
|
const PROMPT_INTERVAL = 5;
|
|
const STALE_MS = 24 * 60 * 60 * 1000;
|
|
|
|
export const PROMPT_CONTEXT = [
|
|
'[SKILL?] Check the available Codex skills and invoke every matching skill before acting.',
|
|
'[HINT] Use Codex sub-agent collaboration for substantial independent work.',
|
|
'[ROLE] Coordinate specialized work when delegation is requested; keep simple tasks direct.'
|
|
].join('\n');
|
|
|
|
function sessionKey(sessionId) {
|
|
if (typeof sessionId !== 'string' || !sessionId || sessionId.length > 4096) return null;
|
|
return createHash('sha256').update(sessionId, 'utf8').digest('hex');
|
|
}
|
|
|
|
function ensureStateDir() {
|
|
mkdirSync(STATE_DIR, { recursive: true, mode: 0o700 });
|
|
chmodSync(STATE_DIR, 0o700);
|
|
}
|
|
|
|
function counterPath(key) {
|
|
return path.join(STATE_DIR, `${key}.counter`);
|
|
}
|
|
|
|
function pruneStaleCounters() {
|
|
const cutoff = Date.now() - STALE_MS;
|
|
for (const name of readdirSync(STATE_DIR)) {
|
|
if (!name.endsWith('.counter')) continue;
|
|
const file = path.join(STATE_DIR, name);
|
|
try {
|
|
if (statSync(file).mtimeMs < cutoff) unlinkSync(file);
|
|
} catch {}
|
|
}
|
|
}
|
|
|
|
export function resetPromptCounter(sessionId) {
|
|
const key = sessionKey(sessionId);
|
|
if (!key) return;
|
|
try {
|
|
ensureStateDir();
|
|
const file = counterPath(key);
|
|
writeFileSync(file, '', { mode: 0o600 });
|
|
chmodSync(file, 0o600);
|
|
pruneStaleCounters();
|
|
} catch {}
|
|
}
|
|
|
|
export function promptIsDue(sessionId) {
|
|
const key = sessionKey(sessionId);
|
|
if (!key) return false;
|
|
try {
|
|
ensureStateDir();
|
|
const file = counterPath(key);
|
|
appendFileSync(file, 'x', { mode: 0o600 });
|
|
chmodSync(file, 0o600);
|
|
const count = statSync(file).size;
|
|
return Number.isSafeInteger(count) && count > 0 && count % PROMPT_INTERVAL === 0;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|