team-anya-cli 0.1.4 → 0.1.6

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.
Files changed (46) hide show
  1. package/apps/server/dist/broker/cc-broker.js +5 -1
  2. package/apps/server/dist/franky/context-builder.js +160 -0
  3. package/apps/server/dist/franky/franky-mcp-server.js +107 -0
  4. package/apps/server/dist/franky/franky-orchestrator.js +450 -0
  5. package/apps/server/dist/franky/index.js +5 -0
  6. package/apps/server/dist/franky/topic-router.js +16 -0
  7. package/apps/server/dist/gateway/feishu-sender.js +66 -2
  8. package/apps/server/dist/gateway/feishu-ws.js +5 -4
  9. package/apps/server/dist/gateway/message-intake.js +21 -4
  10. package/apps/server/dist/loid/brain.js +1 -0
  11. package/apps/server/dist/loid/mcp-server.js +1 -0
  12. package/apps/server/dist/loid/session-manager.js +95 -11
  13. package/apps/server/dist/main.js +58 -3
  14. package/apps/web/dist/assets/index-BiiEB0qZ.css +1 -0
  15. package/apps/web/dist/assets/{index-CJzAjoVH.js → index-D1AK5ZEE.js} +189 -189
  16. package/apps/web/dist/index.html +2 -2
  17. package/package.json +1 -1
  18. package/packages/core/dist/office-init.js +4 -0
  19. package/packages/core/dist/scope/defaults.js +15 -0
  20. package/packages/core/dist/scope/index.js +1 -1
  21. package/packages/db/dist/index.js +95 -7
  22. package/packages/db/dist/schema/cc-sessions.js +1 -1
  23. package/packages/db/dist/schema/index.js +1 -0
  24. package/packages/db/dist/schema/tasks.js +2 -0
  25. package/packages/db/dist/schema/topics.js +20 -0
  26. package/packages/db/src/migrations/0005_lethal_golden_guardian.sql +5 -0
  27. package/packages/db/src/migrations/0006_add_topics.sql +21 -0
  28. package/packages/db/src/migrations/0007_add_topic_root_message_id.sql +1 -0
  29. package/packages/db/src/migrations/meta/0005_snapshot.json +1513 -0
  30. package/packages/db/src/migrations/meta/0006_snapshot.json +1513 -0
  31. package/packages/db/src/migrations/meta/_journal.json +21 -0
  32. package/packages/mcp-tools/dist/layer2/franky/topic-checkpoint.js +43 -0
  33. package/packages/mcp-tools/dist/layer2/franky/topic-escalate.js +19 -0
  34. package/packages/mcp-tools/dist/layer2/loid/topic-close.js +22 -0
  35. package/packages/mcp-tools/dist/layer2/loid/topic-create.js +56 -0
  36. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +1 -0
  37. package/packages/mcp-tools/dist/layer3/channel-send.js +1 -0
  38. package/packages/mcp-tools/dist/registry.js +105 -17
  39. package/workspace/CHARTER.md +7 -4
  40. package/workspace/CLAUDE.md +18 -9
  41. package/workspace/PROTOCOL.md +35 -1
  42. package/workspace/TOOLS.md +6 -0
  43. package/workspace/franky/CLAUDE.md +37 -0
  44. package/workspace/franky/PLAYBOOK.md +215 -0
  45. package/workspace/franky/PROFILE.md +80 -0
  46. package/apps/web/dist/assets/index-CHIT0Dya.css +0 -1
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Anya Gateway Dashboard</title>
7
- <script type="module" crossorigin src="/assets/index-CJzAjoVH.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-CHIT0Dya.css">
7
+ <script type="module" crossorigin src="/assets/index-D1AK5ZEE.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-BiiEB0qZ.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "team-anya-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module",
5
5
  "description": "Team Anya - AI 数字员工系统",
