metame-cli 1.5.21 → 1.5.23
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 +3 -2
- package/index.js +25 -24
- package/package.json +1 -1
- package/scripts/agent-intent-shared.js +111 -0
- package/scripts/daemon-agent-commands.js +160 -354
- package/scripts/daemon-agent-intent.js +282 -0
- package/scripts/daemon-agent-lifecycle.js +243 -0
- package/scripts/daemon-agent-workflow.js +295 -0
- package/scripts/daemon-bridges.js +18 -5
- package/scripts/daemon-claude-engine.js +74 -75
- package/scripts/daemon-command-router.js +24 -294
- package/scripts/daemon-prompt-context.js +127 -0
- package/scripts/daemon-reactive-lifecycle.js +138 -6
- package/scripts/daemon-session-commands.js +16 -2
- package/scripts/daemon-session-store.js +104 -0
- package/scripts/daemon-team-workflow.js +146 -0
- package/scripts/daemon.js +14 -3
- package/scripts/docs/hook-config.md +41 -21
- package/scripts/docs/maintenance-manual.md +2 -2
- package/scripts/docs/orphan-files-review.md +1 -1
- package/scripts/docs/pointer-map.md +2 -2
- package/scripts/hooks/intent-agent-capability.js +51 -0
- package/scripts/hooks/intent-doc-router.js +23 -11
- package/scripts/hooks/intent-memory-recall.js +1 -3
- package/scripts/hooks/intent-team-dispatch.js +1 -1
- package/scripts/intent-registry.js +78 -14
- package/scripts/ops-mission-queue.js +101 -36
- package/scripts/ops-reactive-bootstrap.js +86 -0
- package/scripts/resolve-yaml.js +3 -0
- package/scripts/runtime-bootstrap.js +77 -0
- package/scripts/hooks/intent-engine.js +0 -75
- package/scripts/hooks/team-context.js +0 -143
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { normalizeEngineName: _normalizeEngine } = require('./daemon-utils');
|
|
4
|
+
const {
|
|
5
|
+
getBoundProject,
|
|
6
|
+
createWorkspaceAgent,
|
|
7
|
+
bindAgentToChat,
|
|
8
|
+
editAgentRole,
|
|
9
|
+
listAgents,
|
|
10
|
+
unbindAgent,
|
|
11
|
+
handleActivateCommand,
|
|
12
|
+
} = require('./daemon-agent-workflow');
|
|
13
|
+
const {
|
|
14
|
+
startNewAgentWizard,
|
|
15
|
+
completeAgentCreation,
|
|
16
|
+
readAgentRolePreview,
|
|
17
|
+
resetAgentRoleSection,
|
|
18
|
+
handleSoulCommand,
|
|
19
|
+
} = require('./daemon-agent-lifecycle');
|
|
20
|
+
const { parseTeamMembers, createTeamWorkspace } = require('./daemon-team-workflow');
|
|
4
21
|
|
|
5
22
|
function createAgentCommandHandler(deps) {
|
|
6
23
|
const {
|
|
@@ -22,6 +39,7 @@ function createAgentCommandHandler(deps) {
|
|
|
22
39
|
loadSessionTags,
|
|
23
40
|
sessionRichLabel,
|
|
24
41
|
getSessionRecentContext,
|
|
42
|
+
getSessionRecentDialogue,
|
|
25
43
|
pendingBinds,
|
|
26
44
|
pendingAgentFlows,
|
|
27
45
|
pendingTeamFlows,
|
|
@@ -163,21 +181,6 @@ function createAgentCommandHandler(deps) {
|
|
|
163
181
|
return true;
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
// Pending activations have no TTL — they persist until consumed.
|
|
167
|
-
// The creating chatId is stored to prevent self-activation.
|
|
168
|
-
|
|
169
|
-
// Returns the latest pending activation, excluding the creating chat
|
|
170
|
-
function getLatestActivationForChat(chatId) {
|
|
171
|
-
if (!pendingActivations || pendingActivations.size === 0) return null;
|
|
172
|
-
const cid = String(chatId);
|
|
173
|
-
let latest = null;
|
|
174
|
-
for (const rec of pendingActivations.values()) {
|
|
175
|
-
if (rec.createdByChatId === cid) continue; // creating chat cannot self-activate
|
|
176
|
-
if (!latest || rec.createdAt > latest.createdAt) latest = rec;
|
|
177
|
-
}
|
|
178
|
-
return latest;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
184
|
function resolveTtl(valueOrGetter, fallbackMs) {
|
|
182
185
|
const raw = typeof valueOrGetter === 'function' ? valueOrGetter() : valueOrGetter;
|
|
183
186
|
const num = Number(raw);
|
|
@@ -206,6 +209,29 @@ function createAgentCommandHandler(deps) {
|
|
|
206
209
|
pendingAgentFlows.set(flowKey, { ...flow, __ts: Date.now() });
|
|
207
210
|
}
|
|
208
211
|
|
|
212
|
+
function getFreshTimedFlow(flowMap, flowKey) {
|
|
213
|
+
if (!flowMap) return null;
|
|
214
|
+
const flow = flowMap.get(flowKey);
|
|
215
|
+
if (!flow) return null;
|
|
216
|
+
const FLOW_TTL_MS = resolveTtl(agentFlowTtlMs, 10 * 60 * 1000);
|
|
217
|
+
const ts = Number(flow.__ts || 0);
|
|
218
|
+
if (!(ts > 0) && flow && typeof flow === 'object') {
|
|
219
|
+
const stamped = { ...flow, __ts: Date.now() };
|
|
220
|
+
flowMap.set(flowKey, stamped);
|
|
221
|
+
return stamped;
|
|
222
|
+
}
|
|
223
|
+
if (ts > 0 && (Date.now() - ts) > FLOW_TTL_MS) {
|
|
224
|
+
flowMap.delete(flowKey);
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return flow;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function setTimedFlow(flowMap, flowKey, flow) {
|
|
231
|
+
if (!flowMap) return;
|
|
232
|
+
flowMap.set(flowKey, { ...flow, __ts: Date.now() });
|
|
233
|
+
}
|
|
234
|
+
|
|
209
235
|
function setPendingBind(chatKey, agentName) {
|
|
210
236
|
pendingBinds.set(chatKey, { name: agentName, __ts: Date.now() });
|
|
211
237
|
}
|
|
@@ -229,101 +255,31 @@ function createAgentCommandHandler(deps) {
|
|
|
229
255
|
return raw.name || null;
|
|
230
256
|
}
|
|
231
257
|
|
|
232
|
-
function getBoundProject(chatId, cfg) {
|
|
233
|
-
const agentMap = {
|
|
234
|
-
...(cfg.telegram ? cfg.telegram.chat_agent_map : {}),
|
|
235
|
-
...(cfg.feishu ? cfg.feishu.chat_agent_map : {}),
|
|
236
|
-
};
|
|
237
|
-
const boundKey = agentMap[String(chatId)];
|
|
238
|
-
const boundProj = boundKey && cfg.projects && cfg.projects[boundKey];
|
|
239
|
-
return { boundKey: boundKey || null, boundProj: boundProj || null };
|
|
240
|
-
}
|
|
241
|
-
|
|
242
258
|
async function bindViaUnifiedApi(bot, chatId, agentName, agentCwd) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
chatId,
|
|
256
|
-
normalizeCwd(res.data.cwd),
|
|
257
|
-
p.name || agentName || res.data.projectKey || '',
|
|
258
|
-
p.engine || getDefaultEngine()
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
await bot.sendMessage(chatId, `${icon} ${p.name || agentName} ${action}\n目录: ${displayCwd}`);
|
|
262
|
-
return { ok: true, data: res.data };
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Backward-compatible fallback
|
|
266
|
-
const fallback = await doBindAgent(bot, chatId, agentName, agentCwd);
|
|
267
|
-
if (!fallback || fallback.ok === false) {
|
|
268
|
-
return { ok: false, error: (fallback && fallback.error) || 'bind failed' };
|
|
269
|
-
}
|
|
270
|
-
const fallbackCwd = (fallback.data && fallback.data.cwd) || agentCwd;
|
|
271
|
-
if (fallbackCwd && typeof attachOrCreateSession === 'function') {
|
|
272
|
-
attachOrCreateSession(chatId, normalizeCwd(fallbackCwd), agentName || '', getDefaultEngine());
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
ok: true,
|
|
276
|
-
data: {
|
|
277
|
-
cwd: fallbackCwd,
|
|
278
|
-
projectKey: fallback && fallback.data ? fallback.data.projectKey : null,
|
|
279
|
-
project: fallback && fallback.data ? fallback.data.project : null,
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async function editRoleViaUnifiedApi(workspaceDir, deltaText) {
|
|
285
|
-
if (agentTools && typeof agentTools.editAgentRoleDefinition === 'function') {
|
|
286
|
-
return agentTools.editAgentRoleDefinition(workspaceDir, deltaText);
|
|
287
|
-
}
|
|
288
|
-
const legacy = await mergeAgentRole(workspaceDir, deltaText);
|
|
289
|
-
if (legacy.error) return { ok: false, error: legacy.error };
|
|
290
|
-
return { ok: true, data: legacy };
|
|
259
|
+
return bindAgentToChat({
|
|
260
|
+
agentTools,
|
|
261
|
+
doBindAgent,
|
|
262
|
+
bot,
|
|
263
|
+
chatId,
|
|
264
|
+
agentName,
|
|
265
|
+
agentCwd,
|
|
266
|
+
HOME,
|
|
267
|
+
attachOrCreateSession,
|
|
268
|
+
normalizeCwd,
|
|
269
|
+
getDefaultEngine,
|
|
270
|
+
});
|
|
291
271
|
}
|
|
292
272
|
|
|
293
273
|
async function listAgentsViaUnifiedApi(chatId) {
|
|
294
|
-
|
|
295
|
-
return agentTools.listAllAgents(chatId);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const cfg = loadConfig();
|
|
299
|
-
const projects = cfg.projects || {};
|
|
300
|
-
const entries = Object.entries(projects)
|
|
301
|
-
.filter(([, p]) => p && p.cwd)
|
|
302
|
-
.map(([key, p]) => ({
|
|
303
|
-
key,
|
|
304
|
-
name: p.name || key,
|
|
305
|
-
cwd: p.cwd,
|
|
306
|
-
icon: p.icon || '🤖',
|
|
307
|
-
}));
|
|
308
|
-
const { boundKey } = getBoundProject(chatId, cfg);
|
|
309
|
-
return { ok: true, data: { agents: entries, boundKey } };
|
|
274
|
+
return listAgents({ agentTools, chatId, loadConfig });
|
|
310
275
|
}
|
|
311
276
|
|
|
312
277
|
async function unbindViaUnifiedApi(chatId) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
278
|
+
return unbindAgent({ agentTools, chatId, loadConfig, writeConfigSafe, backupConfig });
|
|
279
|
+
}
|
|
316
280
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const ak = isTg ? 'telegram' : 'feishu';
|
|
320
|
-
if (!cfg[ak]) cfg[ak] = {};
|
|
321
|
-
if (!cfg[ak].chat_agent_map) cfg[ak].chat_agent_map = {};
|
|
322
|
-
const old = cfg[ak].chat_agent_map[String(chatId)] || null;
|
|
323
|
-
if (old) {
|
|
324
|
-
delete cfg[ak].chat_agent_map[String(chatId)];
|
|
325
|
-
}
|
|
326
|
-
return { ok: true, data: { unbound: !!old, previousProjectKey: old } };
|
|
281
|
+
async function editRoleViaUnifiedApi(workspaceDir, deltaText) {
|
|
282
|
+
return editAgentRole({ agentTools, mergeAgentRole, workspaceDir, deltaText });
|
|
327
283
|
}
|
|
328
284
|
|
|
329
285
|
async function handleAgentCommand(ctx) {
|
|
@@ -466,11 +422,19 @@ function createAgentCommandHandler(deps) {
|
|
|
466
422
|
|
|
467
423
|
// 读取最近对话片段,帮助确认是否切换到正确的 session
|
|
468
424
|
const recentCtx = getSessionRecentContext ? getSessionRecentContext(targetSessionId) : null;
|
|
425
|
+
const recentDialogue = getSessionRecentDialogue ? getSessionRecentDialogue(targetSessionId, 4) : null;
|
|
469
426
|
let msg = `✅ 已切换: **${label}**\n📁 ${path.basename(cwd)}`;
|
|
470
427
|
if (selectedLogicalCurrent) {
|
|
471
428
|
msg += '\n\n已恢复当前智能体会话。';
|
|
472
429
|
}
|
|
473
|
-
if (
|
|
430
|
+
if (Array.isArray(recentDialogue) && recentDialogue.length > 0) {
|
|
431
|
+
msg += '\n\n最近对话:';
|
|
432
|
+
for (const item of recentDialogue) {
|
|
433
|
+
const marker = item.role === 'assistant' ? '🤖' : '👤';
|
|
434
|
+
const snippet = String(item.text || '').replace(/\n/g, ' ').slice(0, 120);
|
|
435
|
+
if (snippet) msg += `\n${marker} ${snippet}`;
|
|
436
|
+
}
|
|
437
|
+
} else if (recentCtx) {
|
|
474
438
|
if (recentCtx.lastUser) {
|
|
475
439
|
const snippet = recentCtx.lastUser.replace(/\n/g, ' ').slice(0, 80);
|
|
476
440
|
msg += `\n\n💬 上次你说: _${snippet}${recentCtx.lastUser.length > 80 ? '…' : ''}_`;
|
|
@@ -492,48 +456,68 @@ function createAgentCommandHandler(deps) {
|
|
|
492
456
|
|
|
493
457
|
// /agent new 多步向导状态机(name/desc 步骤)
|
|
494
458
|
if (pendingAgentFlows) {
|
|
495
|
-
const flow = pendingAgentFlows
|
|
459
|
+
const flow = getFreshTimedFlow(pendingAgentFlows, String(chatId));
|
|
496
460
|
if (flow && text && !text.startsWith('/')) {
|
|
497
461
|
if (flow.step === 'name') {
|
|
498
462
|
flow.name = text.trim();
|
|
463
|
+
if (flow.isClone) {
|
|
464
|
+
pendingAgentFlows.delete(String(chatId));
|
|
465
|
+
return completeAgentCreation({
|
|
466
|
+
bot,
|
|
467
|
+
chatId,
|
|
468
|
+
flow,
|
|
469
|
+
description: '',
|
|
470
|
+
createWorkspaceAgent: ({ chatId: createChatId, agentName, workspaceDir, roleDescription }) => createWorkspaceAgent({
|
|
471
|
+
agentTools,
|
|
472
|
+
chatId: createChatId,
|
|
473
|
+
agentName,
|
|
474
|
+
workspaceDir,
|
|
475
|
+
roleDescription,
|
|
476
|
+
attachOrCreateSession,
|
|
477
|
+
normalizeCwd,
|
|
478
|
+
getDefaultEngine,
|
|
479
|
+
}),
|
|
480
|
+
doBindAgent,
|
|
481
|
+
mergeAgentRole,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
499
484
|
flow.step = 'desc';
|
|
500
|
-
pendingAgentFlows
|
|
485
|
+
setTimedFlow(pendingAgentFlows, String(chatId), flow);
|
|
501
486
|
await bot.sendMessage(chatId, `好的,Agent 名称是「${flow.name}」\n\n步骤3/3:请描述这个 Agent 的角色和职责(用自然语言):`);
|
|
502
487
|
return true;
|
|
503
488
|
}
|
|
504
489
|
if (flow.step === 'desc') {
|
|
505
490
|
pendingAgentFlows.delete(String(chatId));
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
}
|
|
524
|
-
return true;
|
|
491
|
+
return completeAgentCreation({
|
|
492
|
+
bot,
|
|
493
|
+
chatId,
|
|
494
|
+
flow,
|
|
495
|
+
description: text.trim(),
|
|
496
|
+
createWorkspaceAgent: ({ chatId: createChatId, agentName, workspaceDir, roleDescription }) => createWorkspaceAgent({
|
|
497
|
+
agentTools,
|
|
498
|
+
chatId: createChatId,
|
|
499
|
+
agentName,
|
|
500
|
+
workspaceDir,
|
|
501
|
+
roleDescription,
|
|
502
|
+
attachOrCreateSession,
|
|
503
|
+
normalizeCwd,
|
|
504
|
+
getDefaultEngine,
|
|
505
|
+
}),
|
|
506
|
+
doBindAgent,
|
|
507
|
+
mergeAgentRole,
|
|
508
|
+
});
|
|
525
509
|
}
|
|
526
510
|
}
|
|
527
511
|
}
|
|
528
512
|
|
|
529
513
|
// /agent new team 多步向导状态机
|
|
530
514
|
if (pendingTeamFlows) {
|
|
531
|
-
const teamFlow = pendingTeamFlows
|
|
515
|
+
const teamFlow = getFreshTimedFlow(pendingTeamFlows, String(chatId));
|
|
532
516
|
if (teamFlow && text && !text.startsWith('/')) {
|
|
533
517
|
if (teamFlow.step === 'name') {
|
|
534
518
|
teamFlow.name = text.trim();
|
|
535
519
|
teamFlow.step = 'members';
|
|
536
|
-
pendingTeamFlows
|
|
520
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
537
521
|
await bot.sendMessage(chatId, `团队名称:「${teamFlow.name}」
|
|
538
522
|
|
|
539
523
|
请输入成员列表,格式:
|
|
@@ -549,26 +533,14 @@ function createAgentCommandHandler(deps) {
|
|
|
549
533
|
}
|
|
550
534
|
|
|
551
535
|
if (teamFlow.step === 'members') {
|
|
552
|
-
const
|
|
553
|
-
const memberLines = text.split(/[,,\n]/).filter(l => l.trim());
|
|
554
|
-
const members = [];
|
|
555
|
-
for (const line of memberLines) {
|
|
556
|
-
const parts = line.trim().split(':');
|
|
557
|
-
const name = parts[0] && parts[0].trim();
|
|
558
|
-
const icon = (parts[1] && parts[1].trim()) || '🤖';
|
|
559
|
-
const rawColor = parts[2] && parts[2].trim().toLowerCase();
|
|
560
|
-
const color = validColors.includes(rawColor) ? rawColor : validColors[members.length % validColors.length];
|
|
561
|
-
if (name) {
|
|
562
|
-
members.push({ key: name, name: `${teamFlow.name} · ${name}`, icon, color, nicknames: [name] });
|
|
563
|
-
}
|
|
564
|
-
}
|
|
536
|
+
const members = parseTeamMembers(text, teamFlow.name);
|
|
565
537
|
if (members.length === 0) {
|
|
566
538
|
await bot.sendMessage(chatId, '⚠️ 请至少添加一个成员,格式:名称:icon:颜色');
|
|
567
539
|
return true;
|
|
568
540
|
}
|
|
569
541
|
teamFlow.members = members;
|
|
570
542
|
teamFlow.step = 'cwd';
|
|
571
|
-
pendingTeamFlows
|
|
543
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
572
544
|
const memberList = members.map(m => `${m.icon} ${m.name} (${m.color})`).join('\n');
|
|
573
545
|
await bot.sendMessage(chatId, `✅ 成员配置:\n\n${memberList}\n\n正在选择父目录...`);
|
|
574
546
|
await sendBrowse(bot, chatId, 'team-new', HOME, `为「${teamFlow.name}」选择父工作目录`);
|
|
@@ -601,37 +573,17 @@ function createAgentCommandHandler(deps) {
|
|
|
601
573
|
|
|
602
574
|
// /agent new [team] — 创建新 Agent 或团队
|
|
603
575
|
if (agentSub === 'new') {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
输入 /cancel 可取消`);
|
|
616
|
-
return true;
|
|
617
|
-
}
|
|
618
|
-
// /agent new clone — 分身模式
|
|
619
|
-
const isClone = secondArg === 'clone';
|
|
620
|
-
let parentCwd = null;
|
|
621
|
-
if (isClone) {
|
|
622
|
-
const cfg = loadConfig();
|
|
623
|
-
const agentMap = {
|
|
624
|
-
...(cfg.telegram ? cfg.telegram.chat_agent_map : {}),
|
|
625
|
-
...(cfg.feishu ? cfg.feishu.chat_agent_map : {}),
|
|
626
|
-
};
|
|
627
|
-
const boundKey = agentMap[String(chatId)];
|
|
628
|
-
const boundProj = boundKey && cfg.projects && cfg.projects[boundKey];
|
|
629
|
-
if (boundProj && boundProj.cwd) parentCwd = normalizeCwd(boundProj.cwd);
|
|
630
|
-
}
|
|
631
|
-
pendingAgentFlows.set(String(chatId), { step: 'dir', isClone, parentCwd, __ts: Date.now() });
|
|
632
|
-
const hint = isClone ? `(${parentCwd ? '分身模式:将链接父 Agent 的 CLAUDE.md' : '⚠️ 当前群未绑定 Agent'})` : '';
|
|
633
|
-
await sendBrowse(bot, chatId, 'agent-new', HOME, `步骤1/3:选择 Agent 的工作目录${hint}`);
|
|
634
|
-
return true;
|
|
576
|
+
return startNewAgentWizard({
|
|
577
|
+
bot,
|
|
578
|
+
chatId,
|
|
579
|
+
secondArg: agentParts[1],
|
|
580
|
+
pendingTeamFlows,
|
|
581
|
+
pendingAgentFlows,
|
|
582
|
+
loadConfig,
|
|
583
|
+
normalizeCwd,
|
|
584
|
+
sendBrowse,
|
|
585
|
+
HOME,
|
|
586
|
+
});
|
|
635
587
|
}
|
|
636
588
|
|
|
637
589
|
// /agent bind <名称> [目录]
|
|
@@ -703,12 +655,7 @@ function createAgentCommandHandler(deps) {
|
|
|
703
655
|
return true;
|
|
704
656
|
}
|
|
705
657
|
|
|
706
|
-
const
|
|
707
|
-
let currentContent = '(CLAUDE.md 不存在)';
|
|
708
|
-
if (fs.existsSync(claudeMdPath)) {
|
|
709
|
-
currentContent = fs.readFileSync(claudeMdPath, 'utf8');
|
|
710
|
-
if (currentContent.length > 500) currentContent = currentContent.slice(0, 500) + '\n...(已截断)';
|
|
711
|
-
}
|
|
658
|
+
const currentContent = readAgentRolePreview({ fs, path, cwd });
|
|
712
659
|
setFlow(String(chatId) + ':edit', { cwd });
|
|
713
660
|
await bot.sendMessage(chatId, `📄 当前 CLAUDE.md 内容:\n\`\`\`\n${currentContent}\n\`\`\`\n\n请描述你想做的修改(用自然语言,例如:「把角色改成后端工程师,专注 Python」):`);
|
|
714
661
|
return true;
|
|
@@ -738,18 +685,15 @@ function createAgentCommandHandler(deps) {
|
|
|
738
685
|
return true;
|
|
739
686
|
}
|
|
740
687
|
const cwd = normalizeCwd(boundProj.cwd);
|
|
741
|
-
const
|
|
742
|
-
if (
|
|
688
|
+
const resetResult = resetAgentRoleSection({ fs, path, cwd });
|
|
689
|
+
if (resetResult.status === 'missing') {
|
|
743
690
|
await bot.sendMessage(chatId, '⚠️ CLAUDE.md 不存在,无需重置');
|
|
744
691
|
return true;
|
|
745
692
|
}
|
|
746
|
-
|
|
747
|
-
const after = before.replace(/(?:^|\n)## Agent 角色\n[\s\S]*?(?=\n## |$)/, '').trimStart();
|
|
748
|
-
if (after === before.trimStart()) {
|
|
693
|
+
if (resetResult.status === 'unchanged') {
|
|
749
694
|
await bot.sendMessage(chatId, '⚠️ 未找到「## Agent 角色」section,CLAUDE.md 未修改');
|
|
750
695
|
return true;
|
|
751
696
|
}
|
|
752
|
-
fs.writeFileSync(claudeMdPath, after, 'utf8');
|
|
753
697
|
await bot.sendMessage(chatId, '✅ 已删除角色 section,请重新发送角色描述(/agent edit 或自然语言修改)');
|
|
754
698
|
return true;
|
|
755
699
|
}
|
|
@@ -768,66 +712,16 @@ function createAgentCommandHandler(deps) {
|
|
|
768
712
|
await bot.sendMessage(chatId, '❌ 当前群未绑定 Agent,请先 /agent bind <名称> <目录>');
|
|
769
713
|
return true;
|
|
770
714
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
? Object.entries(res.data.views).map(([k, v]) => k + ':' + v).join(', ')
|
|
782
|
-
: '—';
|
|
783
|
-
await bot.sendMessage(chatId, [
|
|
784
|
-
'✅ Agent Soul 层已就绪',
|
|
785
|
-
'agent_id: ' + res.data.agentId,
|
|
786
|
-
'链接方式: ' + viewModes,
|
|
787
|
-
'',
|
|
788
|
-
'文件位置:',
|
|
789
|
-
' SOUL.md → ~/.metame/agents/' + res.data.agentId + '/soul.md',
|
|
790
|
-
' MEMORY.md → ~/.metame/agents/' + res.data.agentId + '/memory-snapshot.md',
|
|
791
|
-
].join('\n'));
|
|
792
|
-
}
|
|
793
|
-
} else {
|
|
794
|
-
await bot.sendMessage(chatId, '❌ agentTools 不可用');
|
|
795
|
-
}
|
|
796
|
-
return true;
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
if (soulAction === 'edit') {
|
|
800
|
-
const soulText = agentParts.slice(2).join(' ').trim();
|
|
801
|
-
if (!soulText) {
|
|
802
|
-
await bot.sendMessage(chatId, '用法: /agent soul edit <新内容>\n当前内容: /agent soul');
|
|
803
|
-
return true;
|
|
804
|
-
}
|
|
805
|
-
try {
|
|
806
|
-
fs.writeFileSync(soulPath, soulText, 'utf8');
|
|
807
|
-
await bot.sendMessage(chatId, '✅ SOUL.md 已更新');
|
|
808
|
-
} catch (e) {
|
|
809
|
-
await bot.sendMessage(chatId, '❌ 写入失败: ' + e.message);
|
|
810
|
-
}
|
|
811
|
-
return true;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Default: show current SOUL.md content
|
|
815
|
-
if (!fs.existsSync(soulPath)) {
|
|
816
|
-
await bot.sendMessage(chatId, [
|
|
817
|
-
'⚠️ SOUL.md 不存在',
|
|
818
|
-
'',
|
|
819
|
-
'老项目或刚绑定的 Agent 可能尚未建立 Soul 层。',
|
|
820
|
-
'运行 /agent soul repair 自动生成。',
|
|
821
|
-
].join('\n'));
|
|
822
|
-
return true;
|
|
823
|
-
}
|
|
824
|
-
try {
|
|
825
|
-
const soulContent = fs.readFileSync(soulPath, 'utf8').trim().slice(0, 2000);
|
|
826
|
-
await bot.sendMessage(chatId, '📋 当前 Soul:\n\n' + soulContent);
|
|
827
|
-
} catch (e) {
|
|
828
|
-
await bot.sendMessage(chatId, '❌ 读取 SOUL.md 失败: ' + e.message);
|
|
829
|
-
}
|
|
830
|
-
return true;
|
|
715
|
+
return handleSoulCommand({
|
|
716
|
+
bot,
|
|
717
|
+
chatId,
|
|
718
|
+
soulAction,
|
|
719
|
+
soulText: agentParts.slice(2).join(' ').trim(),
|
|
720
|
+
cwd: normalizeCwd(boundProj.cwd),
|
|
721
|
+
fs,
|
|
722
|
+
path,
|
|
723
|
+
agentTools,
|
|
724
|
+
});
|
|
831
725
|
}
|
|
832
726
|
|
|
833
727
|
|
|
@@ -853,66 +747,13 @@ function createAgentCommandHandler(deps) {
|
|
|
853
747
|
|
|
854
748
|
// /activate — bind this unbound chat to the most recently created pending agent
|
|
855
749
|
if (text === '/activate' || text.startsWith('/activate ')) {
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
if (!activation) {
|
|
864
|
-
// Check if this chat was the creator (self-activate attempt)
|
|
865
|
-
if (pendingActivations) {
|
|
866
|
-
for (const rec of pendingActivations.values()) {
|
|
867
|
-
if (rec.createdByChatId === String(chatId)) {
|
|
868
|
-
await bot.sendMessage(chatId,
|
|
869
|
-
`❌ 不能在创建来源群激活。\n请在你新建的目标群里发送 \`/activate\`\n\n` +
|
|
870
|
-
`或在任意群用: \`/agent bind ${rec.agentName} ${rec.cwd}\``
|
|
871
|
-
);
|
|
872
|
-
return true;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
// No pending activation — fall back to scanning daemon.yaml for unbound projects
|
|
877
|
-
const allBoundKeys = new Set(Object.values({
|
|
878
|
-
...(cfg.telegram ? cfg.telegram.chat_agent_map : {}),
|
|
879
|
-
...(cfg.feishu ? cfg.feishu.chat_agent_map : {}),
|
|
880
|
-
}));
|
|
881
|
-
const unboundProjects = Object.entries(cfg.projects || {})
|
|
882
|
-
.filter(([key, p]) => p && p.cwd && !allBoundKeys.has(key))
|
|
883
|
-
.map(([key, p]) => ({ key, name: p.name || key, cwd: p.cwd, icon: p.icon || '🤖' }));
|
|
884
|
-
|
|
885
|
-
if (unboundProjects.length === 1) {
|
|
886
|
-
// Exactly one unbound project — auto-bind using project KEY (not display name)
|
|
887
|
-
// to ensure toProjectKey() resolves to the correct existing key in daemon.yaml
|
|
888
|
-
const proj = unboundProjects[0];
|
|
889
|
-
const bindRes2 = await bindViaUnifiedApi(bot, chatId, proj.key, proj.cwd);
|
|
890
|
-
if (bindRes2.ok) pendingActivations && pendingActivations.delete(proj.key);
|
|
891
|
-
return true;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
if (unboundProjects.length > 1) {
|
|
895
|
-
// Multiple unbound projects — show pick list using project keys
|
|
896
|
-
const lines = ['请选择要激活的 Agent:', ''];
|
|
897
|
-
for (const p of unboundProjects) {
|
|
898
|
-
lines.push(`${p.icon} ${p.name} → \`/agent bind ${p.key} ${p.cwd}\``);
|
|
899
|
-
}
|
|
900
|
-
lines.push('\n发送对应命令即可绑定此群。');
|
|
901
|
-
await bot.sendMessage(chatId, lines.join('\n'));
|
|
902
|
-
return true;
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
// Truly nothing to activate
|
|
906
|
-
await bot.sendMessage(chatId,
|
|
907
|
-
'没有待激活的 Agent。\n\n如果已创建过 Agent,直接用:\n`/agent bind <名称> <目录>`\n即可绑定,不需要重新创建。'
|
|
908
|
-
);
|
|
909
|
-
return true;
|
|
910
|
-
}
|
|
911
|
-
const bindRes = await bindViaUnifiedApi(bot, chatId, activation.agentName, activation.cwd);
|
|
912
|
-
if (bindRes.ok) {
|
|
913
|
-
pendingActivations && pendingActivations.delete(activation.agentKey);
|
|
914
|
-
}
|
|
915
|
-
return true;
|
|
750
|
+
return handleActivateCommand({
|
|
751
|
+
bot,
|
|
752
|
+
chatId,
|
|
753
|
+
loadConfig,
|
|
754
|
+
pendingActivations,
|
|
755
|
+
bindAgent: (agentName, agentCwd) => bindViaUnifiedApi(bot, chatId, agentName, agentCwd),
|
|
756
|
+
});
|
|
916
757
|
}
|
|
917
758
|
|
|
918
759
|
// /agent-dir <path>: /agent new 向导的目录选择回调(步骤1→步骤2)
|
|
@@ -928,7 +769,7 @@ function createAgentCommandHandler(deps) {
|
|
|
928
769
|
pendingAgentFlows.set(String(chatId), flow);
|
|
929
770
|
const displayPath = dirPath.replace(HOME, '~');
|
|
930
771
|
const cloneHint = flow.isClone ? '(分身模式)' : '';
|
|
931
|
-
await bot.sendMessage(chatId, `✓ 已选择目录:${displayPath}${cloneHint}\n\n步骤2/3:给这个 Agent 起个名字?`);
|
|
772
|
+
await bot.sendMessage(chatId, `✓ 已选择目录:${displayPath}${cloneHint}\n\n${flow.isClone ? '步骤2/2' : '步骤2/3'}:给这个 Agent 起个名字?`);
|
|
932
773
|
return true;
|
|
933
774
|
}
|
|
934
775
|
|
|
@@ -948,65 +789,30 @@ function createAgentCommandHandler(deps) {
|
|
|
948
789
|
// /agent-team-dir <path>: directory picker callback for team creation
|
|
949
790
|
if (text.startsWith('/agent-team-dir ')) {
|
|
950
791
|
const dirPath = expandPath(text.slice(16).trim());
|
|
951
|
-
const teamFlow = pendingTeamFlows
|
|
792
|
+
const teamFlow = getFreshTimedFlow(pendingTeamFlows, String(chatId));
|
|
952
793
|
if (!teamFlow || teamFlow.step !== 'cwd') {
|
|
953
794
|
await bot.sendMessage(chatId, '❌ 没有待完成的团队创建,请重新发送 /agent new team');
|
|
954
795
|
return true;
|
|
955
796
|
}
|
|
956
797
|
teamFlow.step = 'creating';
|
|
957
|
-
pendingTeamFlows
|
|
798
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
958
799
|
await bot.sendMessage(chatId, `⏳ 正在创建团队「${teamFlow.name}」...`);
|
|
959
800
|
|
|
960
801
|
try {
|
|
961
|
-
const teamDir = path.join(dirPath, 'team');
|
|
962
|
-
if (!fs.existsSync(teamDir)) fs.mkdirSync(teamDir, { recursive: true });
|
|
963
|
-
|
|
964
802
|
const members = Array.isArray(teamFlow.members) ? teamFlow.members : [];
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// Register in daemon.yaml under parent project's team array
|
|
981
|
-
const cfg = loadConfig();
|
|
982
|
-
let parentProjectKey = null;
|
|
983
|
-
if (cfg.projects) {
|
|
984
|
-
for (const [projKey, proj] of Object.entries(cfg.projects)) {
|
|
985
|
-
if (normalizeCwd(proj.cwd || '') === normalizeCwd(dirPath)) {
|
|
986
|
-
parentProjectKey = projKey;
|
|
987
|
-
break;
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
if (parentProjectKey && cfg.projects[parentProjectKey]) {
|
|
993
|
-
const proj = cfg.projects[parentProjectKey];
|
|
994
|
-
if (!proj.team) proj.team = [];
|
|
995
|
-
for (const member of members) {
|
|
996
|
-
if (!proj.team.some(m => m.key === member.key)) {
|
|
997
|
-
proj.team.push({
|
|
998
|
-
key: member.key,
|
|
999
|
-
name: member.name,
|
|
1000
|
-
icon: member.icon,
|
|
1001
|
-
color: member.color,
|
|
1002
|
-
cwd: path.join(teamDir, member.key),
|
|
1003
|
-
nicknames: member.nicknames,
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
if (writeConfigSafe) writeConfigSafe(cfg);
|
|
1008
|
-
if (backupConfig) backupConfig();
|
|
1009
|
-
}
|
|
803
|
+
const { teamDir, parentProjectKey } = createTeamWorkspace({
|
|
804
|
+
fs,
|
|
805
|
+
path,
|
|
806
|
+
execSync,
|
|
807
|
+
dirPath,
|
|
808
|
+
teamName: teamFlow.name,
|
|
809
|
+
members,
|
|
810
|
+
loadConfig,
|
|
811
|
+
normalizeCwd,
|
|
812
|
+
writeConfigSafe,
|
|
813
|
+
backupConfig,
|
|
814
|
+
HOME,
|
|
815
|
+
});
|
|
1010
816
|
|
|
1011
817
|
const memberList = members.map(m => `${m.icon} ${m.key}`).join(' | ');
|
|
1012
818
|
const yamlNote = parentProjectKey
|