team-anya-cli 0.1.0
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 +38 -0
- package/anya/prompts/execution-guides/git-delivery.md +38 -0
- package/anya/prompts/execution-guides/testing-and-self-heal.md +28 -0
- package/anya/prompts/protocols/brief-assembly.md +55 -0
- package/anya/prompts/protocols/report.md +175 -0
- package/anya/prompts/protocols/review.md +90 -0
- package/anya/prompts/task-claude-md.template.md +32 -0
- package/apps/server/dist/broker/cc-broker.js +257 -0
- package/apps/server/dist/cli.js +296 -0
- package/apps/server/dist/config.js +76 -0
- package/apps/server/dist/daemon.js +51 -0
- package/apps/server/dist/gateway/chat-sync.js +135 -0
- package/apps/server/dist/gateway/command-router.js +114 -0
- package/apps/server/dist/gateway/commands/cancel.js +32 -0
- package/apps/server/dist/gateway/commands/help.js +16 -0
- package/apps/server/dist/gateway/commands/index.js +26 -0
- package/apps/server/dist/gateway/commands/restart.js +34 -0
- package/apps/server/dist/gateway/commands/status.js +34 -0
- package/apps/server/dist/gateway/commands/tasks.js +33 -0
- package/apps/server/dist/gateway/feishu-sender.js +346 -0
- package/apps/server/dist/gateway/feishu-ws.js +254 -0
- package/apps/server/dist/gateway/http.js +994 -0
- package/apps/server/dist/gateway/media-downloader.js +149 -0
- package/apps/server/dist/gateway/message-events.js +10 -0
- package/apps/server/dist/gateway/message-intake.js +50 -0
- package/apps/server/dist/gateway/message-queue.js +104 -0
- package/apps/server/dist/gateway/session-reader.js +142 -0
- package/apps/server/dist/gateway/ws-push.js +115 -0
- package/apps/server/dist/loid/brain.js +104 -0
- package/apps/server/dist/loid/clarifier.js +162 -0
- package/apps/server/dist/loid/context-builder.js +413 -0
- package/apps/server/dist/loid/mcp-server.js +104 -0
- package/apps/server/dist/loid/memory-settler.js +189 -0
- package/apps/server/dist/loid/opportunity-manager.js +148 -0
- package/apps/server/dist/loid/profile-updater.js +179 -0
- package/apps/server/dist/loid/reporter.js +148 -0
- package/apps/server/dist/loid/schemas.js +117 -0
- package/apps/server/dist/loid/self-calibrator.js +314 -0
- package/apps/server/dist/loid/session-manager.js +217 -0
- package/apps/server/dist/loid/session.js +271 -0
- package/apps/server/dist/loid/worktree-manager.js +191 -0
- package/apps/server/dist/main.js +337 -0
- package/apps/server/dist/tracing/index.js +2 -0
- package/apps/server/dist/tracing/trace-context.js +92 -0
- package/apps/server/dist/types/message.js +2 -0
- package/apps/server/dist/yor/yor-mcp-server.js +104 -0
- package/apps/server/dist/yor/yor-orchestrator.js +233 -0
- package/apps/web/dist/assets/index-CHIT0Dya.css +1 -0
- package/apps/web/dist/assets/index-CJzAjoVH.js +798 -0
- package/apps/web/dist/index.html +13 -0
- package/package.json +42 -0
- package/packages/cc-client/dist/claude-code-backend.js +664 -0
- package/packages/cc-client/dist/index.js +2 -0
- package/packages/cc-client/package.json +11 -0
- package/packages/core/dist/constants.js +59 -0
- package/packages/core/dist/errors.js +35 -0
- package/packages/core/dist/index.js +7 -0
- package/packages/core/dist/office-init.js +97 -0
- package/packages/core/dist/scope/checker.js +114 -0
- package/packages/core/dist/scope/defaults.js +40 -0
- package/packages/core/dist/scope/index.js +3 -0
- package/packages/core/dist/state-machine.js +85 -0
- package/packages/core/dist/types/audit.js +12 -0
- package/packages/core/dist/types/backend.js +2 -0
- package/packages/core/dist/types/commitment.js +17 -0
- package/packages/core/dist/types/communication.js +18 -0
- package/packages/core/dist/types/index.js +8 -0
- package/packages/core/dist/types/opportunity.js +27 -0
- package/packages/core/dist/types/org.js +26 -0
- package/packages/core/dist/types/task.js +46 -0
- package/packages/core/package.json +10 -0
- package/packages/db/dist/client.js +69 -0
- package/packages/db/dist/index.js +603 -0
- package/packages/db/dist/schema/audit-events.js +13 -0
- package/packages/db/dist/schema/cc-sessions.js +14 -0
- package/packages/db/dist/schema/chats.js +33 -0
- package/packages/db/dist/schema/commitments.js +18 -0
- package/packages/db/dist/schema/communication-events.js +14 -0
- package/packages/db/dist/schema/index.js +12 -0
- package/packages/db/dist/schema/message-log.js +20 -0
- package/packages/db/dist/schema/opportunities.js +23 -0
- package/packages/db/dist/schema/org.js +36 -0
- package/packages/db/dist/schema/projects.js +23 -0
- package/packages/db/dist/schema/tasks.js +46 -0
- package/packages/db/dist/schema/trace-spans.js +19 -0
- package/packages/db/package.json +12 -0
- package/packages/db/src/migrations/0000_simple_magneto.sql +148 -0
- package/packages/db/src/migrations/0001_nifty_morph.sql +42 -0
- package/packages/db/src/migrations/0002_common_joshua_kane.sql +20 -0
- package/packages/db/src/migrations/0003_add_cc_sessions.sql +13 -0
- package/packages/db/src/migrations/0004_jittery_triathlon.sql +1 -0
- package/packages/db/src/migrations/meta/0000_snapshot.json +987 -0
- package/packages/db/src/migrations/meta/0001_snapshot.json +1280 -0
- package/packages/db/src/migrations/meta/0002_snapshot.json +1417 -0
- package/packages/db/src/migrations/meta/0004_snapshot.json +1505 -0
- package/packages/db/src/migrations/meta/_journal.json +41 -0
- package/packages/mcp-tools/dist/index.js +41 -0
- package/packages/mcp-tools/dist/layer1/audit-append.js +38 -0
- package/packages/mcp-tools/dist/layer1/audit-query.js +51 -0
- package/packages/mcp-tools/dist/layer1/memory-brief.js +168 -0
- package/packages/mcp-tools/dist/layer1/memory-context.js +124 -0
- package/packages/mcp-tools/dist/layer1/memory-digest.js +126 -0
- package/packages/mcp-tools/dist/layer1/memory-forget.js +108 -0
- package/packages/mcp-tools/dist/layer1/memory-learn.js +63 -0
- package/packages/mcp-tools/dist/layer1/memory-recall.js +287 -0
- package/packages/mcp-tools/dist/layer1/memory-reflect.js +80 -0
- package/packages/mcp-tools/dist/layer1/memory-remember.js +119 -0
- package/packages/mcp-tools/dist/layer1/memory-search.js +263 -0
- package/packages/mcp-tools/dist/layer1/memory-write.js +21 -0
- package/packages/mcp-tools/dist/layer1/org-lookup.js +47 -0
- package/packages/mcp-tools/dist/layer1/project-get.js +28 -0
- package/packages/mcp-tools/dist/layer1/project-list.js +20 -0
- package/packages/mcp-tools/dist/layer1/report-daily.js +68 -0
- package/packages/mcp-tools/dist/layer1/task-get.js +29 -0
- package/packages/mcp-tools/dist/layer1/task-update.js +34 -0
- package/packages/mcp-tools/dist/layer2/loid/decision-log.js +15 -0
- package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +15 -0
- package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +30 -0
- package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +12 -0
- package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +77 -0
- package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +18 -0
- package/packages/mcp-tools/dist/layer2/loid/project-remove.js +16 -0
- package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +33 -0
- package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +177 -0
- package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +38 -0
- package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +8 -0
- package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +7 -0
- package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +7 -0
- package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +15 -0
- package/packages/mcp-tools/dist/layer2/loid/yor-status.js +8 -0
- package/packages/mcp-tools/dist/layer2/yor/task-block.js +11 -0
- package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +35 -0
- package/packages/mcp-tools/dist/layer2/yor/task-progress.js +21 -0
- package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +191 -0
- package/packages/mcp-tools/dist/layer3/adapters/types.js +28 -0
- package/packages/mcp-tools/dist/layer3/channel-receive.js +11 -0
- package/packages/mcp-tools/dist/layer3/channel-send.js +90 -0
- package/packages/mcp-tools/dist/layer3/file-upload.js +44 -0
- package/packages/mcp-tools/dist/registry.js +779 -0
- package/packages/mcp-tools/package.json +13 -0
- package/workspace/.claude/settings.local.json +9 -0
- package/workspace/.mcp.json +12 -0
- package/workspace/CHARTER.md +73 -0
- package/workspace/CLAUDE.md +49 -0
- package/workspace/PROTOCOL.md +126 -0
- package/workspace/TOOLS.md +464 -0
- package/workspace/audit/.gitkeep +0 -0
- package/workspace/loid/CLAUDE.md +12 -0
- package/workspace/loid/PLAYBOOK.md +198 -0
- package/workspace/loid/PROFILE.md +78 -0
- package/workspace/memory/commitments/.gitkeep +0 -0
- package/workspace/memory/execution/.gitkeep +0 -0
- package/workspace/memory/people/.gitkeep +0 -0
- package/workspace/memory/projects/.gitkeep +0 -0
- package/workspace/memory/self/.gitkeep +0 -0
- package/workspace/reference/identity/.gitkeep +0 -0
- package/workspace/reference/org/escalation.yaml +24 -0
- package/workspace/reference/org/ownership.yaml +28 -0
- package/workspace/reports/.gitkeep +0 -0
- package/workspace/yor/CLAUDE.md +22 -0
- package/workspace/yor/PLAYBOOK.md +73 -0
- package/workspace/yor/PROFILE.md +52 -0
- package/workspace/yor/SELF-HEAL.md +39 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { getChat, upsertChat, upsertChatMember, removeChatMembers, upsertOrgMember } from '@team-anya/db';
|
|
2
|
+
const SYNC_INTERVAL_MS = 60 * 60 * 1000; // 1 小时内不重复同步
|
|
3
|
+
export class ChatSyncService {
|
|
4
|
+
larkClient;
|
|
5
|
+
db;
|
|
6
|
+
syncing = new Set(); // 防并发重复同步
|
|
7
|
+
constructor(larkClient, db) {
|
|
8
|
+
this.larkClient = larkClient;
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
/** 检查是否需要同步(不存在或超过同步间隔) */
|
|
12
|
+
needsSync(chatId) {
|
|
13
|
+
const chat = getChat(this.db, chatId);
|
|
14
|
+
if (!chat)
|
|
15
|
+
return true;
|
|
16
|
+
if (!chat.last_synced_at)
|
|
17
|
+
return true;
|
|
18
|
+
const elapsed = Date.now() - new Date(chat.last_synced_at).getTime();
|
|
19
|
+
return elapsed > SYNC_INTERVAL_MS;
|
|
20
|
+
}
|
|
21
|
+
/** 同步单个群信息 */
|
|
22
|
+
async syncChat(chatId) {
|
|
23
|
+
try {
|
|
24
|
+
const resp = await this.larkClient.im.chat.get({
|
|
25
|
+
path: { chat_id: chatId },
|
|
26
|
+
});
|
|
27
|
+
const chatData = resp?.data;
|
|
28
|
+
if (!chatData)
|
|
29
|
+
return;
|
|
30
|
+
upsertChat(this.db, {
|
|
31
|
+
chat_id: chatId,
|
|
32
|
+
platform: 'feishu',
|
|
33
|
+
name: chatData.name ?? null,
|
|
34
|
+
description: chatData.description ?? null,
|
|
35
|
+
avatar: chatData.avatar ?? null,
|
|
36
|
+
owner_id: chatData.owner_id ?? null,
|
|
37
|
+
chat_mode: chatData.chat_mode ?? null,
|
|
38
|
+
chat_type: chatData.chat_type ?? null,
|
|
39
|
+
chat_tag: chatData.chat_tag ?? null,
|
|
40
|
+
external: chatData.external ? 1 : 0,
|
|
41
|
+
tenant_key: chatData.tenant_key ?? null,
|
|
42
|
+
user_count: typeof chatData.user_count === 'string'
|
|
43
|
+
? parseInt(chatData.user_count, 10)
|
|
44
|
+
: (chatData.user_count ?? null),
|
|
45
|
+
bot_count: typeof chatData.bot_count === 'string'
|
|
46
|
+
? parseInt(chatData.bot_count, 10)
|
|
47
|
+
: (chatData.bot_count ?? null),
|
|
48
|
+
last_synced_at: new Date().toISOString(),
|
|
49
|
+
});
|
|
50
|
+
console.log(`[chat-sync] 群信息同步完成: ${chatId} (${chatData.name ?? 'unnamed'})`);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
console.warn(`[chat-sync] 同步群信息失败 (${chatId}):`, err);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** 同步群成员(全量替换) */
|
|
57
|
+
async syncChatMembers(chatId) {
|
|
58
|
+
try {
|
|
59
|
+
// 清除旧成员数据
|
|
60
|
+
removeChatMembers(this.db, chatId);
|
|
61
|
+
let pageToken;
|
|
62
|
+
let memberCount = 0;
|
|
63
|
+
do {
|
|
64
|
+
const resp = await this.larkClient.im.chatMembers.get({
|
|
65
|
+
path: { chat_id: chatId },
|
|
66
|
+
params: {
|
|
67
|
+
member_id_type: 'open_id',
|
|
68
|
+
...(pageToken ? { page_token: pageToken } : {}),
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const items = resp?.data?.items ?? [];
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
const openId = item.member_id;
|
|
74
|
+
if (!openId)
|
|
75
|
+
continue;
|
|
76
|
+
// 同步用户完整信息到 org_members
|
|
77
|
+
await this.resolveAndUpsertMember(openId);
|
|
78
|
+
// 写入 chat_members 关联
|
|
79
|
+
upsertChatMember(this.db, {
|
|
80
|
+
chat_id: chatId,
|
|
81
|
+
member_id: openId,
|
|
82
|
+
role: item.member_id_type === 'owner' ? 'owner' : 'member',
|
|
83
|
+
});
|
|
84
|
+
memberCount++;
|
|
85
|
+
}
|
|
86
|
+
pageToken = resp?.data?.page_token || undefined;
|
|
87
|
+
} while (pageToken);
|
|
88
|
+
console.log(`[chat-sync] 群成员同步完成: ${chatId}, 共 ${memberCount} 人`);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
console.warn(`[chat-sync] 同步群成员失败 (${chatId}):`, err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** 完整同步(群信息 + 成员) */
|
|
95
|
+
async syncChatFull(chatId) {
|
|
96
|
+
if (this.syncing.has(chatId))
|
|
97
|
+
return;
|
|
98
|
+
this.syncing.add(chatId);
|
|
99
|
+
try {
|
|
100
|
+
await this.syncChat(chatId);
|
|
101
|
+
await this.syncChatMembers(chatId);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
this.syncing.delete(chatId);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** 解析飞书用户完整信息并 upsert 到 org_members */
|
|
108
|
+
async resolveAndUpsertMember(openId) {
|
|
109
|
+
try {
|
|
110
|
+
const resp = await this.larkClient.contact.user.get({
|
|
111
|
+
path: { user_id: openId },
|
|
112
|
+
params: { user_id_type: 'open_id' },
|
|
113
|
+
});
|
|
114
|
+
const user = resp?.data?.user;
|
|
115
|
+
if (!user?.name)
|
|
116
|
+
return;
|
|
117
|
+
upsertOrgMember(this.db, {
|
|
118
|
+
member_id: openId,
|
|
119
|
+
name: user.name,
|
|
120
|
+
platform: 'feishu',
|
|
121
|
+
union_id: user.union_id ?? undefined,
|
|
122
|
+
user_id: user.user_id ?? undefined,
|
|
123
|
+
en_name: user.en_name ?? undefined,
|
|
124
|
+
email: user.email ?? undefined,
|
|
125
|
+
employee_no: user.employee_no ?? undefined,
|
|
126
|
+
avatar_url: user.avatar?.avatar_origin ?? user.avatar?.avatar_72 ?? undefined,
|
|
127
|
+
last_synced_at: new Date().toISOString(),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.warn(`[chat-sync] 解析用户信息失败 (${openId}):`, err);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=chat-sync.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// ── CommandRouter ──
|
|
2
|
+
export class CommandRouter {
|
|
3
|
+
commands = new Map();
|
|
4
|
+
deps;
|
|
5
|
+
constructor(deps) {
|
|
6
|
+
this.deps = deps;
|
|
7
|
+
}
|
|
8
|
+
register(cmd) {
|
|
9
|
+
this.commands.set(cmd.name, cmd);
|
|
10
|
+
for (const alias of cmd.aliases) {
|
|
11
|
+
this.commands.set(alias, cmd);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
getCommands() {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const result = [];
|
|
17
|
+
for (const cmd of this.commands.values()) {
|
|
18
|
+
if (!seen.has(cmd.name)) {
|
|
19
|
+
seen.add(cmd.name);
|
|
20
|
+
result.push(cmd);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 尝试处理消息。返回 true 表示已拦截(不需要送 Loid)。
|
|
27
|
+
*/
|
|
28
|
+
async tryHandle(message) {
|
|
29
|
+
const parsed = this.parseCommand(message);
|
|
30
|
+
if (!parsed)
|
|
31
|
+
return false;
|
|
32
|
+
const { commandName, args } = parsed;
|
|
33
|
+
const cmd = this.commands.get(commandName);
|
|
34
|
+
if (!cmd)
|
|
35
|
+
return false;
|
|
36
|
+
const logger = this.deps.logger;
|
|
37
|
+
logger?.info(`[CommandRouter] 匹配命令: /${cmd.name} (category: ${cmd.category})`);
|
|
38
|
+
// pass-through:标记 metadata 后继续送 Loid
|
|
39
|
+
if (cmd.category === 'pass-through') {
|
|
40
|
+
if (!message.metadata)
|
|
41
|
+
message.metadata = {};
|
|
42
|
+
message.metadata._command = cmd.name;
|
|
43
|
+
message.metadata._commandArgs = args;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// intercepted / control:执行 handler 并直接回复
|
|
47
|
+
try {
|
|
48
|
+
const ctx = {
|
|
49
|
+
message,
|
|
50
|
+
args,
|
|
51
|
+
db: this.deps.db,
|
|
52
|
+
broker: this.deps.broker,
|
|
53
|
+
router: this,
|
|
54
|
+
onRestart: this.deps.onRestart,
|
|
55
|
+
};
|
|
56
|
+
const result = await cmd.handler(ctx);
|
|
57
|
+
if (result) {
|
|
58
|
+
await this.sendResponse(message, result);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
logger?.error(`[CommandRouter] 命令执行失败 (/${cmd.name}):`, err);
|
|
63
|
+
await this.sendResponse(message, {
|
|
64
|
+
text: `命令执行出错: ${err instanceof Error ? err.message : String(err)}`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 解析消息中的命令名和参数。
|
|
71
|
+
* 群聊需要 mentionsAnya,私聊直接匹配。
|
|
72
|
+
*/
|
|
73
|
+
parseCommand(message) {
|
|
74
|
+
let text = message.content.trim();
|
|
75
|
+
// 群聊:需要 @Anya
|
|
76
|
+
if (!message.isDirectMessage) {
|
|
77
|
+
if (!message.mentionsAnya)
|
|
78
|
+
return null;
|
|
79
|
+
// 去掉 @mention 前缀(飞书格式通常是 @_user_1 或已被替换为空)
|
|
80
|
+
text = text.replace(/^@\S+\s*/, '').trim();
|
|
81
|
+
}
|
|
82
|
+
// 匹配 /command 格式
|
|
83
|
+
const slashMatch = text.match(/^\/(\S+)(?:\s+(.*))?$/s);
|
|
84
|
+
if (slashMatch) {
|
|
85
|
+
return { commandName: slashMatch[1].toLowerCase(), args: (slashMatch[2] ?? '').trim() };
|
|
86
|
+
}
|
|
87
|
+
// 裸词匹配:仅对已注册命令名/别名生效
|
|
88
|
+
const firstWord = text.split(/\s+/)[0]?.toLowerCase();
|
|
89
|
+
if (firstWord && this.commands.has(firstWord)) {
|
|
90
|
+
const rest = text.slice(firstWord.length).trim();
|
|
91
|
+
return { commandName: firstWord, args: rest };
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
async sendResponse(message, result) {
|
|
96
|
+
const sender = this.deps.feishuSender;
|
|
97
|
+
const messageId = message.metadata?.message_id;
|
|
98
|
+
try {
|
|
99
|
+
if (messageId) {
|
|
100
|
+
await sender.sendReply({ text: result.text, replyToMessageId: messageId });
|
|
101
|
+
}
|
|
102
|
+
else if (message.chatId) {
|
|
103
|
+
await sender.sendText({ receiveIdType: 'chat_id', receiveId: message.chatId, text: result.text });
|
|
104
|
+
}
|
|
105
|
+
else if (message.sender) {
|
|
106
|
+
await sender.sendText({ receiveIdType: 'open_id', receiveId: message.sender, text: result.text });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
this.deps.logger?.error('[CommandRouter] 发送回复失败:', err);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=command-router.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getTask, updateTask, insertAuditEvent } from '@team-anya/db';
|
|
2
|
+
import { TaskStatus, validateTransition } from '@team-anya/core';
|
|
3
|
+
export const cancelCommand = {
|
|
4
|
+
name: 'cancel',
|
|
5
|
+
aliases: ['取消'],
|
|
6
|
+
category: 'control',
|
|
7
|
+
description: '取消任务(/cancel <task_id>)',
|
|
8
|
+
handler: async (ctx) => {
|
|
9
|
+
const taskId = ctx.args.trim();
|
|
10
|
+
if (!taskId) {
|
|
11
|
+
return { text: '请指定任务 ID,例如: /cancel ANYA-20260224-001' };
|
|
12
|
+
}
|
|
13
|
+
const task = getTask(ctx.db, taskId);
|
|
14
|
+
if (!task) {
|
|
15
|
+
return { text: `任务 ${taskId} 不存在` };
|
|
16
|
+
}
|
|
17
|
+
const currentStatus = task.status;
|
|
18
|
+
if (!validateTransition(currentStatus, TaskStatus.CANCELLED)) {
|
|
19
|
+
return { text: `任务 ${taskId} 当前状态为 ${currentStatus},无法取消` };
|
|
20
|
+
}
|
|
21
|
+
updateTask(ctx.db, taskId, { status: TaskStatus.CANCELLED });
|
|
22
|
+
insertAuditEvent(ctx.db, {
|
|
23
|
+
event_type: 'command_invoked',
|
|
24
|
+
actor: ctx.message.senderName ?? ctx.message.sender ?? 'unknown',
|
|
25
|
+
summary: `取消任务 ${taskId}`,
|
|
26
|
+
task_id: taskId,
|
|
27
|
+
detail: JSON.stringify({ command: 'cancel', from: currentStatus, to: TaskStatus.CANCELLED }),
|
|
28
|
+
});
|
|
29
|
+
return { text: `任务 ${taskId} 已取消(${currentStatus} → CANCELLED)` };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=cancel.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const helpCommand = {
|
|
2
|
+
name: 'help',
|
|
3
|
+
aliases: ['h', '帮助'],
|
|
4
|
+
category: 'intercepted',
|
|
5
|
+
description: '显示命令列表',
|
|
6
|
+
handler: async (ctx) => {
|
|
7
|
+
const commands = ctx.router.getCommands();
|
|
8
|
+
const lines = ['📋 可用命令:', ''];
|
|
9
|
+
for (const cmd of commands) {
|
|
10
|
+
const aliases = cmd.aliases.length > 0 ? ` (${cmd.aliases.join(', ')})` : '';
|
|
11
|
+
lines.push(`/${cmd.name}${aliases} — ${cmd.description}`);
|
|
12
|
+
}
|
|
13
|
+
return { text: lines.join('\n') };
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { helpCommand } from './help.js';
|
|
2
|
+
import { statusCommand } from './status.js';
|
|
3
|
+
import { tasksCommand } from './tasks.js';
|
|
4
|
+
import { cancelCommand } from './cancel.js';
|
|
5
|
+
import { restartCommand } from './restart.js';
|
|
6
|
+
// pass-through 命令(仅标记 metadata,透传给 Loid)
|
|
7
|
+
const passThrough = (name, description) => ({
|
|
8
|
+
name,
|
|
9
|
+
aliases: [],
|
|
10
|
+
category: 'pass-through',
|
|
11
|
+
description,
|
|
12
|
+
handler: async () => null,
|
|
13
|
+
});
|
|
14
|
+
export function registerBuiltinCommands(router) {
|
|
15
|
+
// 直接处理
|
|
16
|
+
router.register(helpCommand);
|
|
17
|
+
router.register(statusCommand);
|
|
18
|
+
router.register(tasksCommand);
|
|
19
|
+
router.register(cancelCommand);
|
|
20
|
+
router.register(restartCommand);
|
|
21
|
+
// 透传 Loid
|
|
22
|
+
router.register(passThrough('task', '显式创建任务(透传 Loid)'));
|
|
23
|
+
router.register(passThrough('report', '生成报告(透传 Loid)'));
|
|
24
|
+
router.register(passThrough('review', '审核交付(透传 Loid)'));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const restartCommand = {
|
|
2
|
+
name: 'restart',
|
|
3
|
+
aliases: ['重启'],
|
|
4
|
+
category: 'control',
|
|
5
|
+
description: '重启所有 Claude Code 实例(安装 skills 后需要重启生效)',
|
|
6
|
+
handler: async (ctx) => {
|
|
7
|
+
if (!ctx.onRestart) {
|
|
8
|
+
return { text: '重启功能未配置' };
|
|
9
|
+
}
|
|
10
|
+
// 列出当前实例
|
|
11
|
+
const instances = ctx.broker.status();
|
|
12
|
+
const activeCount = instances.filter(i => i.state !== 'disposed').length;
|
|
13
|
+
if (activeCount === 0) {
|
|
14
|
+
return { text: '当前没有活跃的 CC 实例,无需重启' };
|
|
15
|
+
}
|
|
16
|
+
// 检查正在执行任务的 Yor 实例
|
|
17
|
+
const executingYor = instances.filter(i => i.role === 'yor' && i.state === 'executing');
|
|
18
|
+
let warning = '';
|
|
19
|
+
if (executingYor.length > 0) {
|
|
20
|
+
const taskIds = executingYor.map(i => i.taskId ?? i.id).join(', ');
|
|
21
|
+
warning = `\n\n⚠️ 注意: ${executingYor.length} 个 Yor 实例正在执行任务 (${taskIds}),已强制终止`;
|
|
22
|
+
}
|
|
23
|
+
const result = await ctx.onRestart();
|
|
24
|
+
const lines = [
|
|
25
|
+
'🔄 CC 实例已全部重启',
|
|
26
|
+
'',
|
|
27
|
+
`已释放实例: ${result.disposed} 个`,
|
|
28
|
+
'Loid 会话将在下次收到消息时自动重建',
|
|
29
|
+
'Yor 实例将在下次派工时重新创建',
|
|
30
|
+
];
|
|
31
|
+
return { text: lines.join('\n') + warning };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=restart.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getTaskCountByStatus } from '@team-anya/db';
|
|
2
|
+
export const statusCommand = {
|
|
3
|
+
name: 'status',
|
|
4
|
+
aliases: ['s', '状态'],
|
|
5
|
+
category: 'intercepted',
|
|
6
|
+
description: '系统状态概览',
|
|
7
|
+
handler: async (ctx) => {
|
|
8
|
+
const counts = getTaskCountByStatus(ctx.db);
|
|
9
|
+
const total = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
10
|
+
const yorInstances = ctx.broker.getInstancesByRole('yor');
|
|
11
|
+
const activeYorCount = yorInstances.length;
|
|
12
|
+
const lines = ['📊 系统状态', ''];
|
|
13
|
+
lines.push(`任务总数: ${total}`);
|
|
14
|
+
if (total > 0) {
|
|
15
|
+
const statusLabels = {
|
|
16
|
+
NEW: '新建',
|
|
17
|
+
NEED_CLARIFICATION: '待澄清',
|
|
18
|
+
READY: '就绪',
|
|
19
|
+
IN_PROGRESS: '执行中',
|
|
20
|
+
DELIVERING: '交付中',
|
|
21
|
+
DONE: '已完成',
|
|
22
|
+
BLOCKED: '已阻塞',
|
|
23
|
+
CANCELLED: '已取消',
|
|
24
|
+
};
|
|
25
|
+
for (const [status, count] of Object.entries(counts)) {
|
|
26
|
+
const label = statusLabels[status] ?? status;
|
|
27
|
+
lines.push(` ${label}: ${count}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
lines.push(`\n活跃 Yor 实例: ${activeYorCount}`);
|
|
31
|
+
return { text: lines.join('\n') };
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getAllTasks, getTasksByChatId, getTasksByStatus } from '@team-anya/db';
|
|
2
|
+
const MAX_DISPLAY = 15;
|
|
3
|
+
export const tasksCommand = {
|
|
4
|
+
name: 'tasks',
|
|
5
|
+
aliases: ['t', '任务'],
|
|
6
|
+
category: 'intercepted',
|
|
7
|
+
description: '任务列表(支持状态过滤:/tasks in_progress)',
|
|
8
|
+
handler: async (ctx) => {
|
|
9
|
+
const statusFilter = ctx.args.toUpperCase().trim();
|
|
10
|
+
let taskList;
|
|
11
|
+
if (statusFilter) {
|
|
12
|
+
taskList = getTasksByStatus(ctx.db, statusFilter);
|
|
13
|
+
}
|
|
14
|
+
else if (!ctx.message.isDirectMessage && ctx.message.chatId) {
|
|
15
|
+
taskList = getTasksByChatId(ctx.db, ctx.message.chatId);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
taskList = getAllTasks(ctx.db);
|
|
19
|
+
}
|
|
20
|
+
if (taskList.length === 0) {
|
|
21
|
+
const hint = statusFilter ? `(状态: ${statusFilter})` : '';
|
|
22
|
+
return { text: `暂无任务${hint}` };
|
|
23
|
+
}
|
|
24
|
+
const displayed = taskList.slice(0, MAX_DISPLAY);
|
|
25
|
+
const lines = [`📝 任务列表(共 ${taskList.length} 条${taskList.length > MAX_DISPLAY ? `,显示前 ${MAX_DISPLAY} 条` : ''}):`, ''];
|
|
26
|
+
for (const task of displayed) {
|
|
27
|
+
const status = task.status.padEnd(18);
|
|
28
|
+
lines.push(`${task.task_id} ${status} ${task.title ?? '(无标题)'}`);
|
|
29
|
+
}
|
|
30
|
+
return { text: lines.join('\n') };
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=tasks.js.map
|