import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; function envFlag(value, fallback = false) { const raw = String(value ?? '').trim().toLowerCase(); if (!raw) return fallback; return ['1', 'true', 'yes', 'on'].includes(raw); } export function expandHome(value) { const text = String(value ?? '').trim(); if (!text) return text; if (text.startsWith('~/')) return path.join(os.homedir(), text.slice(2)); if (text === '~') return os.homedir(); return text; } export function cursorExecutorEnabled(env = process.env) { return envFlag(env.MEMIND_CURSOR_EXECUTOR_ENABLED, false); } export function resolvePreferredCodeExecutor(env = process.env) { if (!cursorExecutorEnabled(env)) return 'aider'; if (envFlag(env.MEMIND_AIDER_SKILL_USE_CURSOR, true)) return 'cursor'; return 'aider'; } export function cursorPageTasksDefaultEnabled(env = process.env) { // 已废弃:页面任务是否走 Cursor 由 memindadm「智趣体验通道」白名单控制,不再靠此 env 全员开启。 if (!cursorExecutorEnabled(env)) return false; return envFlag(env.MEMIND_CURSOR_PAGE_TASKS_DEFAULT, false); } /** @deprecated 普通 Agent 编排不再默认走 Cursor;仅页面/问卷/Excel 等代码任务使用 enforcePageGenerationCursorRuntime */ export function cursorAgentTasksDefaultEnabled(env = process.env) { if (!cursorExecutorEnabled(env)) return false; return envFlag(env.MEMIND_CURSOR_AGENT_TASKS_DEFAULT, false); } /** 智趣执行失败时回退 DeepSeek / Goose 会话 */ export function cursorDeepseekFallbackEnabled(env = process.env) { return envFlag(env.MEMIND_CURSOR_DEEPSEEK_FALLBACK, false); } export function resolveCursorAgentBin(env = process.env) { const configured = expandHome( env.MEMIND_CURSOR_EXECUTOR_AGENT_BIN ?? env.MEMIND_CURSOR_HELP_AGENT_BIN ?? '~/.local/bin/agent', ); if (configured && fs.existsSync(configured)) return configured; return 'agent'; } export function buildCursorExecutorInstruction(instruction) { const base = String(instruction ?? '').trim(); if (!base) return base; return [ base, '', '执行要求:', '- 在当前 MindSpace 工作区(cwd)内完成改动', '- 页面产物写入 public/ 目录下的 .html 文件', '- 直接完成可交付产物,不要等待用户确认', '- 不要执行 git commit、git push、git checkout、git reset、生产发布或远端同步命令', '- 不要修改当前工作区之外的文件', '- 完成后在最终输出中简述改动文件与验证结果', ].join('\n'); } export function buildCursorExecutorLaunchPlan({ cwd, instruction, env = process.env, agentBin = resolveCursorAgentBin(env), } = {}) { const workspace = path.resolve(String(cwd ?? env.MEMIND_CURSOR_EXECUTOR_WORKSPACE ?? process.cwd())); const prompt = buildCursorExecutorInstruction(instruction); if (!prompt) { return { ok: false, executor: 'cursor', message: 'TKMind 智趣执行器需要 instruction', }; } return { ok: true, executor: 'cursor', executorLabel: 'TKMind 智趣', cwd: workspace, command: agentBin, args: [ '--print', '--trust', '--force', '--approve-mcps', '--output-format', 'stream-json', '--workspace', workspace, prompt, ], env: {}, notes: [ 'TKMind 智趣执行器在 MindSpace 工作区 headless 执行。', 'Goose 仍负责对话编排,智趣只负责代码/页面落盘。', ], }; }