team-anya 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.

Potentially problematic release.


This version of team-anya might be problematic. Click here for more details.

Files changed (177) hide show
  1. package/README.md +38 -0
  2. package/anya/prompts/execution-guides/git-delivery.md +38 -0
  3. package/anya/prompts/execution-guides/testing-and-self-heal.md +28 -0
  4. package/anya/prompts/protocols/brief-assembly.md +55 -0
  5. package/anya/prompts/protocols/report.md +175 -0
  6. package/anya/prompts/protocols/review.md +90 -0
  7. package/anya/prompts/task-claude-md.template.md +32 -0
  8. package/apps/server/dist/broker/cc-broker.js +257 -0
  9. package/apps/server/dist/cli.js +296 -0
  10. package/apps/server/dist/config.js +76 -0
  11. package/apps/server/dist/daemon.js +51 -0
  12. package/apps/server/dist/gateway/chat-sync.js +135 -0
  13. package/apps/server/dist/gateway/command-router.js +114 -0
  14. package/apps/server/dist/gateway/commands/cancel.js +32 -0
  15. package/apps/server/dist/gateway/commands/help.js +16 -0
  16. package/apps/server/dist/gateway/commands/index.js +26 -0
  17. package/apps/server/dist/gateway/commands/restart.js +34 -0
  18. package/apps/server/dist/gateway/commands/status.js +34 -0
  19. package/apps/server/dist/gateway/commands/tasks.js +33 -0
  20. package/apps/server/dist/gateway/feishu-sender.js +346 -0
  21. package/apps/server/dist/gateway/feishu-ws.js +254 -0
  22. package/apps/server/dist/gateway/http.js +994 -0
  23. package/apps/server/dist/gateway/media-downloader.js +149 -0
  24. package/apps/server/dist/gateway/message-events.js +10 -0
  25. package/apps/server/dist/gateway/message-intake.js +50 -0
  26. package/apps/server/dist/gateway/message-queue.js +104 -0
  27. package/apps/server/dist/gateway/session-reader.js +142 -0
  28. package/apps/server/dist/gateway/ws-push.js +115 -0
  29. package/apps/server/dist/loid/brain.js +104 -0
  30. package/apps/server/dist/loid/brief-assembler.js +156 -0
  31. package/apps/server/dist/loid/clarifier.js +162 -0
  32. package/apps/server/dist/loid/commitment-tracker.js +236 -0
  33. package/apps/server/dist/loid/context-builder.js +413 -0
  34. package/apps/server/dist/loid/dispatcher.js +544 -0
  35. package/apps/server/dist/loid/intent-classifier.js +158 -0
  36. package/apps/server/dist/loid/mcp-server.js +104 -0
  37. package/apps/server/dist/loid/memory-settler.js +189 -0
  38. package/apps/server/dist/loid/opportunity-manager.js +148 -0
  39. package/apps/server/dist/loid/process-manager.js +186 -0
  40. package/apps/server/dist/loid/profile-updater.js +179 -0
  41. package/apps/server/dist/loid/reporter.js +148 -0
  42. package/apps/server/dist/loid/schemas.js +117 -0
  43. package/apps/server/dist/loid/self-calibrator.js +314 -0
  44. package/apps/server/dist/loid/session-manager.js +217 -0
  45. package/apps/server/dist/loid/session.js +271 -0
  46. package/apps/server/dist/loid/worktree-manager.js +191 -0
  47. package/apps/server/dist/main.js +337 -0
  48. package/apps/server/dist/tracing/index.js +2 -0
  49. package/apps/server/dist/tracing/trace-context.js +92 -0
  50. package/apps/server/dist/types/message.js +2 -0
  51. package/apps/server/dist/yor/yor-mcp-server.js +104 -0
  52. package/apps/server/dist/yor/yor-orchestrator.js +233 -0
  53. package/apps/web/dist/assets/index-CHIT0Dya.css +1 -0
  54. package/apps/web/dist/assets/index-CJzAjoVH.js +798 -0
  55. package/apps/web/dist/index.html +13 -0
  56. package/package.json +42 -0
  57. package/packages/cc-client/dist/claude-code-backend.js +664 -0
  58. package/packages/cc-client/dist/index.js +2 -0
  59. package/packages/cc-client/package.json +11 -0
  60. package/packages/core/dist/constants.js +59 -0
  61. package/packages/core/dist/errors.js +35 -0
  62. package/packages/core/dist/index.js +7 -0
  63. package/packages/core/dist/office-init.js +97 -0
  64. package/packages/core/dist/scope/checker.js +114 -0
  65. package/packages/core/dist/scope/defaults.js +40 -0
  66. package/packages/core/dist/scope/index.js +3 -0
  67. package/packages/core/dist/state-machine.js +85 -0
  68. package/packages/core/dist/types/audit.js +12 -0
  69. package/packages/core/dist/types/backend.js +2 -0
  70. package/packages/core/dist/types/commitment.js +17 -0
  71. package/packages/core/dist/types/communication.js +18 -0
  72. package/packages/core/dist/types/index.js +8 -0
  73. package/packages/core/dist/types/opportunity.js +27 -0
  74. package/packages/core/dist/types/org.js +26 -0
  75. package/packages/core/dist/types/task.js +46 -0
  76. package/packages/core/package.json +10 -0
  77. package/packages/db/dist/client.js +69 -0
  78. package/packages/db/dist/index.js +603 -0
  79. package/packages/db/dist/schema/audit-events.js +13 -0
  80. package/packages/db/dist/schema/cc-sessions.js +14 -0
  81. package/packages/db/dist/schema/chats.js +33 -0
  82. package/packages/db/dist/schema/commitments.js +18 -0
  83. package/packages/db/dist/schema/communication-events.js +14 -0
  84. package/packages/db/dist/schema/index.js +12 -0
  85. package/packages/db/dist/schema/message-log.js +20 -0
  86. package/packages/db/dist/schema/opportunities.js +23 -0
  87. package/packages/db/dist/schema/org.js +36 -0
  88. package/packages/db/dist/schema/projects.js +23 -0
  89. package/packages/db/dist/schema/tasks.js +46 -0
  90. package/packages/db/dist/schema/trace-spans.js +19 -0
  91. package/packages/db/package.json +12 -0
  92. package/packages/db/src/migrations/0000_simple_magneto.sql +148 -0
  93. package/packages/db/src/migrations/0001_nifty_morph.sql +42 -0
  94. package/packages/db/src/migrations/0002_common_joshua_kane.sql +20 -0
  95. package/packages/db/src/migrations/0003_add_cc_sessions.sql +13 -0
  96. package/packages/db/src/migrations/0004_jittery_triathlon.sql +1 -0
  97. package/packages/db/src/migrations/meta/0000_snapshot.json +987 -0
  98. package/packages/db/src/migrations/meta/0001_snapshot.json +1280 -0
  99. package/packages/db/src/migrations/meta/0002_snapshot.json +1417 -0
  100. package/packages/db/src/migrations/meta/0004_snapshot.json +1505 -0
  101. package/packages/db/src/migrations/meta/_journal.json +41 -0
  102. package/packages/mcp-tools/dist/index.js +41 -0
  103. package/packages/mcp-tools/dist/layer1/audit-append.js +38 -0
  104. package/packages/mcp-tools/dist/layer1/audit-query.js +51 -0
  105. package/packages/mcp-tools/dist/layer1/memory-brief.js +168 -0
  106. package/packages/mcp-tools/dist/layer1/memory-context.js +124 -0
  107. package/packages/mcp-tools/dist/layer1/memory-digest.js +126 -0
  108. package/packages/mcp-tools/dist/layer1/memory-forget.js +108 -0
  109. package/packages/mcp-tools/dist/layer1/memory-learn.js +63 -0
  110. package/packages/mcp-tools/dist/layer1/memory-recall.js +287 -0
  111. package/packages/mcp-tools/dist/layer1/memory-reflect.js +80 -0
  112. package/packages/mcp-tools/dist/layer1/memory-remember.js +119 -0
  113. package/packages/mcp-tools/dist/layer1/memory-search.js +263 -0
  114. package/packages/mcp-tools/dist/layer1/memory-write.js +21 -0
  115. package/packages/mcp-tools/dist/layer1/org-lookup.js +47 -0
  116. package/packages/mcp-tools/dist/layer1/project-get.js +28 -0
  117. package/packages/mcp-tools/dist/layer1/project-list.js +20 -0
  118. package/packages/mcp-tools/dist/layer1/report-daily.js +68 -0
  119. package/packages/mcp-tools/dist/layer1/task-get.js +29 -0
  120. package/packages/mcp-tools/dist/layer1/task-update.js +34 -0
  121. package/packages/mcp-tools/dist/layer2/loid/decision-log.js +15 -0
  122. package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +15 -0
  123. package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +30 -0
  124. package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +12 -0
  125. package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +77 -0
  126. package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +18 -0
  127. package/packages/mcp-tools/dist/layer2/loid/project-remove.js +16 -0
  128. package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +33 -0
  129. package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +177 -0
  130. package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +38 -0
  131. package/packages/mcp-tools/dist/layer2/loid/task-review.js +151 -0
  132. package/packages/mcp-tools/dist/layer2/loid/workspace-cleanup.js +7 -0
  133. package/packages/mcp-tools/dist/layer2/loid/workspace-info.js +31 -0
  134. package/packages/mcp-tools/dist/layer2/loid/workspace-prepare.js +12 -0
  135. package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +8 -0
  136. package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +7 -0
  137. package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +7 -0
  138. package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +15 -0
  139. package/packages/mcp-tools/dist/layer2/loid/yor-status.js +8 -0
  140. package/packages/mcp-tools/dist/layer2/yor/code-lint.js +47 -0
  141. package/packages/mcp-tools/dist/layer2/yor/code-test.js +52 -0
  142. package/packages/mcp-tools/dist/layer2/yor/git-add.js +24 -0
  143. package/packages/mcp-tools/dist/layer2/yor/git-commit.js +24 -0
  144. package/packages/mcp-tools/dist/layer2/yor/git-push.js +64 -0
  145. package/packages/mcp-tools/dist/layer2/yor/task-block.js +11 -0
  146. package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +35 -0
  147. package/packages/mcp-tools/dist/layer2/yor/task-progress.js +21 -0
  148. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +191 -0
  149. package/packages/mcp-tools/dist/layer3/adapters/types.js +28 -0
  150. package/packages/mcp-tools/dist/layer3/channel-receive.js +11 -0
  151. package/packages/mcp-tools/dist/layer3/channel-send.js +90 -0
  152. package/packages/mcp-tools/dist/layer3/file-upload.js +44 -0
  153. package/packages/mcp-tools/dist/registry.js +779 -0
  154. package/packages/mcp-tools/package.json +13 -0
  155. package/workspace/.claude/settings.local.json +9 -0
  156. package/workspace/.mcp.json +12 -0
  157. package/workspace/CHARTER.md +73 -0
  158. package/workspace/CLAUDE.md +49 -0
  159. package/workspace/PROTOCOL.md +126 -0
  160. package/workspace/TOOLS.md +464 -0
  161. package/workspace/audit/.gitkeep +0 -0
  162. package/workspace/loid/CLAUDE.md +12 -0
  163. package/workspace/loid/PLAYBOOK.md +198 -0
  164. package/workspace/loid/PROFILE.md +78 -0
  165. package/workspace/memory/commitments/.gitkeep +0 -0
  166. package/workspace/memory/execution/.gitkeep +0 -0
  167. package/workspace/memory/people/.gitkeep +0 -0
  168. package/workspace/memory/projects/.gitkeep +0 -0
  169. package/workspace/memory/self/.gitkeep +0 -0
  170. package/workspace/reference/identity/.gitkeep +0 -0
  171. package/workspace/reference/org/escalation.yaml +24 -0
  172. package/workspace/reference/org/ownership.yaml +28 -0
  173. package/workspace/reports/.gitkeep +0 -0
  174. package/workspace/yor/CLAUDE.md +22 -0
  175. package/workspace/yor/PLAYBOOK.md +73 -0
  176. package/workspace/yor/PROFILE.md +52 -0
  177. package/workspace/yor/SELF-HEAL.md +39 -0
