- VERSION file as authoritative version source - export.mjs reads VERSION with package.json fallback - bw-ota.ps1 DryRun mode for safe testing - auto-setup.ps1 bumped to v3.2.0 (Phase 8 OTA)
83 lines
3.0 KiB
JavaScript
83 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* token-saver-model-advisor.js · TSE Layer 1 · 2026-04-27
|
||
* UserPromptSubmit · 分析 prompt 复杂度, 建议模型选择
|
||
* 节流: 每会话每复杂度级别仅提醒 1 次
|
||
* 行为: fail-open
|
||
*/
|
||
'use strict';
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const CLAUDE_ROOT = require('./lib/root.js');
|
||
const readStdin = require('./lib/read-stdin.js');
|
||
|
||
const STATE_DIR = path.join(CLAUDE_ROOT, 'session-state');
|
||
const STATE_PATH = path.join(STATE_DIR, 'tse-model-advisor.json');
|
||
|
||
const SIMPLE = [
|
||
/^(查找|搜索|找到?|在哪|哪个文件)/,
|
||
/^(翻译|translate)\b/i, /^(格式化|format)\b/i,
|
||
/^(解释|explain|what is|what does)\b/i,
|
||
/^(列出|list|show)\b/i, /^(帮我看|看一下|看看)/,
|
||
/^(改个?名|rename)\b/i, /^(运行|run|execute)\s+(test|build|lint)\b/i,
|
||
];
|
||
const COMPLEX = [
|
||
/(架构|architecture|设计方案|system design)/i,
|
||
/(全面审计|comprehensive audit|全栈审计)/i,
|
||
/(从零开始|from scratch|end.to.end)/i,
|
||
/(重构整个|refactor the entire|重新设计)/i,
|
||
/(安全审查|security review|红队|red.team)/i,
|
||
/(性能优化方案|performance optimization plan)/i,
|
||
/(对比分析|comparative analysis|trade.off)/i,
|
||
];
|
||
|
||
function classify(p) {
|
||
if (!p || p.length < 3) return null;
|
||
for (const re of SIMPLE) { if (re.test(p.trim())) return 'simple'; }
|
||
for (const re of COMPLEX) { if (re.test(p.trim())) return 'complex'; }
|
||
if (p.trim().length < 25) return 'simple';
|
||
return null;
|
||
}
|
||
|
||
function loadState() {
|
||
try { return fs.existsSync(STATE_PATH) ? JSON.parse(fs.readFileSync(STATE_PATH, 'utf8')) : {}; } catch { return {}; }
|
||
}
|
||
function saveState(s) {
|
||
try {
|
||
if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
|
||
const tmp = STATE_PATH + '.tmp.' + process.pid;
|
||
fs.writeFileSync(tmp, JSON.stringify(s, null, 2), 'utf8');
|
||
fs.renameSync(tmp, STATE_PATH);
|
||
} catch {}
|
||
}
|
||
|
||
(async () => {
|
||
try {
|
||
const hookData = await readStdin();
|
||
const prompt = hookData.prompt || '';
|
||
const sid = hookData.session_id || 'u';
|
||
const level = classify(prompt);
|
||
if (!level) process.exit(0);
|
||
|
||
const state = loadState();
|
||
const ss = state[sid] || { a: {}, ts: Date.now() };
|
||
if (ss.a[level]) process.exit(0);
|
||
ss.a[level] = true; ss.ts = Date.now();
|
||
state[sid] = ss;
|
||
const cutoff = Date.now() - 86400000;
|
||
for (const k of Object.keys(state)) { if (state[k].ts < cutoff) delete state[k]; }
|
||
saveState(state);
|
||
|
||
const msg = level === 'simple'
|
||
? '[TSE·MODEL_ADVISOR] 简单任务检测。如当前是 Opus, 建议 /model sonnet 或 /model haiku 以节省 5 倍额度。子 Agent 请指定 model: "haiku"<22><>'
|
||
: '[TSE·MODEL_ADVISOR] 复杂任务检测。Opus 适合规划阶段。建议: 方案确定后切回 Sonnet 执行, 子 Agent 用 Sonnet/Haiku。';
|
||
|
||
process.stdout.write(JSON.stringify({
|
||
continue: true, suppressOutput: true,
|
||
hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: msg }
|
||
}));
|
||
process.exit(0);
|
||
} catch { process.exit(0); }
|
||
})();
|