metame-cli 1.4.7 → 1.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -627,38 +627,23 @@ const GLOBAL_MARKER_END = '<!-- METAME-GLOBAL:END -->';
627
627
  // Sections to inject (each only injected if not already present in user's manual content)
628
628
  const GLOBAL_SECTIONS = [
629
629
  { detect: /dispatch_to|Agent Dispatch/i, text: [
630
- '## Agent Dispatch(跨 Agent 通信)',
631
- '',
632
- '识别到"告诉X/让X/通知X"且 X 为已知 Agent 昵称时,直接调用 dispatch,无需确认:',
633
- '',
634
- '```bash',
635
- '~/.metame/bin/dispatch_to <project_key> "内容"',
636
- '```',
637
- '',
638
- '手机端用户可发送 `/dispatch <project_key> <消息>` 跨 agent 通信。',
639
- '',
630
+ '## Agent Dispatch',
631
+ '"告诉X/让X"→ `~/.metame/bin/dispatch_to <project_key> "内容"`,手机端 `/dispatch <key> <消息>`。',
640
632
  '新增 Agent:`/agent bind <名称> <工作目录>`',
641
633
  ]},
642
634
  { detect: /memory-search\.js|跨会话记忆/i, text: [
643
635
  '## 跨会话记忆',
644
- '',
645
- '搜索命令(支持多关键词并行 + QMD 向量混合搜索):',
646
- '```bash',
647
- 'node ~/.metame/memory-search.js "关键词1" "keyword2" "函数名"',
648
- '# 只搜事实:--facts;只搜会话:--sessions;最近会话:--recent',
649
- '```',
650
- '',
651
- '**搜索策略**:一次传 3-4 个关键词(中文描述 + 英文术语 + 函数/配置名)。',
652
- '**触发场景**:用户说"上次/之前/以前"、涉及已有决策/踩过的坑、排查"之前能用现在不行"。',
636
+ '用户提"上次/之前"时搜索:`node ~/.metame/memory-search.js "关键词1" "keyword2"`',
637
+ '一次传 3-4 个关键词(中文+英文+函数名),`--facts` 只搜事实,`--sessions` 只搜会话。',
653
638
  ]},
654
639
  { detect: /skill-manager|Skills.*技能/i, text: [
655
- '## Skills(技能扩展)',
656
- '',
657
- '遇到任何能力不足、工具缺失、任务失败,**第一步永远是查阅 skill-manager**:',
658
- '```bash',
659
- 'cat ~/.claude/skills/skill-manager/SKILL.md',
660
- '```',
661
- 'skill-manager 掌握完整技能清单和决策流程(调用现有 → 商城安装 → GitHub 包装 → 从零创建 → 任务完成后进化)。不要绕过它自己猜。',
640
+ '## Skills',
641
+ '能力不足/工具缺失/任务失败 → 先查 `cat ~/.claude/skills/skill-manager/SKILL.md`,不要自己猜。',
642
+ ]},
643
+ { detect: /\[\[FILE:|手机端文件/i, text: [
644
+ '## 手机端文件交互',
645
+ '**收**:用户发图片/文件自动存到 `upload/`,用 Read 查看。',
646
+ '**发**:回复末尾加 `[[FILE:/absolute/path]]`,daemon 自动发手机。不要读内容再复述。',
662
647
  ]},
663
648
  ];
664
649
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metame-cli",
3
- "version": "1.4.7",
3
+ "version": "1.4.10",
4
4
  "description": "The Cognitive Profile Layer for Claude Code. Knows how you think, not just what you said.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,4 +33,4 @@
33
33
  "engines": {
34
34
  "node": ">=22.5"
35
35
  }
36
- }
36
+ }
package/scripts/daemon.js CHANGED
@@ -29,6 +29,19 @@ const DISPATCH_DIR = path.join(METAME_DIR, 'dispatch');
29
29
  const DISPATCH_LOG = path.join(DISPATCH_DIR, 'dispatch-log.jsonl');
30
30
  const SOCK_PATH = path.join(METAME_DIR, 'daemon.sock');
31
31
 
32
+ // Resolve claude binary path (daemon may not inherit user's full PATH)
33
+ const CLAUDE_BIN = (() => {
34
+ const candidates = [
35
+ path.join(HOME, '.local', 'bin', 'claude'), // npm global (Linux/Mac)
36
+ path.join(HOME, '.npm-global', 'bin', 'claude'), // custom npm prefix
37
+ '/usr/local/bin/claude',
38
+ '/opt/homebrew/bin/claude',
39
+ ];
40
+ try { return execSync('which claude 2>/dev/null', { encoding: 'utf8' }).trim(); } catch {}
41
+ for (const p of candidates) { if (fs.existsSync(p)) return p; }
42
+ return 'claude'; // fallback: hope it's in PATH
43
+ })();
44
+
32
45
  // Skill evolution module (hot path + cold path)
33
46
  let skillEvolution = null;
34
47
  try { skillEvolution = require('./skill-evolution'); } catch { /* graceful fallback */ }
