mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-26 23:04:38 +03:00
ebd620d262
Харнес-слой поверх пола §1B: ловит правки мимо навыков-мутаторов. - support-guard.mjs (PreToolUse Edit|Write|MultiEdit) — §1A: блокирует сырую правку объекта поставщика «на замке» / read-only конфы; реакция deny|warn|off из .v8-project.json editingAllowedCheck, идентично §1B. - skill-suggester.mjs (PostToolUse Read|Grep|Glob|Edit|Write|MultiEdit) — ненавязчивая подсказка профильного навыка, throttle 1×/сессия/группа, не блокирует; флаг skillSuggester (on|off). - common/: support-state.mjs (порт декодера bin 1:1 из Assert-EditAllowed), project.mjs (реакция из .v8-project.json), object-class.mjs (карта путь→навык с различением cf/cfe и mxl/скд по нюху корня). - test/run.mjs: 38 standalone-тестов на корпусе cfsrc + синтетике. - plugin.json: hooks → ./hooks/hooks.json (авто-загрузка в плагине). §1C (грубый Bash-гейт) отброшен — дублирует §1B, формат bin заморожен. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
68 lines
2.3 KiB
JavaScript
68 lines
2.3 KiB
JavaScript
// project.mjs v1.0 — read reaction mode from .v8-project.json for Claude Code hooks
|
|
// Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
|
//
|
|
// Canonical port of Get-EditMode / _sg_get_edit_mode
|
|
// (reference: .claude/skills/meta-edit/scripts/meta-edit.ps1:181-201, meta-edit.py:50-68).
|
|
// configSrc is matched here ONLY to fetch a per-database override — identically to the
|
|
// in-skill guard §1B, so that a raw Edit and an edit-via-skill behave the same under the
|
|
// same databases[].editingAllowedCheck. Never throws — falls back to the default.
|
|
|
|
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
|
|
const WIN = process.platform === 'win32';
|
|
|
|
function norm(p) {
|
|
let s = resolve(p).replace(/[\\/]+$/, '');
|
|
return WIN ? s.toLowerCase() : s;
|
|
}
|
|
|
|
function findV8Project(startDir) {
|
|
let d = startDir;
|
|
for (let i = 0; i < 20 && d; i++) {
|
|
const pj = join(d, '.v8-project.json');
|
|
if (existsSync(pj)) return pj;
|
|
const parent = dirname(d);
|
|
if (parent === d) break;
|
|
d = parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Generic reader: returns databases[].<key> for the matching configSrc, else global
|
|
// proj.<key>, else fallback. cwd is the hook's stdin cwd; cfgDir is the resolved config root.
|
|
export function getProjectSetting(key, cfgDir, cwd, fallback) {
|
|
try {
|
|
const pj = findV8Project(cwd) || (cfgDir ? findV8Project(cfgDir) : null);
|
|
if (!pj) return fallback;
|
|
let raw = readFileSync(pj, 'utf8');
|
|
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1); // strip BOM
|
|
const proj = JSON.parse(raw);
|
|
if (cfgDir && Array.isArray(proj.databases)) {
|
|
const cfgFull = norm(cfgDir);
|
|
for (const db of proj.databases) {
|
|
if (db && db.configSrc) {
|
|
const src = norm(db.configSrc);
|
|
if (cfgFull === src || cfgFull.startsWith(src + sep)) {
|
|
if (db[key]) return db[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (proj[key]) return proj[key];
|
|
return fallback;
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
// Guard reaction: deny (default) | warn | off.
|
|
export function getEditMode(cfgDir, cwd) {
|
|
return getProjectSetting('editingAllowedCheck', cfgDir, cwd, 'deny');
|
|
}
|
|
|
|
// Suggester switch: on (default) | off.
|
|
export function getSuggesterMode(cfgDir, cwd) {
|
|
return getProjectSetting('skillSuggester', cfgDir, cwd, 'on');
|
|
}
|