95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
function readStdin() {
|
|
return fs.readFileSync(0, 'utf8');
|
|
}
|
|
|
|
function isInside(parent, target) {
|
|
const relative = path.relative(parent, target);
|
|
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
}
|
|
|
|
function collectPathStrings(value, key = '', output = []) {
|
|
if (value == null) {
|
|
return output;
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
if (/path|file/i.test(key)) {
|
|
output.push(value);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
value.forEach((item) => collectPathStrings(item, key, output));
|
|
return output;
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
Object.entries(value).forEach(([childKey, childValue]) => {
|
|
collectPathStrings(childValue, childKey, output);
|
|
});
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
function resolveCandidate(candidate, rootDir) {
|
|
const cleaned = String(candidate).trim().replace(/^["']|["']$/g, '');
|
|
if (!cleaned) {
|
|
return null;
|
|
}
|
|
|
|
return path.isAbsolute(cleaned)
|
|
? path.resolve(cleaned)
|
|
: path.resolve(rootDir, cleaned);
|
|
}
|
|
|
|
try {
|
|
const input = JSON.parse(readStdin() || '{}');
|
|
if (input.tool_response && input.tool_response.error) {
|
|
process.exit(0);
|
|
}
|
|
|
|
const projectDir = process.env.GEMINI_PROJECT_DIR || input.cwd || process.cwd();
|
|
const wikiDir = path.resolve(projectDir, 'wiki');
|
|
const outputDir = path.resolve(projectDir, 'output');
|
|
const indexPath = path.resolve(wikiDir, 'index.md');
|
|
const logPath = path.resolve(wikiDir, 'log.md');
|
|
const candidates = collectPathStrings(input.tool_input || {});
|
|
const targets = candidates
|
|
.map((candidate) => resolveCandidate(candidate, projectDir))
|
|
.filter(Boolean);
|
|
|
|
const wikiTargets = targets.filter((target) => isInside(wikiDir, target));
|
|
const outputTargets = targets.filter((target) => isInside(outputDir, target));
|
|
|
|
if (wikiTargets.some((target) => target !== indexPath && target !== logPath)) {
|
|
process.stdout.write(JSON.stringify({
|
|
suppressOutput: true,
|
|
hookSpecificOutput: {
|
|
additionalContext:
|
|
'wiki/ 페이지를 수정했다. 이 턴이 끝나기 전에 YAML frontmatter(title, tags, sources, created, updated), [[wikilink]], wiki/index.md 한 줄 항목, wiki/log.md 변경 기록이 모두 반영되었는지 확인하라.'
|
|
}
|
|
}));
|
|
process.exit(0);
|
|
}
|
|
|
|
if (outputTargets.length > 0) {
|
|
process.stdout.write(JSON.stringify({
|
|
suppressOutput: true,
|
|
hookSpecificOutput: {
|
|
additionalContext:
|
|
'output/ 결과물은 wiki/ 페이지에 근거해야 한다. 가능하면 사용한 핵심 wiki 페이지를 본문 또는 말미에 남기고, raw/ 직접 해석에 의존하지 마라.'
|
|
}
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
process.stderr.write(String(error));
|
|
process.exit(1);
|
|
}
|