|
12 | 12 | * This bridge never spawns it; when it is down, tools return a helpful error. |
13 | 13 | */ |
14 | 14 |
|
15 | | -import { readFileSync } from 'node:fs'; |
| 15 | +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; |
16 | 16 | import { homedir } from 'node:os'; |
17 | 17 | import { join } from 'node:path'; |
18 | 18 | import { createInterface } from 'node:readline'; |
19 | 19 |
|
20 | 20 | const SERVER = { name: 'evolver-proxy', version: '0.1.0' }; |
21 | 21 | const DEFAULT_PROTOCOL = '2025-06-18'; |
| 22 | +const CODEX_GUIDANCE_START = '<!-- evolver-codex-guidance:start -->'; |
| 23 | +const CODEX_GUIDANCE_END = '<!-- evolver-codex-guidance:end -->'; |
22 | 24 |
|
23 | 25 | function log(...a) { process.stderr.write('[evolver-proxy-mcp] ' + a.join(' ') + '\n'); } |
24 | 26 |
|
| 27 | +function codexGuidanceSection(language) { |
| 28 | + if (language === 'zh') { |
| 29 | + return `${CODEX_GUIDANCE_START} |
| 30 | +## Evolver 全局指令 |
| 31 | +
|
| 32 | +- 开始实质性工作前,先调用 \`evolver_status\`;如果 Proxy 正常,再用简短任务信号调用 \`evolver_search_assets\`,优先复用已有 Genes/Capsules。 |
| 33 | +- 如果召回到相关资产,用 \`evolver_fetch_asset\` 获取候选内容;只采用与当前任务匹配、可验证的部分。 |
| 34 | +- 任务结束后,只有当产出确实可复用为 Gene/Capsule 时才调用 \`evolver_publish_asset\`;普通本地结果由已安装的 Evolver hooks 在 Stop 阶段记录。 |
| 35 | +- 如果 MCP 工具不可用、Proxy token 过期或 transport 断开,重启/启动 Evolver Proxy,或在插件/Proxy 更新后开启一个新的 Codex 线程。 |
| 36 | +${CODEX_GUIDANCE_END}`; |
| 37 | + } |
| 38 | + |
| 39 | + return `${CODEX_GUIDANCE_START} |
| 40 | +## Evolver Global Guidance |
| 41 | +
|
| 42 | +- Before substantive work, call \`evolver_status\`; if the Proxy is running, call \`evolver_search_assets\` with concise task signals before designing or editing. |
| 43 | +- If assets match, call \`evolver_fetch_asset\` for promising IDs and apply only the relevant, verifiable guidance. |
| 44 | +- At task end, call \`evolver_publish_asset\` only for genuinely reusable Genes/Capsules; ordinary local outcomes are recorded by installed Evolver hooks when present. |
| 45 | +- If MCP tools are unavailable, the Proxy token is stale, or transport is closed, start/restart the Evolver Proxy or open a new Codex thread after plugin/Proxy changes. |
| 46 | +${CODEX_GUIDANCE_END}`; |
| 47 | +} |
| 48 | + |
| 49 | +function updateCodexGuidanceContent(before, section) { |
| 50 | + const start = before.indexOf(CODEX_GUIDANCE_START); |
| 51 | + const end = before.indexOf(CODEX_GUIDANCE_END); |
| 52 | + if ((start === -1) !== (end === -1) || (start !== -1 && end < start)) { |
| 53 | + return { |
| 54 | + ok: false, |
| 55 | + error: `Malformed Evolver guidance markers in ~/.codex/AGENTS.md. Expected both ${CODEX_GUIDANCE_START} and ${CODEX_GUIDANCE_END}.`, |
| 56 | + }; |
| 57 | + } |
| 58 | + |
| 59 | + if (start !== -1) { |
| 60 | + const replaceEnd = end + CODEX_GUIDANCE_END.length; |
| 61 | + const next = before.slice(0, start) + section + before.slice(replaceEnd); |
| 62 | + return { ok: true, content: next, action: 'updated' }; |
| 63 | + } |
| 64 | + |
| 65 | + const trimmed = before.trimEnd(); |
| 66 | + const prefix = trimmed ? trimmed + '\n\n' : ''; |
| 67 | + return { ok: true, content: prefix + section + '\n', action: 'inserted' }; |
| 68 | +} |
| 69 | + |
| 70 | +function installCodexGuidance(args = {}) { |
| 71 | + const language = args.language === 'zh' ? 'zh' : 'en'; |
| 72 | + const dryRun = args.dry_run === true; |
| 73 | + const codexDir = join(homedir(), '.codex'); |
| 74 | + const agentsPath = join(codexDir, 'AGENTS.md'); |
| 75 | + let before = ''; |
| 76 | + let existed = true; |
| 77 | + try { |
| 78 | + before = readFileSync(agentsPath, 'utf8'); |
| 79 | + } catch { |
| 80 | + existed = false; |
| 81 | + } |
| 82 | + |
| 83 | + const section = codexGuidanceSection(language); |
| 84 | + const updated = updateCodexGuidanceContent(before, section); |
| 85 | + if (!updated.ok) return { ok: false, error: updated.error }; |
| 86 | + |
| 87 | + const content = updated.content.endsWith('\n') ? updated.content : updated.content + '\n'; |
| 88 | + const changed = content !== before; |
| 89 | + if (dryRun) { |
| 90 | + return { |
| 91 | + ok: true, |
| 92 | + data: { |
| 93 | + dry_run: true, |
| 94 | + changed, |
| 95 | + action: changed ? updated.action : 'unchanged', |
| 96 | + agents_path: agentsPath, |
| 97 | + language, |
| 98 | + section, |
| 99 | + }, |
| 100 | + }; |
| 101 | + } |
| 102 | + |
| 103 | + if (!changed) { |
| 104 | + return { |
| 105 | + ok: true, |
| 106 | + data: { |
| 107 | + changed: false, |
| 108 | + action: 'unchanged', |
| 109 | + agents_path: agentsPath, |
| 110 | + language, |
| 111 | + }, |
| 112 | + }; |
| 113 | + } |
| 114 | + |
| 115 | + mkdirSync(codexDir, { recursive: true }); |
| 116 | + let backupPath = null; |
| 117 | + if (existed) { |
| 118 | + backupPath = agentsPath + `.bak.${Math.floor(Date.now() / 1000)}`; |
| 119 | + writeFileSync(backupPath, before, 'utf8'); |
| 120 | + } |
| 121 | + writeFileSync(agentsPath, content, 'utf8'); |
| 122 | + return { |
| 123 | + ok: true, |
| 124 | + data: { |
| 125 | + changed: true, |
| 126 | + action: updated.action, |
| 127 | + agents_path: agentsPath, |
| 128 | + backup_path: backupPath, |
| 129 | + language, |
| 130 | + restart_recommended: true, |
| 131 | + }, |
| 132 | + }; |
| 133 | +} |
| 134 | + |
25 | 135 | /** |
26 | 136 | * Resolve the live Proxy connection. ~/.evolver/settings.json is authoritative: |
27 | 137 | * the running Proxy writes both its url and a per-instance auth token there. |
@@ -152,6 +262,19 @@ const TOOLS = [ |
152 | 262 | }, |
153 | 263 | handler: (a) => proxyFetch('POST', '/mailbox/poll', { type: a.type, limit: a.limit || 10 }), |
154 | 264 | }, |
| 265 | + { |
| 266 | + name: 'evolver_install_codex_guidance', |
| 267 | + description: 'Install or update the global Codex ~/.codex/AGENTS.md Evolver guidance section. Creates a timestamped backup before writing. Use only when the user explicitly wants global Codex guidance installed or refreshed.', |
| 268 | + inputSchema: { |
| 269 | + type: 'object', |
| 270 | + properties: { |
| 271 | + language: { type: 'string', enum: ['en', 'zh'], default: 'en', description: 'Language for the injected AGENTS.md section.' }, |
| 272 | + dry_run: { type: 'boolean', default: false, description: 'Preview the section and change action without writing files.' }, |
| 273 | + }, |
| 274 | + additionalProperties: false, |
| 275 | + }, |
| 276 | + handler: installCodexGuidance, |
| 277 | + }, |
155 | 278 | ]; |
156 | 279 |
|
157 | 280 | const TOOL_BY_NAME = Object.fromEntries(TOOLS.map(t => [t.name, t])); |
@@ -185,7 +308,7 @@ async function dispatch(req) { |
185 | 308 | protocolVersion: params?.protocolVersion || DEFAULT_PROTOCOL, |
186 | 309 | capabilities: { tools: {} }, |
187 | 310 | serverInfo: SERVER, |
188 | | - instructions: 'Evolver Proxy bridge. Use evolver_search_assets before substantive work to reuse proven genes/capsules; evolver_status to check the Proxy; evolver_publish_asset to contribute new ones.', |
| 311 | + instructions: 'Evolver Proxy bridge. Use evolver_search_assets before substantive work to reuse proven genes/capsules; evolver_status to check the Proxy; evolver_publish_asset to contribute new ones. Use evolver_install_codex_guidance only when the user explicitly wants global Codex AGENTS.md guidance installed or refreshed.', |
189 | 312 | }); |
190 | 313 | case 'notifications/initialized': |
191 | 314 | case 'initialized': |
|
0 commit comments