@@ -0,0 +1,18 @@
1
+ import { fileUpload } from '../../layer3/file-upload.js';
2
+ /**
3
+ * delivery.upload - 上传文件产物
4
+ *
5
+ * 委托给 file.upload 实现。target 参数暂不使用(预留给后续上传到指定位置的场景)。
6
+ */
7
+ export async function deliveryUpload(deps, input) {
8
+ const result = await fileUpload(deps, {
9
+ file_path: input.file_path,
10
+ });
11
+ return {
12
+ status: 'uploaded',
13
+ url: result.url,
14
+ file_name: result.file_name,
15
+ file_size: result.file_size,
16
+ };
17
+ }
18
+ //# sourceMappingURL=delivery-upload.js.map
@@ -0,0 +1,16 @@
1
+ import { getProject, softDeleteProject, insertAuditEvent } from '@team-anya/db';
2
+ export async function projectRemove(db, _workspacePath, input) {
3
+ const existing = getProject(db, input.project_id);
4
+ if (!existing) {
5
+ return { removed: false, message: `Project ${input.project_id} not found` };
6
+ }
7
+ softDeleteProject(db, input.project_id);
8
+ insertAuditEvent(db, {
9
+ event_type: 'project_removed',
10
+ actor: 'loid',
11
+ summary: `Project ${input.project_id} soft-deleted: ${input.reason}`,
12
+ detail: JSON.stringify({ project_id: input.project_id, reason: input.reason }),
13
+ });
14
+ return { removed: true, message: `Project ${input.project_id} archived` };
15
+ }
16
+ //# sourceMappingURL=project-remove.js.map
@@ -0,0 +1,33 @@
1
+ import { getProject, upsertProject, syncProjectRepos, insertAuditEvent } from '@team-anya/db';
2
+ export async function projectUpsert(db, _workspacePath, input) {
3
+ const existing = getProject(db, input.project_id);
4
+ const action = existing ? 'updated' : 'created';
5
+ upsertProject(db, {
6
+ project_id: input.project_id,
7
+ name: input.name,
8
+ description: input.description ?? null,
9
+ platform: input.platform ?? 'github',
10
+ claude_md: input.claude_md ?? null,
11
+ });
12
+ const repos = input.repos ?? [];
13
+ if (repos.length > 0) {
14
+ syncProjectRepos(db, input.project_id, repos.map(r => ({
15
+ name: r.name,
16
+ git_url: r.git_url,
17
+ repo_path: r.repo_path,
18
+ default_branch: r.default_branch ?? 'main',
19
+ })));
20
+ }
21
+ insertAuditEvent(db, {
22
+ event_type: `project_${action}`,
23
+ actor: 'loid',
24
+ summary: `Project ${input.project_id} ${action}`,
25
+ detail: JSON.stringify({
26
+ project_id: input.project_id,
27
+ platform: input.platform ?? 'github',
28
+ repo_count: repos.length,
29
+ }),
30
+ });
31
+ return { project_id: input.project_id, action };
32
+ }
33
+ //# sourceMappingURL=project-upsert.js.map
@@ -0,0 +1,177 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { execFile as execFileCb } from 'node:child_process';
3
+ import { join } from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { createTask, updateTask, insertAuditEvent, getTodayMaxSequence, } from '@team-anya/db';
6
+ import { TaskStatus, generateTaskId, assertTransition } from '@team-anya/core';
7
+ const execFile = promisify(execFileCb);
8
+ /**
9
+ * 创建任务、写 brief、自动准备工作区
10
+ *
11
+ * 派工两步走:task.dispatch(MCP) → yor.spawn(MCP)
12
+ * dispatch 内部自动根据 project mode 准备隔离工作区(git worktree / mkdir)。
13
+ * 工作区准备失败时标记 BLOCKED 并返回 error,不抛异常。
14
+ */
15
+ export async function taskDispatch(deps, input) {
16
+ const { db, workspacePath, logger } = deps;
17
+ const seq = getTodayMaxSequence(db) + 1;
18
+ const taskId = generateTaskId(seq);
19
+ // 1. 创建任务
20
+ createTask(db, {
21
+ task_id: taskId,
22
+ title: input.title,
23
+ status: TaskStatus.NEW,
24
+ source_type: 'feishu',
25
+ source_ref: input.source_message_id ?? null,
26
+ objective: input.objective,
27
+ acceptance_criteria: input.acceptance_criteria ? JSON.stringify(input.acceptance_criteria) : undefined,
28
+ context: input.context,
29
+ project_id: input.project_id,
30
+ created_by: input.created_by ?? null,
31
+ source_chat_id: input.source_chat_id ?? null,
32
+ });
33
+ // 2. 写 brief 到 workspace
34
+ const taskDir = join(workspacePath, 'yor', 'tasks', taskId);
35
+ await mkdir(taskDir, { recursive: true });
36
+ const briefPath = join(taskDir, 'brief.md');
37
+ await writeFile(briefPath, input.brief, 'utf-8');
38
+ // 3. 审计日志
39
+ insertAuditEvent(db, {
40
+ event_type: 'task_dispatched',
41
+ actor: 'loid',
42
+ task_id: taskId,
43
+ summary: `Loid 派工: ${input.title}`,
44
+ detail: JSON.stringify({ brief_length: input.brief.length, project: input.project_id }),
45
+ });
46
+ // 4. 获取项目配置
47
+ const project = deps.getProjectConfig
48
+ ? await deps.getProjectConfig(input.project_id)
49
+ : undefined;
50
+ logger?.info(`[anya:pipeline] [Loid] 创建任务 ${taskId} "${input.title}"${input.project_id ? ` | project=${input.project_id} mode=${project?.mode}` : ''}`);
51
+ // 5. 自动准备工作区
52
+ const workspaceResult = await prepareWorkspace(taskId, taskDir, project, logger);
53
+ if (!workspaceResult.ok) {
54
+ // 工作区准备失败 → NEW → BLOCKED(经状态机校验)
55
+ assertTransition(TaskStatus.NEW, TaskStatus.BLOCKED);
56
+ updateTask(db, taskId, { status: TaskStatus.BLOCKED });
57
+ insertAuditEvent(db, {
58
+ event_type: 'task_status_changed',
59
+ actor: 'loid',
60
+ task_id: taskId,
61
+ summary: `NEW → BLOCKED: 工作区准备失败: ${workspaceResult.error}`,
62
+ detail: JSON.stringify({ from: 'NEW', to: 'BLOCKED', reason: workspaceResult.error }),
63
+ });
64
+ return { task_id: taskId, status: 'blocked', brief_path: briefPath, error: workspaceResult.error };
65
+ }
66
+ // 6. 工作区就绪 → NEW → READY,同时回写工作区信息
67
+ assertTransition(TaskStatus.NEW, TaskStatus.READY);
68
+ const branchName = project?.mode === 'project' ? `feat/anya-${taskId}` : undefined;
69
+ updateTask(db, taskId, {
70
+ status: TaskStatus.READY,
71
+ workspace_path: workspaceResult.workingDir,
72
+ assignee: 'yor',
73
+ ...(branchName ? { branch: branchName } : {}),
74
+ });
75
+ insertAuditEvent(db, {
76
+ event_type: 'task_status_changed',
77
+ actor: 'loid',
78
+ task_id: taskId,
79
+ summary: 'NEW → READY: 工作区准备完成',
80
+ detail: JSON.stringify({ from: 'NEW', to: 'READY', workspace_path: workspaceResult.workingDir, branch: branchName }),
81
+ });
82
+ return { task_id: taskId, status: 'dispatched', brief_path: briefPath, working_dir: workspaceResult.workingDir };
83
+ }
84
+ async function prepareWorkspace(taskId, taskDir, project, logger) {
85
+ const mode = project?.mode ?? 'adhoc';
86
+ try {
87
+ switch (mode) {
88
+ case 'project': {
89
+ const repos = project.repos;
90
+ const workingDir = taskDir;
91
+ const createdWorktrees = [];
92
+ try {
93
+ for (const repo of repos) {
94
+ const defaultBranch = repo.default_branch ?? 'main';
95
+ const wtPath = join(workingDir, repo.name);
96
+ await execFile('git', ['-C', repo.repo_path, 'fetch', 'origin']);
97
+ await execFile('git', ['-C', repo.repo_path, 'worktree', 'add', wtPath, '-b', `feat/anya-${taskId}`, `origin/${defaultBranch}`]);
98
+ createdWorktrees.push({ repoPath: repo.repo_path, wtPath });
99
+ }
100
+ }
101
+ catch (err) {
102
+ // 回滚已创建的 worktree
103
+ for (const wt of createdWorktrees) {
104
+ try {
105
+ await execFile('git', ['-C', wt.repoPath, 'worktree', 'remove', wt.wtPath, '--force']);
106
+ }
107
+ catch {
108
+ // 回滚失败忽略
109
+ }
110
+ }
111
+ const msg = err instanceof Error ? err.message : String(err);
112
+ return { ok: false, error: `worktree 准备失败: ${msg}` };
113
+ }
114
+ // git init 为 CC 提供项目边界(防止全局搜索)
115
+ await execFile('git', ['init', workingDir]);
116
+ // .gitignore 忽略所有 worktree 子目录(它们各自有独立的 .git)
117
+ const ignoreContent = repos.map(r => `/${r.name}/`).join('\n') + '\n';
118
+ await writeFile(join(workingDir, '.gitignore'), ignoreContent, 'utf-8');
119
+ // 写入 CLAUDE.md 帮助 CC 理解工作区结构
120
+ const claudeMd = buildProjectClaudeMd(taskId, repos, project.claudeMd);
121
+ await writeFile(join(workingDir, 'CLAUDE.md'), claudeMd, 'utf-8');
122
+ // 提交到 git,否则 CC 不会加载 untracked 的 CLAUDE.md
123
+ await execFile('git', ['-C', workingDir, 'add', 'CLAUDE.md', '.gitignore']);
124
+ await execFile('git', ['-C', workingDir, 'commit', '-m', 'init: workspace config']);
125
+ logger?.info(`[anya:pipeline] [Loid] worktrees 已创建 (${repos.length} repos): ${workingDir}`);
126
+ return { ok: true, workingDir };
127
+ }
128
+ case 'adhoc':
129
+ default: {
130
+ const workingDir = taskDir;
131
+ await mkdir(join(workingDir, 'adhoc'), { recursive: true });
132
+ // 在任务级目录 git init,版本管理整个任务目录
133
+ await execFile('git', ['init', workingDir]);
134
+ await writeFile(join(workingDir, '.gitignore'), '*.tmp\nnode_modules/\n.DS_Store\n', 'utf-8');
135
+ await execFile('git', ['-C', workingDir, 'add', '.']);
136
+ await execFile('git', ['-C', workingDir, 'commit', '-m', `chore: init task ${taskId}`]);
137
+ logger?.info(`[anya:pipeline] [Loid] adhoc 目录已创建并 git init: ${workingDir}`);
138
+ return { ok: true, workingDir };
139
+ }
140
+ }
141
+ }
142
+ catch (err) {
143
+ const msg = err instanceof Error ? err.message : String(err);
144
+ return { ok: false, error: `工作区准备失败 (${mode}): ${msg}` };
145
+ }
146
+ }
147
+ // ── 工作区 CLAUDE.md 生成 ──
148
+ function buildProjectClaudeMd(taskId, repos, projectClaudeMd) {
149
+ const lines = [
150
+ `# 任务工作区 ${taskId}`,
151
+ '',
152
+ '本目录是任务工作区根目录。每个子目录是一个独立的 git worktree。',
153
+ '',
154
+ '## 仓库列表',
155
+ '',
156
+ '| 目录 | 分支 |',
157
+ '|------|------|',
158
+ ];
159
+ for (const repo of repos) {
160
+ lines.push(`| \`${repo.name}/\` | \`feat/anya-${taskId}\` (基于 ${repo.default_branch}) |`);
161
+ }
162
+ lines.push('');
163
+ lines.push('## 重要');
164
+ lines.push('');
165
+ lines.push('- 每个子目录是独立的 git 仓库(worktree),搜索代码时请进入对应子目录');
166
+ lines.push('- git 操作(add/commit/push)必须在对应子目录内执行');
167
+ lines.push('- 不要在根目录执行 git commit');
168
+ lines.push('- 任务产物请读取 `brief.md` 了解详情');
169
+ if (projectClaudeMd) {
170
+ lines.push('');
171
+ lines.push('## 项目技术栈');
172
+ lines.push('');
173
+ lines.push(projectClaudeMd);
174
+ }
175
+ return lines.join('\n') + '\n';
176
+ }
177
+ //# sourceMappingURL=task-dispatch.js.map
@@ -0,0 +1,38 @@
1
+ import { getTask, getTasksByStatus } from '@team-anya/db';
2
+ import { TaskStatus } from '@team-anya/core';
3
+ /**
4
+ * 查询任务状态和历史
5
+ *
6
+ * 可按 task_id 或 status 过滤。不传参数时返回所有活跃任务。
7
+ */
8
+ export async function taskLookup(db, input) {
9
+ let tasks;
10
+ if (input.task_id) {
11
+ const task = getTask(db, input.task_id);
12
+ tasks = task ? [task] : [];
13
+ }
14
+ else if (input.status) {
15
+ tasks = getTasksByStatus(db, input.status);
16
+ }
17
+ else {
18
+ // 默认返回活跃任务
19
+ tasks = [
20
+ ...getTasksByStatus(db, TaskStatus.IN_PROGRESS),
21
+ ...getTasksByStatus(db, TaskStatus.READY),
22
+ ...getTasksByStatus(db, TaskStatus.NEED_CLARIFICATION),
23
+ ...getTasksByStatus(db, TaskStatus.DELIVERING),
24
+ ...getTasksByStatus(db, TaskStatus.BLOCKED),
25
+ ];
26
+ }
27
+ return {
28
+ tasks: tasks.map(t => ({
29
+ task_id: t.task_id,
30
+ title: t.title,
31
+ status: t.status,
32
+ assignee: t.assignee,
33
+ created_at: t.created_at,
34
+ pr_url: t.pr_url ?? null,
35
+ })),
36
+ };
37
+ }
38
+ //# sourceMappingURL=task-lookup.js.map
@@ -0,0 +1,151 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getTask, updateTask, insertAuditEvent, getMessageLogBySourceRef, } from '@team-anya/db';
4
+ import { TaskStatus } from '@team-anya/core';
5
+ /**
6
+ * 验收审核(合并 approve_delivery + reject_delivery)
7
+ *
8
+ * 支持三级判断:
9
+ * - PASS: 验收通过 → DONE + 通知人类
10
+ * - REVISE: 打回返工 → retry_count++ + feedback + 重派
11
+ * - ESCALATE: 升级 → BLOCKED + 通知人类
12
+ */
13
+ export async function taskReview(deps, input) {
14
+ const { db, workspacePath, dispatcher, logger } = deps;
15
+ const task = getTask(db, input.task_id);
16
+ if (!task) {
17
+ return {
18
+ task_id: input.task_id,
19
+ judgment: input.judgment,
20
+ success: false,
21
+ message: '任务不存在',
22
+ };
23
+ }
24
+ switch (input.judgment) {
25
+ case 'PASS': {
26
+ updateTask(db, input.task_id, { status: TaskStatus.DONE });
27
+ insertAuditEvent(db, {
28
+ event_type: 'delivery_approved',
29
+ actor: 'loid',
30
+ task_id: input.task_id,
31
+ summary: `Loid 验收通过: ${input.summary ?? '无评语'}`,
32
+ });
33
+ // 通知人类
34
+ let notified = false;
35
+ if (input.notify_human !== false && deps.channelSend) {
36
+ try {
37
+ let chatId = null;
38
+ if (task.source_ref) {
39
+ const sourceMsg = getMessageLogBySourceRef(db, task.source_ref);
40
+ chatId = sourceMsg?.chat_id ?? null;
41
+ }
42
+ if (chatId) {
43
+ const prPart = task.pr_url ? `,PR: ${task.pr_url}` : '';
44
+ await deps.channelSend(`feishu://${chatId}`, `${input.task_id} 搞定了——${input.summary ?? task.title}${prPart}`, { task_id: input.task_id });
45
+ notified = true;
46
+ }
47
+ }
48
+ catch (err) {
49
+ logger?.error(`[anya:pipeline] [Loid] 任务完成通知发送失败 (${input.task_id}):`, err);
50
+ }
51
+ }
52
+ logger?.info(`[anya:pipeline] [Loid] 验收通过 ${input.task_id}${notified ? ' (已通知)' : ''}`);
53
+ return { task_id: input.task_id, judgment: 'PASS', success: true, notified };
54
+ }
55
+ case 'REVISE': {
56
+ const retryCount = (task.retry_count ?? 0) + 1;
57
+ const maxRetries = task.max_retries ?? 3;
58
+ if (retryCount > maxRetries) {
59
+ // 超过最大重试次数,标记为 BLOCKED
60
+ updateTask(db, input.task_id, {
61
+ status: TaskStatus.BLOCKED,
62
+ blocked_reason: `超过最大返工次数 (${maxRetries}): ${input.feedback ?? '未说明'}`,
63
+ blocked_since: new Date().toISOString(),
64
+ retry_count: retryCount,
65
+ });
66
+ insertAuditEvent(db, {
67
+ event_type: 'delivery_rejected_max_retries',
68
+ actor: 'loid',
69
+ task_id: input.task_id,
70
+ summary: `Loid 打回但已达上限 (${retryCount}/${maxRetries}): ${input.feedback ?? ''}`,
71
+ });
72
+ logger?.info(`[anya:pipeline] [Loid] 打回 ${input.task_id} (已达上限 ${retryCount}/${maxRetries}, BLOCKED)`);
73
+ return {
74
+ task_id: input.task_id,
75
+ judgment: 'REVISE',
76
+ success: true,
77
+ retry_count: retryCount,
78
+ max_retries: maxRetries,
79
+ blocked: true,
80
+ message: `已达最大返工次数 ${maxRetries},任务已阻塞`,
81
+ };
82
+ }
83
+ // 更新任务状态和反馈
84
+ updateTask(db, input.task_id, {
85
+ status: TaskStatus.READY,
86
+ retry_count: retryCount,
87
+ blocked_reason: null,
88
+ blocked_since: null,
89
+ });
90
+ // 写返工反馈
91
+ const taskDir = join(workspacePath, 'yor', input.task_id);
92
+ await mkdir(taskDir, { recursive: true });
93
+ const feedbackContent = `# 返工反馈 (第 ${retryCount} 次)\n\n## 打回原因\n${input.feedback ?? '未说明'}\n\n## 修改意见\n${input.feedback ?? '无'}\n`;
94
+ await writeFile(join(taskDir, `feedback-${retryCount}.md`), feedbackContent, 'utf-8');
95
+ insertAuditEvent(db, {
96
+ event_type: 'delivery_rejected',
97
+ actor: 'loid',
98
+ task_id: input.task_id,
99
+ summary: `Loid 打回 (${retryCount}/${maxRetries}): ${input.feedback ?? ''}`,
100
+ detail: JSON.stringify({ feedback: input.feedback }),
101
+ });
102
+ // 重新派工
103
+ if (dispatcher) {
104
+ await dispatcher.dispatch(input.task_id);
105
+ }
106
+ logger?.info(`[anya:pipeline] [Loid] 打回 ${input.task_id} → 返工 (${retryCount}/${maxRetries})`);
107
+ return {
108
+ task_id: input.task_id,
109
+ judgment: 'REVISE',
110
+ success: true,
111
+ retry_count: retryCount,
112
+ max_retries: maxRetries,
113
+ };
114
+ }
115
+ case 'ESCALATE': {
116
+ updateTask(db, input.task_id, {
117
+ status: TaskStatus.BLOCKED,
118
+ blocked_reason: `需人类决策: ${input.escalation_note ?? '未说明'}`,
119
+ blocked_since: new Date().toISOString(),
120
+ });
121
+ insertAuditEvent(db, {
122
+ event_type: 'delivery_escalated',
123
+ actor: 'loid',
124
+ task_id: input.task_id,
125
+ summary: `Loid 升级: ${input.escalation_note ?? '需人类介入'}`,
126
+ detail: JSON.stringify({ escalation_note: input.escalation_note }),
127
+ });
128
+ // 通过通道向人类发升级通知
129
+ let notified = false;
130
+ if (deps.channelSend) {
131
+ try {
132
+ let chatId = null;
133
+ if (task.source_ref) {
134
+ const sourceMsg = getMessageLogBySourceRef(db, task.source_ref);
135
+ chatId = sourceMsg?.chat_id ?? null;
136
+ }
137
+ if (chatId) {
138
+ await deps.channelSend(`feishu://${chatId}`, `${input.task_id} 需要你来决定——${input.escalation_note ?? '详见任务'}`, { task_id: input.task_id, intent: 'escalation' });
139
+ notified = true;
140
+ }
141
+ }
142
+ catch (err) {
143
+ logger?.error(`[anya:pipeline] [Loid] 升级通知发送失败 (${input.task_id}):`, err);
144
+ }
145
+ }
146
+ logger?.info(`[anya:pipeline] [Loid] 升级 ${input.task_id}: ${input.escalation_note ?? '需人类决策'}${notified ? ' (已通知)' : ''}`);
147
+ return { task_id: input.task_id, judgment: 'ESCALATE', success: true, notified };
148
+ }
149
+ }
150
+ }
151
+ //# sourceMappingURL=task-review.js.map
@@ -0,0 +1,7 @@
1
+ export async function workspaceCleanup(deps, input) {
2
+ const { worktreeManager, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] workspace.cleanup ${input.task_id}`);
4
+ await worktreeManager.cleanup(input.task_id, input.project_id);
5
+ return { task_id: input.task_id, cleaned: true };
6
+ }
7
+ //# sourceMappingURL=workspace-cleanup.js.map
@@ -0,0 +1,31 @@
1
+ export async function workspaceInfo(deps, input) {
2
+ const { worktreeManager } = deps;
3
+ await worktreeManager.loadConfig();
4
+ const product = worktreeManager.findProduct(input.project_id);
5
+ if (product) {
6
+ return {
7
+ type: 'product',
8
+ project_id: product.productId,
9
+ name: product.name,
10
+ description: product.description,
11
+ repos: product.repos.map(r => ({
12
+ name: r.name,
13
+ gitUrl: r.gitUrl,
14
+ repoPath: worktreeManager.resolveRepoPath(r.repoPath, r.name),
15
+ defaultBranch: r.defaultBranch,
16
+ })),
17
+ };
18
+ }
19
+ const project = worktreeManager.findProject(input.project_id);
20
+ if (project) {
21
+ return {
22
+ type: 'project',
23
+ project_id: project.projectId,
24
+ gitUrl: project.gitUrl,
25
+ repoPath: worktreeManager.resolveRepoPath(project.repoPath, project.projectId),
26
+ defaultBranch: project.defaultBranch,
27
+ };
28
+ }
29
+ return { type: 'not_found', project_id: input.project_id };
30
+ }
31
+ //# sourceMappingURL=workspace-info.js.map
@@ -0,0 +1,12 @@
1
+ export async function workspacePrepare(deps, input) {
2
+ const { worktreeManager, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] workspace.prepare ${input.task_id} | project=${input.project_id ?? 'none'}`);
4
+ const result = await worktreeManager.prepare(input.task_id, input.project_id);
5
+ return {
6
+ path: result.workingDir,
7
+ branch: result.branch,
8
+ type: result.mode,
9
+ repos: result.repos,
10
+ };
11
+ }
12
+ //# sourceMappingURL=workspace-prepare.js.map
@@ -0,0 +1,8 @@
1
+ export function yorApprove(deps, input) {
2
+ const { yorOrchestrator, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] yor.approve task=${input.task_id}`);
4
+ const message = input.message ?? '审核通过,任务完成。你可以做最后的清理工作。';
5
+ yorOrchestrator.approveAndShutdown(input.task_id, message);
6
+ return { task_id: input.task_id, approved: true };
7
+ }
8
+ //# sourceMappingURL=yor-approve.js.map
@@ -0,0 +1,7 @@
1
+ export async function yorKill(deps, input) {
2
+ const { yorOrchestrator, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] yor.kill task=${input.task_id}`);
4
+ const result = await yorOrchestrator.kill(input.task_id);
5
+ return { task_id: input.task_id, killed: result.success };
6
+ }
7
+ //# sourceMappingURL=yor-kill.js.map
@@ -0,0 +1,7 @@
1
+ export function yorRework(deps, input) {
2
+ const { yorOrchestrator, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] yor.rework task=${input.task_id}`);
4
+ yorOrchestrator.sendRework(input.task_id, input.feedback);
5
+ return { task_id: input.task_id, sent: true };
6
+ }
7
+ //# sourceMappingURL=yor-rework.js.map
@@ -0,0 +1,15 @@
1
+ export async function yorSpawn(deps, input) {
2
+ const { yorOrchestrator, logger } = deps;
3
+ logger?.info(`[anya:pipeline] [Loid] yor.spawn ${input.task_id} | dir=${input.working_dir}`);
4
+ const result = await yorOrchestrator.spawnAndExecute({
5
+ taskId: input.task_id,
6
+ workingDir: input.working_dir,
7
+ briefPath: input.brief_path,
8
+ env: input.env,
9
+ });
10
+ return {
11
+ instance_id: result.instanceId,
12
+ task_id: input.task_id,
13
+ };
14
+ }
15
+ //# sourceMappingURL=yor-spawn.js.map
@@ -0,0 +1,8 @@
1
+ export function yorStatus(deps) {
2
+ const instances = deps.yorOrchestrator.status();
3
+ return {
4
+ total: instances.length,
5
+ instances,
6
+ };
7
+ }
8
+ //# sourceMappingURL=yor-status.js.map
@@ -0,0 +1,47 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ const execAsync = promisify(exec);
6
+ /**
7
+ * code.lint - 运行 lint 检查
8
+ *
9
+ * 自动检测包管理器,运行 lint 脚本。
10
+ * 返回 lint 结果。
11
+ */
12
+ export async function codeLint(input) {
13
+ const { working_dir } = input;
14
+ let command;
15
+ if (existsSync(join(working_dir, 'pnpm-lock.yaml')) || existsSync(join(working_dir, 'pnpm-workspace.yaml'))) {
16
+ command = 'pnpm lint';
17
+ }
18
+ else if (existsSync(join(working_dir, 'yarn.lock'))) {
19
+ command = 'yarn lint';
20
+ }
21
+ else {
22
+ command = 'npm run lint';
23
+ }
24
+ try {
25
+ const { stdout, stderr } = await execAsync(command, {
26
+ cwd: working_dir,
27
+ timeout: 3 * 60 * 1000, // 3 分钟超时
28
+ maxBuffer: 10 * 1024 * 1024, // 10MB
29
+ });
30
+ const output = [stdout, stderr].filter(Boolean).join('\n');
31
+ return { success: true, passed: true, output };
32
+ }
33
+ catch (err) {
34
+ const output = [err.stdout, err.stderr].filter(Boolean).join('\n');
35
+ const isLintFailure = err.code !== undefined && err.code !== null;
36
+ if (isLintFailure) {
37
+ return { success: true, passed: false, output };
38
+ }
39
+ return {
40
+ success: false,
41
+ passed: false,
42
+ output: output || '',
43
+ error: err.message,
44
+ };
45
+ }
46
+ }
47
+ //# sourceMappingURL=code-lint.js.map
@@ -0,0 +1,52 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { existsSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ const execAsync = promisify(exec);
6
+ /**
7
+ * code.test - 运行测试
8
+ *
9
+ * 默认运行 pnpm test 或 npm test,可指定自定义测试命令。
10
+ * 返回测试输出和通过/失败状态。
11
+ */
12
+ export async function codeTest(input) {
13
+ const { working_dir } = input;
14
+ let command = input.command;
15
+ if (!command) {
16
+ // 自动检测包管理器
17
+ if (existsSync(join(working_dir, 'pnpm-lock.yaml')) || existsSync(join(working_dir, 'pnpm-workspace.yaml'))) {
18
+ command = 'pnpm test';
19
+ }
20
+ else if (existsSync(join(working_dir, 'yarn.lock'))) {
21
+ command = 'yarn test';
22
+ }
23
+ else {
24
+ command = 'npm test';
25
+ }
26
+ }
27
+ try {
28
+ const { stdout, stderr } = await execAsync(command, {
29
+ cwd: working_dir,
30
+ timeout: 5 * 60 * 1000, // 5 分钟超时
31
+ maxBuffer: 10 * 1024 * 1024, // 10MB
32
+ });
33
+ const output = [stdout, stderr].filter(Boolean).join('\n');
34
+ return { success: true, passed: true, output };
35
+ }
36
+ catch (err) {
37
+ // 测试失败但命令执行了(exit code !== 0)
38
+ const output = [err.stdout, err.stderr].filter(Boolean).join('\n');
39
+ const isTestFailure = err.code !== undefined && err.code !== null;
40
+ if (isTestFailure) {
41
+ return { success: true, passed: false, output };
42
+ }
43
+ // 命令本身执行失败
44
+ return {
45
+ success: false,
46
+ passed: false,
47
+ output: output || '',
48
+ error: err.message,
49
+ };
50
+ }
51
+ }
52
+ //# sourceMappingURL=code-test.js.map