6
6
  "bin": {
@@ -13,6 +13,9 @@ const ALWAYS_OVERWRITE_FILES = [
13
13
  'yor/PROFILE.md',
14
14
  'yor/PLAYBOOK.md',
15
15
  'yor/SELF-HEAL.md',
16
+ 'franky/CLAUDE.md',
17
+ 'franky/PROFILE.md',
18
+ 'franky/PLAYBOOK.md',
16
19
  ];
17
20
  /** 仅当目标不存在时才复制的文件(配置/数据,运行时可能被修改) */
18
21
  const COPY_IF_MISSING_FILES = [
@@ -34,6 +37,7 @@ const REQUIRED_DIRS = [
34
37
  'reports',
35
38
  'yor',
36
39
  'loid',
40
+ 'franky',
37
41
  '.claude',
38
42
  ];
39
43
  async function exists(path) {
@@ -37,4 +37,19 @@ export const DEFAULT_FORBIDDEN_PATHS = [
37
37
  '**/.git/objects/**',
38
38
  '**/.git/refs/**',
39
39
  ];
40
+ /**
41
+ * Franky 专项模式的默认 scope
42
+ * 比 Yor 更宽松:允许 git push 到 feature 分支,允许创建 PR
43
+ * 但仍禁止 push main/master 和 force push
44
+ */
45
+ export const FRANKY_DEFAULT_SCOPE = {
46
+ allowedPaths: ['**'],
47
+ forbiddenPaths: DEFAULT_FORBIDDEN_PATHS,
48
+ allowedCommands: ['**'],
49
+ forbiddenCommands: [
50
+ 'git push\\s+--force',
51
+ 'git push\\b.*--force',
52
+ 'git push\\b.*\\b(main|master)\\b',
53
+ ],
54
+ };
40
55
  //# sourceMappingURL=defaults.js.map
@@ -1,3 +1,3 @@
1
1
  export { ScopeChecker, DEFAULT_SCOPE } from './checker.js';
2
- export { DEFAULT_FORBIDDEN_COMMANDS, DEFAULT_FORBIDDEN_PATHS } from './defaults.js';
2
+ export { DEFAULT_FORBIDDEN_COMMANDS, DEFAULT_FORBIDDEN_PATHS, FRANKY_DEFAULT_SCOPE } from './defaults.js';
3
3
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  import { eq, desc, gte, like, and, sql, count, isNull, isNotNull } from 'drizzle-orm';
2
- import { tasks, taskClarifications, commitments, opportunities, auditEvents, orgMembers, orgOwnership, orgEscalationRules, communicationEvents, messageLog, traceSpans, chats, chatMembers, projects, projectRepos, ccSessions } from './schema/index.js';
2
+ import { tasks, taskClarifications, commitments, opportunities, auditEvents, orgMembers, orgOwnership, orgEscalationRules, communicationEvents, messageLog, traceSpans, chats, chatMembers, projects, projectRepos, ccSessions, topics } from './schema/index.js';
3
3
  export { createDB, createTestDB } from './client.js';
4
4
  export * from './schema/index.js';
5
5
  // ── Task CRUD ──
@@ -236,11 +236,32 @@ export function getRecentMessages(db, opts = {}) {
236
236
  conditions.push(eq(messageLog.chat_id, chat_id));
237
237
  if (chat_type)
238
238
  conditions.push(eq(messageLog.chat_type, chat_type));
239
- const query = conditions.length > 0
240
- ? db.select().from(messageLog).where(and(...conditions))
241
- : db.select().from(messageLog);
239
+ const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
242
240
  const orderBy = sortOrder === 'asc' ? messageLog.created_at : desc(messageLog.created_at);
243
- return query
241
+ return db
242
+ .select({
243
+ id: messageLog.id,
244
+ direction: messageLog.direction,
245
+ source_type: messageLog.source_type,
246
+ source_ref: messageLog.source_ref,
247
+ sender: messageLog.sender,
248
+ sender_name: sql `(SELECT name FROM org_members WHERE member_id = ${messageLog.sender})`,
249
+ receiver: messageLog.receiver,
250
+ receiver_name: sql `(SELECT name FROM org_members WHERE member_id = ${messageLog.receiver})`,
251
+ chat_id: messageLog.chat_id,
252
+ chat_type: messageLog.chat_type,
253
+ content: messageLog.content,
254
+ message_type: messageLog.message_type,
255
+ intent_level: messageLog.intent_level,
256
+ related_task_id: messageLog.related_task_id,
257
+ metadata: messageLog.metadata,
258
+ trace_id: messageLog.trace_id,
259
+ created_at: messageLog.created_at,
260
+ chat_name: chats.name,
261
+ })
262
+ .from(messageLog)
263
+ .leftJoin(chats, eq(messageLog.chat_id, chats.chat_id))
264
+ .where(whereClause)
244
265
  .orderBy(orderBy)
245
266
  .limit(limit)
246
267
  .offset(offset)
@@ -425,7 +446,7 @@ export function getMemberChats(db, memberId) {
425
446
  return db.select({
426
447
  chat_id: chats.chat_id,
427
448
  name: chats.name,
428
- chat_type: chats.chat_type,
449
+ chat_mode: chats.chat_mode,
429
450
  chat_tag: chats.chat_tag,
430
451
  user_count: chats.user_count,
431
452
  role: chatMembers.role,
@@ -506,7 +527,7 @@ export function getProjectWithRepos(db, projectId) {
506
527
  }
507
528
  // ── CC Sessions CRUD ──
508
529
  export function insertCCSession(db, data) {
509
- return db.insert(ccSessions).values(data).returning().get();
530
+ return db.insert(ccSessions).values(data).onConflictDoNothing({ target: ccSessions.session_id }).returning().get();
510
531
  }
511
532
  export function getCCSessionsByTask(db, taskId) {
512
533
  return db.select().from(ccSessions)
@@ -600,4 +621,71 @@ export function getAllProjectsWithStats(db, includeDeleted = false) {
600
621
  last_activity_at: tasksByProject[p.project_id]?.last_activity ?? p.updated_at,
601
622
  }));
602
623
  }
624
+ export function upsertTopic(db, input) {
625
+ const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
626
+ return db.insert(topics).values({
627
+ id: input.id,
628
+ project_id: input.project_id ?? null,
629
+ title: input.title,
630
+ description: input.description ?? null,
631
+ status: input.status,
632
+ thread_id: input.thread_id ?? null,
633
+ root_message_id: input.root_message_id ?? null,
634
+ chat_id: input.chat_id ?? null,
635
+ workspace_path: input.workspace_path ?? null,
636
+ branch_name: input.branch_name ?? null,
637
+ created_by: input.created_by ?? null,
638
+ created_at: now,
639
+ updated_at: now,
640
+ }).onConflictDoUpdate({
641
+ target: topics.id,
642
+ set: {
643
+ title: input.title,
644
+ description: input.description ?? null,
645
+ status: input.status,
646
+ thread_id: input.thread_id ?? null,
647
+ root_message_id: input.root_message_id ?? null,
648
+ chat_id: input.chat_id ?? null,
649
+ workspace_path: input.workspace_path ?? null,
650
+ branch_name: input.branch_name ?? null,
651
+ updated_at: now,
652
+ },
653
+ }).run();
654
+ }
655
+ export function getTopic(db, id) {
656
+ return db.select().from(topics).where(eq(topics.id, id)).get();
657
+ }
658
+ export function getTopicByThreadId(db, threadId) {
659
+ return db.select().from(topics)
660
+ .where(and(eq(topics.thread_id, threadId), eq(topics.status, 'active')))
661
+ .get();
662
+ }
663
+ export function updateTopicStatus(db, id, status) {
664
+ const now = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
665
+ return db.update(topics).set({
666
+ status,
667
+ updated_at: now,
668
+ ...(status === 'closed' ? { closed_at: now } : {}),
669
+ }).where(eq(topics.id, id)).run();
670
+ }
671
+ export function getActiveTopics(db) {
672
+ return db.select().from(topics).where(eq(topics.status, 'active')).all();
673
+ }
674
+ export function getTodayMaxTopicSequence(db) {
675
+ const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
676
+ const prefix = `TOPIC-${today}-`;
677
+ const rows = db.select().from(topics).where(like(topics.id, `${prefix}%`)).all();
678
+ let max = 0;
679
+ for (const topic of rows) {
680
+ const seq = parseInt(topic.id.split('-')[2], 10);
681
+ if (seq > max)
682
+ max = seq;
683
+ }
684
+ return max;
685
+ }
686
+ export function generateTopicId(db) {
687
+ const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
688
+ const max = getTodayMaxTopicSequence(db);
689
+ return `TOPIC-${today}-${String(max + 1).padStart(3, '0')}`;
690
+ }
603
691
  //# sourceMappingURL=index.js.map
@@ -2,7 +2,7 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
2
  import { sql } from 'drizzle-orm';
3
3
  export const ccSessions = sqliteTable('cc_sessions', {
4
4
  id: integer('id').primaryKey({ autoIncrement: true }),
5
- session_id: text('session_id').notNull(),
5
+ session_id: text('session_id').notNull().unique(),
6
6
  role: text('role').notNull(), // 'loid' | 'yor'
7
7
  instance_id: text('instance_id'),
8
8
  task_id: text('task_id'),
@@ -9,4 +9,5 @@ export * from './chats.js';
9
9
  export * from './trace-spans.js';
10
10
  export * from './projects.js';
11
11
  export * from './cc-sessions.js';
12
+ export * from './topics.js';
12
13
  //# sourceMappingURL=index.js.map
@@ -29,6 +29,8 @@ export const tasks = sqliteTable('tasks', {
29
29
  cc_session_id: text('cc_session_id'),
30
30
  // 文件路径
31
31
  workspace_path: text('workspace_path'),
32
+ // 专项关联(可选,用于回溯关联)
33
+ topic_id: text('topic_id'),
32
34
  // 时间戳
33
35
  created_at: text('created_at').notNull().default(sql `(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))`),
34
36
  updated_at: text('updated_at').notNull().default(sql `(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))`),
@@ -0,0 +1,20 @@
1
+ import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
2
+ import { sql } from 'drizzle-orm';
3
+ import { projects } from './projects.js';
4
+ export const topics = sqliteTable('topics', {
5
+ id: text('id').primaryKey(),
6
+ project_id: text('project_id').references(() => projects.project_id),
7
+ title: text('title').notNull(),
8
+ description: text('description'),
9
+ status: text('status').notNull().default('active'),
10
+ thread_id: text('thread_id'),
11
+ root_message_id: text('root_message_id'),
12
+ chat_id: text('chat_id'),
13
+ workspace_path: text('workspace_path'),
14
+ branch_name: text('branch_name'),
15
+ created_by: text('created_by'),
16
+ created_at: text('created_at').notNull().default(sql `(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))`),
17
+ updated_at: text('updated_at').notNull().default(sql `(strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))`),
18
+ closed_at: text('closed_at'),
19
+ });
20
+ //# sourceMappingURL=topics.js.map
@@ -0,0 +1,5 @@
1
+ -- 先删除重复的 cc_sessions 记录(保留每个 session_id 中 id 最小的那条)
2
+ DELETE FROM `cc_sessions` WHERE `id` NOT IN (
3
+ SELECT MIN(`id`) FROM `cc_sessions` GROUP BY `session_id`
4
+ );--> statement-breakpoint
5
+ CREATE UNIQUE INDEX `cc_sessions_session_id_unique` ON `cc_sessions` (`session_id`);
@@ -0,0 +1,21 @@
1
+ CREATE TABLE `topics` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `project_id` text REFERENCES `projects`(`project_id`),
4
+ `title` text NOT NULL,
5
+ `description` text,
6
+ `status` text NOT NULL DEFAULT 'active',
7
+ `thread_id` text,
8
+ `chat_id` text,
9
+ `workspace_path` text,
10
+ `branch_name` text,
11
+ `created_by` text,
12
+ `created_at` text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
13
+ `updated_at` text NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
14
+ `closed_at` text
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE INDEX `idx_topics_thread_id` ON `topics` (`thread_id`);
18
+ --> statement-breakpoint
19
+ CREATE INDEX `idx_topics_status` ON `topics` (`status`);
20
+ --> statement-breakpoint
21
+ ALTER TABLE `tasks` ADD COLUMN `topic_id` text REFERENCES `topics`(`id`);
@@ -0,0 +1 @@
1
+ ALTER TABLE `topics` ADD `root_message_id` text;