metame-cli 1.4.20 → 1.4.21
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/README.md
CHANGED
|
@@ -363,6 +363,7 @@ All agents share your cognitive profile (`~/.claude_profile.yaml`) — they all
|
|
|
363
363
|
|
|
364
364
|
| Command | Action |
|
|
365
365
|
|---------|--------|
|
|
366
|
+
| `/continue` | Sync to computer's current work (session + directory) |
|
|
366
367
|
| `/last` | Resume most recent session |
|
|
367
368
|
| `/new` | Start new session (project picker) |
|
|
368
369
|
| `/resume` | Pick from session list |
|
package/package.json
CHANGED
|
@@ -1000,10 +1000,6 @@ Reply with ONLY the name, nothing else. Examples: 插件开发, API重构, Bug
|
|
|
1000
1000
|
try { await bot.sendMessage(chatId, `Error: ${(retry.error || '').slice(0, 200)}`); } catch { /* */ }
|
|
1001
1001
|
return { ok: false, error: retry.error || errMsg };
|
|
1002
1002
|
}
|
|
1003
|
-
} else if (errMsg === 'Stopped by user' && messageQueue.has(chatId)) {
|
|
1004
|
-
// Interrupted by message queue — suppress error, queue timer will handle it
|
|
1005
|
-
log('INFO', `Task interrupted by new message for ${chatId}`);
|
|
1006
|
-
return { ok: false, error: errMsg, interrupted: true };
|
|
1007
1003
|
} else {
|
|
1008
1004
|
// Auto-fallback: if custom provider/model fails, revert to anthropic + opus
|
|
1009
1005
|
const activeProv = providerMod ? providerMod.getActiveName() : 'anthropic';
|
|
@@ -539,6 +539,7 @@ function createCommandRouter(deps) {
|
|
|
539
539
|
'📱 手机端 Claude Code',
|
|
540
540
|
'',
|
|
541
541
|
'⚡ 快速同步电脑工作:',
|
|
542
|
+
'/continue — 接续电脑正在做的工作',
|
|
542
543
|
'/last — 继续电脑上最近的对话',
|
|
543
544
|
'/cd last — 切到电脑最近的项目目录',
|
|
544
545
|
'',
|
|
@@ -575,43 +576,21 @@ function createCommandRouter(deps) {
|
|
|
575
576
|
}
|
|
576
577
|
|
|
577
578
|
// --- Natural language → Claude Code session ---
|
|
578
|
-
// If a task is running:
|
|
579
|
+
// If a task is running: queue message, DON'T kill — will be sent as follow-up after completion
|
|
579
580
|
if (activeProcesses.has(chatId)) {
|
|
580
581
|
const isFirst = !messageQueue.has(chatId);
|
|
581
582
|
if (isFirst) {
|
|
582
|
-
messageQueue.set(chatId, { messages: []
|
|
583
|
+
messageQueue.set(chatId, { messages: [] });
|
|
583
584
|
}
|
|
584
585
|
const q = messageQueue.get(chatId);
|
|
586
|
+
if (q.messages.length >= 10) {
|
|
587
|
+
await bot.sendMessage(chatId, '⚠️ 排队已满(10条),请等当前任务完成');
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
585
590
|
q.messages.push(text);
|
|
586
|
-
// Only notify once (first message), subsequent ones silently queue
|
|
587
591
|
if (isFirst) {
|
|
588
|
-
await bot.sendMessage(chatId, '📝
|
|
589
|
-
}
|
|
590
|
-
// Interrupt the running Claude process
|
|
591
|
-
const proc = activeProcesses.get(chatId);
|
|
592
|
-
if (proc && proc.child && !proc.aborted) {
|
|
593
|
-
proc.aborted = true;
|
|
594
|
-
try { process.kill(-proc.child.pid, 'SIGINT'); } catch { proc.child.kill('SIGINT'); }
|
|
592
|
+
await bot.sendMessage(chatId, '📝 收到,完成后继续处理');
|
|
595
593
|
}
|
|
596
|
-
// Debounce: wait 5s for more messages before processing
|
|
597
|
-
if (q.timer) clearTimeout(q.timer);
|
|
598
|
-
q.timer = setTimeout(async () => {
|
|
599
|
-
// Wait for active process to fully exit (up to 10s)
|
|
600
|
-
for (let i = 0; i < 20 && activeProcesses.has(chatId); i++) {
|
|
601
|
-
await sleep(500);
|
|
602
|
-
}
|
|
603
|
-
const msgs = q.messages.splice(0);
|
|
604
|
-
messageQueue.delete(chatId);
|
|
605
|
-
if (msgs.length === 0) return;
|
|
606
|
-
const combined = msgs.join('\n');
|
|
607
|
-
log('INFO', `Processing ${msgs.length} queued message(s) for ${chatId}`);
|
|
608
|
-
resetCooldown(chatId); // queued msgs already waited, skip cooldown
|
|
609
|
-
try {
|
|
610
|
-
await handleCommand(bot, chatId, combined, config, executeTaskByName);
|
|
611
|
-
} catch (e) {
|
|
612
|
-
log('ERROR', `Queue dispatch failed: ${e.message}`);
|
|
613
|
-
}
|
|
614
|
-
}, 5000);
|
|
615
594
|
return;
|
|
616
595
|
}
|
|
617
596
|
// Strict mode: chats with a fixed agent in chat_agent_map must not cross-dispatch
|
|
@@ -653,8 +632,8 @@ function createCommandRouter(deps) {
|
|
|
653
632
|
}
|
|
654
633
|
const claudeResult = await askClaude(bot, chatId, text, config, readOnly);
|
|
655
634
|
const claudeFailed = !!(claudeResult && claudeResult.ok === false);
|
|
656
|
-
const
|
|
657
|
-
if (claudeFailed && !
|
|
635
|
+
const claudeAborted = !!(claudeResult && claudeResult.error === 'Stopped by user');
|
|
636
|
+
if (claudeFailed && !claudeAborted && !macLocalFirst && macFallbackEnabled && allowLocalMacControl) {
|
|
658
637
|
const fallbackHandled = await tryHandleMacNaturalLanguageIntent(bot, chatId, text, config, {
|
|
659
638
|
source: 'claude-fallback',
|
|
660
639
|
safeOnly: true,
|
|
@@ -664,6 +643,20 @@ function createCommandRouter(deps) {
|
|
|
664
643
|
log('WARN', `Claude-first mac fallback handled for ${String(chatId).slice(-8)} (mode=${macControlMode})`);
|
|
665
644
|
}
|
|
666
645
|
}
|
|
646
|
+
|
|
647
|
+
// Process queued messages as follow-up in the same session (no kill, no context loss)
|
|
648
|
+
// Use while-loop instead of recursion to avoid unbounded stack growth
|
|
649
|
+
while (messageQueue.has(chatId)) {
|
|
650
|
+
const q = messageQueue.get(chatId);
|
|
651
|
+
const msgs = q.messages.splice(0);
|
|
652
|
+
messageQueue.delete(chatId);
|
|
653
|
+
if (msgs.length === 0) break;
|
|
654
|
+
const combined = msgs.join('\n');
|
|
655
|
+
log('INFO', `Follow-up: processing ${msgs.length} queued message(s) for ${chatId}`);
|
|
656
|
+
resetCooldown(chatId);
|
|
657
|
+
const followUp = await askClaude(bot, chatId, combined, config, readOnly);
|
|
658
|
+
if (followUp && followUp.error === 'Stopped by user') break;
|
|
659
|
+
}
|
|
667
660
|
}
|
|
668
661
|
|
|
669
662
|
return { handleCommand };
|
|
@@ -308,8 +308,14 @@ function createSessionCommandHandler(deps) {
|
|
|
308
308
|
return true;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
// /continue — alias for /cd last (sync to computer's latest session)
|
|
312
|
+
if (text === '/continue') {
|
|
313
|
+
// Reuse /cd last logic below
|
|
314
|
+
// fall through with newCwd = 'last'
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (text === '/continue' || text === '/cd' || text.startsWith('/cd ')) {
|
|
318
|
+
let newCwd = text === '/continue' ? 'last' : expandPath(text.slice(3).trim());
|
|
313
319
|
if (!newCwd) {
|
|
314
320
|
await sendDirPicker(bot, chatId, 'cd', 'Switch workdir:');
|
|
315
321
|
return true;
|
|
@@ -333,7 +339,6 @@ function createSessionCommandHandler(deps) {
|
|
|
333
339
|
const name = target.customTitle || target.summary || '';
|
|
334
340
|
const label = name ? name.slice(0, 40) : target.sessionId.slice(0, 8);
|
|
335
341
|
await bot.sendMessage(chatId, `🔄 Synced to: ${label}\n📁 ${path.basename(target.projectPath)}`);
|
|
336
|
-
await sendDirListing(bot, chatId, target.projectPath, null);
|
|
337
342
|
return true;
|
|
338
343
|
}
|
|
339
344
|
await bot.sendMessage(chatId, 'No recent session found.');
|