@@ -395,7 +408,7 @@ function executeTask(task, config) {
395
408
  const asyncEnv = { ...process.env, ...getDaemonProviderEnv(), CLAUDECODE: undefined };
396
409
 
397
410
  return new Promise((resolve) => {
398
- const child = spawn('claude', asyncArgs, {
411
+ const child = spawn(CLAUDE_BIN, asyncArgs, {
399
412
  cwd: cwd || undefined,
400
413
  stdio: ['pipe', 'pipe', 'pipe'],
401
414
  detached: true, // own process group — kills sub-agents on timeout too
@@ -1129,7 +1142,7 @@ async function startTelegramBridge(config, executeTaskByName) {
1129
1142
  const isBindCmd = trimmedText && (trimmedText.startsWith('/bind') || trimmedText.startsWith('/agent bind') || trimmedText.startsWith('/agent new'));
1130
1143
  if (!allowedIds.includes(chatId) && !isBindCmd) {
1131
1144
  log('WARN', `Rejected message from unauthorized chat: ${chatId}`);
1132
- bot.sendMessage(chatId, `⚠️ This chat (ID: ${chatId}) is not authorized.\n\nTo get started, send:\n/agent bind personal ~/\n\nThis will register this chat and bind it to your home directory.`).catch(() => {});
1145
+ bot.sendMessage(chatId, `⚠️ This chat is not authorized.\n\nCopy and send this command to register:\n\n/agent bind personal`).catch(() => {});
1133
1146
  continue;
1134
1147
  }
1135
1148
 
@@ -3709,7 +3722,7 @@ Reply with ONLY the name, nothing else. Examples: 插件开发, API重构, Bug
3709
3722
  */
3710
3723
  function spawnClaudeAsync(args, input, cwd, timeoutMs = 300000) {
3711
3724
  return new Promise((resolve) => {
3712
- const child = spawn('claude', args, {
3725
+ const child = spawn(CLAUDE_BIN, args, {
3713
3726
  cwd,
3714
3727
  stdio: ['pipe', 'pipe', 'pipe'],
3715
3728
  env: { ...process.env, ...getActiveProviderEnv(), CLAUDECODE: undefined },
@@ -4005,7 +4018,7 @@ function spawnClaudeStreaming(args, input, cwd, onStatus, timeoutMs = 600000, ch
4005
4018
  // Add stream-json output format (requires --verbose)
4006
4019
  const streamArgs = [...args, '--output-format', 'stream-json', '--verbose'];
4007
4020
 
4008
- const child = spawn('claude', streamArgs, {
4021
+ const child = spawn(CLAUDE_BIN, streamArgs, {
4009
4022
  cwd,
4010
4023
  stdio: ['pipe', 'pipe', 'pipe'],
4011
4024
  detached: true, // Create new process group so killing -pid kills all sub-agents too
@@ -4232,7 +4245,7 @@ async function askClaude(bot, chatId, prompt, config, readOnly = false) {
4232
4245
  // Send a single status message, updated in-place, deleted on completion
4233
4246
  let statusMsgId = null;
4234
4247
  try {
4235
- const msg = await bot.sendMessage(chatId, '🤔');
4248
+ const msg = await (bot.sendMarkdown ? bot.sendMarkdown(chatId, '🤔') : bot.sendMessage(chatId, '🤔'));
4236
4249
  if (msg && msg.message_id) statusMsgId = msg.message_id;
4237
4250
  } catch (e) {
4238
4251
  log('ERROR', `Failed to send ack to ${chatId}: ${e.message}`);
@@ -4613,7 +4626,7 @@ async function startFeishuBridge(config, executeTaskByName) {
4613
4626
  const isBindCmd = trimmedText && (trimmedText.startsWith('/bind') || trimmedText.startsWith('/agent bind') || trimmedText.startsWith('/agent new'));
4614
4627
  if (!allowedIds.includes(chatId) && !isBindCmd) {
4615
4628
  log('WARN', `Feishu: rejected message from ${chatId}`);
4616
- (bot.sendMarkdown ? bot.sendMarkdown(chatId, `⚠️ **此会话未授权**\n\n会话 ID: \`${chatId}\`\n\n发送以下命令注册:\n\`/agent bind personal ~/\`\n\n这会将此会话绑定到你的主目录。`) : bot.sendMessage(chatId, `⚠️ 此会话 (ID: ${chatId}) 未授权。\n\n发送以下命令注册:\n/agent bind personal ~/\n\n这会将此会话绑定到你的主目录。`)).catch(() => {});
4629
+ (bot.sendMarkdown ? bot.sendMarkdown(chatId, `⚠️ **此会话未授权**\n\n复制下方命令发送即可注册:\n\`\`\`\n/agent bind personal\n\`\`\``) : bot.sendMessage(chatId, `⚠️ 此会话未授权。\n\n复制下方命令发送即可注册:\n/agent bind personal`)).catch(() => {});
4617
4630
  return;
4618
4631
  }
4619
4632
 
@@ -100,9 +100,12 @@ function createBot(config) {
100
100
  async editMessage(chatId, messageId, text) {
101
101
  if (this._editBroken) return false;
102
102
  try {
103
+ // Feishu patch API only works on card (interactive) messages
104
+ // Update card content with markdown element
105
+ const card = { schema: '2.0', body: { elements: [{ tag: 'markdown', content: text }] } };
103
106
  await withTimeout(client.im.message.patch({
104
107
  path: { message_id: messageId },
105
- data: { content: JSON.stringify({ text }) },
108
+ data: { content: JSON.stringify(card) },
106
109
  }));
107
110
  return true;
108
111
  } catch (e) {