metame-cli 1.5.21 → 1.5.22
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 +7 -23
- package/package.json +1 -1
- package/scripts/agent-intent-shared.js +111 -0
- package/scripts/daemon-agent-commands.js +150 -353
- 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-team-workflow.js +146 -0
- package/scripts/daemon.js +7 -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/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 {
|
|
@@ -163,21 +180,6 @@ function createAgentCommandHandler(deps) {
|
|
|
163
180
|
return true;
|
|
164
181
|
}
|
|
165
182
|
|
|
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
183
|
function resolveTtl(valueOrGetter, fallbackMs) {
|
|
182
184
|
const raw = typeof valueOrGetter === 'function' ? valueOrGetter() : valueOrGetter;
|
|
183
185
|
const num = Number(raw);
|
|
@@ -206,6 +208,29 @@ function createAgentCommandHandler(deps) {
|
|
|
206
208
|
pendingAgentFlows.set(flowKey, { ...flow, __ts: Date.now() });
|
|
207
209
|
}
|
|
208
210
|
|
|
211
|
+
function getFreshTimedFlow(flowMap, flowKey) {
|
|
212
|
+
if (!flowMap) return null;
|
|
213
|
+
const flow = flowMap.get(flowKey);
|
|
214
|
+
if (!flow) return null;
|
|
215
|
+
const FLOW_TTL_MS = resolveTtl(agentFlowTtlMs, 10 * 60 * 1000);
|
|
216
|
+
const ts = Number(flow.__ts || 0);
|
|
217
|
+
if (!(ts > 0) && flow && typeof flow === 'object') {
|
|
218
|
+
const stamped = { ...flow, __ts: Date.now() };
|
|
219
|
+
flowMap.set(flowKey, stamped);
|
|
220
|
+
return stamped;
|
|
221
|
+
}
|
|
222
|
+
if (ts > 0 && (Date.now() - ts) > FLOW_TTL_MS) {
|
|
223
|
+
flowMap.delete(flowKey);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return flow;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function setTimedFlow(flowMap, flowKey, flow) {
|
|
230
|
+
if (!flowMap) return;
|
|
231
|
+
flowMap.set(flowKey, { ...flow, __ts: Date.now() });
|
|
232
|
+
}
|
|
233
|
+
|
|
209
234
|
function setPendingBind(chatKey, agentName) {
|
|
210
235
|
pendingBinds.set(chatKey, { name: agentName, __ts: Date.now() });
|
|
211
236
|
}
|
|
@@ -229,101 +254,31 @@ function createAgentCommandHandler(deps) {
|
|
|
229
254
|
return raw.name || null;
|
|
230
255
|
}
|
|
231
256
|
|
|
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
257
|
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 };
|
|
258
|
+
return bindAgentToChat({
|
|
259
|
+
agentTools,
|
|
260
|
+
doBindAgent,
|
|
261
|
+
bot,
|
|
262
|
+
chatId,
|
|
263
|
+
agentName,
|
|
264
|
+
agentCwd,
|
|
265
|
+
HOME,
|
|
266
|
+
attachOrCreateSession,
|
|
267
|
+
normalizeCwd,
|
|
268
|
+
getDefaultEngine,
|
|
269
|
+
});
|
|
291
270
|
}
|
|
292
271
|
|
|
293
272
|
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 } };
|
|
273
|
+
return listAgents({ agentTools, chatId, loadConfig });
|
|
310
274
|
}
|
|
311
275
|
|
|
312
276
|
async function unbindViaUnifiedApi(chatId) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
277
|
+
return unbindAgent({ agentTools, chatId, loadConfig, writeConfigSafe, backupConfig });
|
|
278
|
+
}
|
|
316
279
|
|
|
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 } };
|
|
280
|
+
async function editRoleViaUnifiedApi(workspaceDir, deltaText) {
|
|
281
|
+
return editAgentRole({ agentTools, mergeAgentRole, workspaceDir, deltaText });
|
|
327
282
|
}
|
|
328
283
|
|
|
329
284
|
async function handleAgentCommand(ctx) {
|
|
@@ -492,48 +447,68 @@ function createAgentCommandHandler(deps) {
|
|
|
492
447
|
|
|
493
448
|
// /agent new 多步向导状态机(name/desc 步骤)
|
|
494
449
|
if (pendingAgentFlows) {
|
|
495
|
-
const flow = pendingAgentFlows
|
|
450
|
+
const flow = getFreshTimedFlow(pendingAgentFlows, String(chatId));
|
|
496
451
|
if (flow && text && !text.startsWith('/')) {
|
|
497
452
|
if (flow.step === 'name') {
|
|
498
453
|
flow.name = text.trim();
|
|
454
|
+
if (flow.isClone) {
|
|
455
|
+
pendingAgentFlows.delete(String(chatId));
|
|
456
|
+
return completeAgentCreation({
|
|
457
|
+
bot,
|
|
458
|
+
chatId,
|
|
459
|
+
flow,
|
|
460
|
+
description: '',
|
|
461
|
+
createWorkspaceAgent: ({ chatId: createChatId, agentName, workspaceDir, roleDescription }) => createWorkspaceAgent({
|
|
462
|
+
agentTools,
|
|
463
|
+
chatId: createChatId,
|
|
464
|
+
agentName,
|
|
465
|
+
workspaceDir,
|
|
466
|
+
roleDescription,
|
|
467
|
+
attachOrCreateSession,
|
|
468
|
+
normalizeCwd,
|
|
469
|
+
getDefaultEngine,
|
|
470
|
+
}),
|
|
471
|
+
doBindAgent,
|
|
472
|
+
mergeAgentRole,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
499
475
|
flow.step = 'desc';
|
|
500
|
-
pendingAgentFlows
|
|
476
|
+
setTimedFlow(pendingAgentFlows, String(chatId), flow);
|
|
501
477
|
await bot.sendMessage(chatId, `好的,Agent 名称是「${flow.name}」\n\n步骤3/3:请描述这个 Agent 的角色和职责(用自然语言):`);
|
|
502
478
|
return true;
|
|
503
479
|
}
|
|
504
480
|
if (flow.step === 'desc') {
|
|
505
481
|
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;
|
|
482
|
+
return completeAgentCreation({
|
|
483
|
+
bot,
|
|
484
|
+
chatId,
|
|
485
|
+
flow,
|
|
486
|
+
description: text.trim(),
|
|
487
|
+
createWorkspaceAgent: ({ chatId: createChatId, agentName, workspaceDir, roleDescription }) => createWorkspaceAgent({
|
|
488
|
+
agentTools,
|
|
489
|
+
chatId: createChatId,
|
|
490
|
+
agentName,
|
|
491
|
+
workspaceDir,
|
|
492
|
+
roleDescription,
|
|
493
|
+
attachOrCreateSession,
|
|
494
|
+
normalizeCwd,
|
|
495
|
+
getDefaultEngine,
|
|
496
|
+
}),
|
|
497
|
+
doBindAgent,
|
|
498
|
+
mergeAgentRole,
|
|
499
|
+
});
|
|
525
500
|
}
|
|
526
501
|
}
|
|
527
502
|
}
|
|
528
503
|
|
|
529
504
|
// /agent new team 多步向导状态机
|
|
530
505
|
if (pendingTeamFlows) {
|
|
531
|
-
const teamFlow = pendingTeamFlows
|
|
506
|
+
const teamFlow = getFreshTimedFlow(pendingTeamFlows, String(chatId));
|
|
532
507
|
if (teamFlow && text && !text.startsWith('/')) {
|
|
533
508
|
if (teamFlow.step === 'name') {
|
|
534
509
|
teamFlow.name = text.trim();
|
|
535
510
|
teamFlow.step = 'members';
|
|
536
|
-
pendingTeamFlows
|
|
511
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
537
512
|
await bot.sendMessage(chatId, `团队名称:「${teamFlow.name}」
|
|
538
513
|
|
|
539
514
|
请输入成员列表,格式:
|
|
@@ -549,26 +524,14 @@ function createAgentCommandHandler(deps) {
|
|
|
549
524
|
}
|
|
550
525
|
|
|
551
526
|
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
|
-
}
|
|
527
|
+
const members = parseTeamMembers(text, teamFlow.name);
|
|
565
528
|
if (members.length === 0) {
|
|
566
529
|
await bot.sendMessage(chatId, '⚠️ 请至少添加一个成员,格式:名称:icon:颜色');
|
|
567
530
|
return true;
|
|
568
531
|
}
|
|
569
532
|
teamFlow.members = members;
|
|
570
533
|
teamFlow.step = 'cwd';
|
|
571
|
-
pendingTeamFlows
|
|
534
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
572
535
|
const memberList = members.map(m => `${m.icon} ${m.name} (${m.color})`).join('\n');
|
|
573
536
|
await bot.sendMessage(chatId, `✅ 成员配置:\n\n${memberList}\n\n正在选择父目录...`);
|
|
574
537
|
await sendBrowse(bot, chatId, 'team-new', HOME, `为「${teamFlow.name}」选择父工作目录`);
|
|
@@ -601,37 +564,17 @@ function createAgentCommandHandler(deps) {
|
|
|
601
564
|
|
|
602
565
|
// /agent new [team] — 创建新 Agent 或团队
|
|
603
566
|
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;
|
|
567
|
+
return startNewAgentWizard({
|
|
568
|
+
bot,
|
|
569
|
+
chatId,
|
|
570
|
+
secondArg: agentParts[1],
|
|
571
|
+
pendingTeamFlows,
|
|
572
|
+
pendingAgentFlows,
|
|
573
|
+
loadConfig,
|
|
574
|
+
normalizeCwd,
|
|
575
|
+
sendBrowse,
|
|
576
|
+
HOME,
|
|
577
|
+
});
|
|
635
578
|
}
|
|
636
579
|
|
|
637
580
|
// /agent bind <名称> [目录]
|
|
@@ -703,12 +646,7 @@ function createAgentCommandHandler(deps) {
|
|
|
703
646
|
return true;
|
|
704
647
|
}
|
|
705
648
|
|
|
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
|
-
}
|
|
649
|
+
const currentContent = readAgentRolePreview({ fs, path, cwd });
|
|
712
650
|
setFlow(String(chatId) + ':edit', { cwd });
|
|
713
651
|
await bot.sendMessage(chatId, `📄 当前 CLAUDE.md 内容:\n\`\`\`\n${currentContent}\n\`\`\`\n\n请描述你想做的修改(用自然语言,例如:「把角色改成后端工程师,专注 Python」):`);
|
|
714
652
|
return true;
|
|
@@ -738,18 +676,15 @@ function createAgentCommandHandler(deps) {
|
|
|
738
676
|
return true;
|
|
739
677
|
}
|
|
740
678
|
const cwd = normalizeCwd(boundProj.cwd);
|
|
741
|
-
const
|
|
742
|
-
if (
|
|
679
|
+
const resetResult = resetAgentRoleSection({ fs, path, cwd });
|
|
680
|
+
if (resetResult.status === 'missing') {
|
|
743
681
|
await bot.sendMessage(chatId, '⚠️ CLAUDE.md 不存在,无需重置');
|
|
744
682
|
return true;
|
|
745
683
|
}
|
|
746
|
-
|
|
747
|
-
const after = before.replace(/(?:^|\n)## Agent 角色\n[\s\S]*?(?=\n## |$)/, '').trimStart();
|
|
748
|
-
if (after === before.trimStart()) {
|
|
684
|
+
if (resetResult.status === 'unchanged') {
|
|
749
685
|
await bot.sendMessage(chatId, '⚠️ 未找到「## Agent 角色」section,CLAUDE.md 未修改');
|
|
750
686
|
return true;
|
|
751
687
|
}
|
|
752
|
-
fs.writeFileSync(claudeMdPath, after, 'utf8');
|
|
753
688
|
await bot.sendMessage(chatId, '✅ 已删除角色 section,请重新发送角色描述(/agent edit 或自然语言修改)');
|
|
754
689
|
return true;
|
|
755
690
|
}
|
|
@@ -768,66 +703,16 @@ function createAgentCommandHandler(deps) {
|
|
|
768
703
|
await bot.sendMessage(chatId, '❌ 当前群未绑定 Agent,请先 /agent bind <名称> <目录>');
|
|
769
704
|
return true;
|
|
770
705
|
}
|
|
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;
|
|
706
|
+
return handleSoulCommand({
|
|
707
|
+
bot,
|
|
708
|
+
chatId,
|
|
709
|
+
soulAction,
|
|
710
|
+
soulText: agentParts.slice(2).join(' ').trim(),
|
|
711
|
+
cwd: normalizeCwd(boundProj.cwd),
|
|
712
|
+
fs,
|
|
713
|
+
path,
|
|
714
|
+
agentTools,
|
|
715
|
+
});
|
|
831
716
|
}
|
|
832
717
|
|
|
833
718
|
|
|
@@ -853,66 +738,13 @@ function createAgentCommandHandler(deps) {
|
|
|
853
738
|
|
|
854
739
|
// /activate — bind this unbound chat to the most recently created pending agent
|
|
855
740
|
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;
|
|
741
|
+
return handleActivateCommand({
|
|
742
|
+
bot,
|
|
743
|
+
chatId,
|
|
744
|
+
loadConfig,
|
|
745
|
+
pendingActivations,
|
|
746
|
+
bindAgent: (agentName, agentCwd) => bindViaUnifiedApi(bot, chatId, agentName, agentCwd),
|
|
747
|
+
});
|
|
916
748
|
}
|
|
917
749
|
|
|
918
750
|
// /agent-dir <path>: /agent new 向导的目录选择回调(步骤1→步骤2)
|
|
@@ -928,7 +760,7 @@ function createAgentCommandHandler(deps) {
|
|
|
928
760
|
pendingAgentFlows.set(String(chatId), flow);
|
|
929
761
|
const displayPath = dirPath.replace(HOME, '~');
|
|
930
762
|
const cloneHint = flow.isClone ? '(分身模式)' : '';
|
|
931
|
-
await bot.sendMessage(chatId, `✓ 已选择目录:${displayPath}${cloneHint}\n\n步骤2/3:给这个 Agent 起个名字?`);
|
|
763
|
+
await bot.sendMessage(chatId, `✓ 已选择目录:${displayPath}${cloneHint}\n\n${flow.isClone ? '步骤2/2' : '步骤2/3'}:给这个 Agent 起个名字?`);
|
|
932
764
|
return true;
|
|
933
765
|
}
|
|
934
766
|
|
|
@@ -948,65 +780,30 @@ function createAgentCommandHandler(deps) {
|
|
|
948
780
|
// /agent-team-dir <path>: directory picker callback for team creation
|
|
949
781
|
if (text.startsWith('/agent-team-dir ')) {
|
|
950
782
|
const dirPath = expandPath(text.slice(16).trim());
|
|
951
|
-
const teamFlow = pendingTeamFlows
|
|
783
|
+
const teamFlow = getFreshTimedFlow(pendingTeamFlows, String(chatId));
|
|
952
784
|
if (!teamFlow || teamFlow.step !== 'cwd') {
|
|
953
785
|
await bot.sendMessage(chatId, '❌ 没有待完成的团队创建,请重新发送 /agent new team');
|
|
954
786
|
return true;
|
|
955
787
|
}
|
|
956
788
|
teamFlow.step = 'creating';
|
|
957
|
-
pendingTeamFlows
|
|
789
|
+
setTimedFlow(pendingTeamFlows, String(chatId), teamFlow);
|
|
958
790
|
await bot.sendMessage(chatId, `⏳ 正在创建团队「${teamFlow.name}」...`);
|
|
959
791
|
|
|
960
792
|
try {
|
|
961
|
-
const teamDir = path.join(dirPath, 'team');
|
|
962
|
-
if (!fs.existsSync(teamDir)) fs.mkdirSync(teamDir, { recursive: true });
|
|
963
|
-
|
|
964
793
|
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
|
-
}
|
|
794
|
+
const { teamDir, parentProjectKey } = createTeamWorkspace({
|
|
795
|
+
fs,
|
|
796
|
+
path,
|
|
797
|
+
execSync,
|
|
798
|
+
dirPath,
|
|
799
|
+
teamName: teamFlow.name,
|
|
800
|
+
members,
|
|
801
|
+
loadConfig,
|
|
802
|
+
normalizeCwd,
|
|
803
|
+
writeConfigSafe,
|
|
804
|
+
backupConfig,
|
|
805
|
+
HOME,
|
|
806
|
+
});
|
|
1010
807
|
|
|
1011
808
|
const memberList = members.map(m => `${m.icon} ${m.key}`).join(' | ');
|
|
1012
809
|
const yamlNote = parentProjectKey
|