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.
Files changed (163) 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/clarifier.js +162 -0
  31. package/apps/server/dist/loid/context-builder.js +413 -0
  32. package/apps/server/dist/loid/mcp-server.js +104 -0
  33. package/apps/server/dist/loid/memory-settler.js +189 -0
  34. package/apps/server/dist/loid/opportunity-manager.js +148 -0
  35. package/apps/server/dist/loid/profile-updater.js +179 -0
  36. package/apps/server/dist/loid/reporter.js +148 -0
  37. package/apps/server/dist/loid/schemas.js +117 -0
  38. package/apps/server/dist/loid/self-calibrator.js +314 -0
  39. package/apps/server/dist/loid/session-manager.js +217 -0
  40. package/apps/server/dist/loid/session.js +271 -0
  41. package/apps/server/dist/loid/worktree-manager.js +191 -0
  42. package/apps/server/dist/main.js +337 -0
  43. package/apps/server/dist/tracing/index.js +2 -0
  44. package/apps/server/dist/tracing/trace-context.js +92 -0
  45. package/apps/server/dist/types/message.js +2 -0
  46. package/apps/server/dist/yor/yor-mcp-server.js +104 -0
  47. package/apps/server/dist/yor/yor-orchestrator.js +233 -0
  48. package/apps/web/dist/assets/index-CHIT0Dya.css +1 -0
  49. package/apps/web/dist/assets/index-CJzAjoVH.js +798 -0
  50. package/apps/web/dist/index.html +13 -0
  51. package/package.json +42 -0
  52. package/packages/cc-client/dist/claude-code-backend.js +664 -0
  53. package/packages/cc-client/dist/index.js +2 -0
  54. package/packages/cc-client/package.json +11 -0
  55. package/packages/core/dist/constants.js +59 -0
  56. package/packages/core/dist/errors.js +35 -0
  57. package/packages/core/dist/index.js +7 -0
  58. package/packages/core/dist/office-init.js +97 -0
  59. package/packages/core/dist/scope/checker.js +114 -0
  60. package/packages/core/dist/scope/defaults.js +40 -0
  61. package/packages/core/dist/scope/index.js +3 -0
  62. package/packages/core/dist/state-machine.js +85 -0
  63. package/packages/core/dist/types/audit.js +12 -0
  64. package/packages/core/dist/types/backend.js +2 -0
  65. package/packages/core/dist/types/commitment.js +17 -0
  66. package/packages/core/dist/types/communication.js +18 -0
  67. package/packages/core/dist/types/index.js +8 -0
  68. package/packages/core/dist/types/opportunity.js +27 -0
  69. package/packages/core/dist/types/org.js +26 -0
  70. package/packages/core/dist/types/task.js +46 -0
  71. package/packages/core/package.json +10 -0
  72. package/packages/db/dist/client.js +69 -0
  73. package/packages/db/dist/index.js +603 -0
  74. package/packages/db/dist/schema/audit-events.js +13 -0
  75. package/packages/db/dist/schema/cc-sessions.js +14 -0
  76. package/packages/db/dist/schema/chats.js +33 -0
  77. package/packages/db/dist/schema/commitments.js +18 -0
  78. package/packages/db/dist/schema/communication-events.js +14 -0
  79. package/packages/db/dist/schema/index.js +12 -0
  80. package/packages/db/dist/schema/message-log.js +20 -0
  81. package/packages/db/dist/schema/opportunities.js +23 -0
  82. package/packages/db/dist/schema/org.js +36 -0
  83. package/packages/db/dist/schema/projects.js +23 -0
  84. package/packages/db/dist/schema/tasks.js +46 -0
  85. package/packages/db/dist/schema/trace-spans.js +19 -0
  86. package/packages/db/package.json +12 -0
  87. package/packages/db/src/migrations/0000_simple_magneto.sql +148 -0
  88. package/packages/db/src/migrations/0001_nifty_morph.sql +42 -0
  89. package/packages/db/src/migrations/0002_common_joshua_kane.sql +20 -0
  90. package/packages/db/src/migrations/0003_add_cc_sessions.sql +13 -0
  91. package/packages/db/src/migrations/0004_jittery_triathlon.sql +1 -0
  92. package/packages/db/src/migrations/meta/0000_snapshot.json +987 -0
  93. package/packages/db/src/migrations/meta/0001_snapshot.json +1280 -0
  94. package/packages/db/src/migrations/meta/0002_snapshot.json +1417 -0
  95. package/packages/db/src/migrations/meta/0004_snapshot.json +1505 -0
  96. package/packages/db/src/migrations/meta/_journal.json +41 -0
  97. package/packages/mcp-tools/dist/index.js +41 -0
  98. package/packages/mcp-tools/dist/layer1/audit-append.js +38 -0
  99. package/packages/mcp-tools/dist/layer1/audit-query.js +51 -0
  100. package/packages/mcp-tools/dist/layer1/memory-brief.js +168 -0
  101. package/packages/mcp-tools/dist/layer1/memory-context.js +124 -0
  102. package/packages/mcp-tools/dist/layer1/memory-digest.js +126 -0
  103. package/packages/mcp-tools/dist/layer1/memory-forget.js +108 -0
  104. package/packages/mcp-tools/dist/layer1/memory-learn.js +63 -0
  105. package/packages/mcp-tools/dist/layer1/memory-recall.js +287 -0
  106. package/packages/mcp-tools/dist/layer1/memory-reflect.js +80 -0
  107. package/packages/mcp-tools/dist/layer1/memory-remember.js +119 -0
  108. package/packages/mcp-tools/dist/layer1/memory-search.js +263 -0
  109. package/packages/mcp-tools/dist/layer1/memory-write.js +21 -0
  110. package/packages/mcp-tools/dist/layer1/org-lookup.js +47 -0
  111. package/packages/mcp-tools/dist/layer1/project-get.js +28 -0
  112. package/packages/mcp-tools/dist/layer1/project-list.js +20 -0
  113. package/packages/mcp-tools/dist/layer1/report-daily.js +68 -0
  114. package/packages/mcp-tools/dist/layer1/task-get.js +29 -0
  115. package/packages/mcp-tools/dist/layer1/task-update.js +34 -0
  116. package/packages/mcp-tools/dist/layer2/loid/decision-log.js +15 -0
  117. package/packages/mcp-tools/dist/layer2/loid/decision-no-action.js +15 -0
  118. package/packages/mcp-tools/dist/layer2/loid/delivery-create-pr.js +30 -0
  119. package/packages/mcp-tools/dist/layer2/loid/delivery-share.js +12 -0
  120. package/packages/mcp-tools/dist/layer2/loid/delivery-submit.js +77 -0
  121. package/packages/mcp-tools/dist/layer2/loid/delivery-upload.js +18 -0
  122. package/packages/mcp-tools/dist/layer2/loid/project-remove.js +16 -0
  123. package/packages/mcp-tools/dist/layer2/loid/project-upsert.js +33 -0
  124. package/packages/mcp-tools/dist/layer2/loid/task-dispatch.js +177 -0
  125. package/packages/mcp-tools/dist/layer2/loid/task-lookup.js +38 -0
  126. package/packages/mcp-tools/dist/layer2/loid/yor-approve.js +8 -0
  127. package/packages/mcp-tools/dist/layer2/loid/yor-kill.js +7 -0
  128. package/packages/mcp-tools/dist/layer2/loid/yor-rework.js +7 -0
  129. package/packages/mcp-tools/dist/layer2/loid/yor-spawn.js +15 -0
  130. package/packages/mcp-tools/dist/layer2/loid/yor-status.js +8 -0
  131. package/packages/mcp-tools/dist/layer2/yor/task-block.js +11 -0
  132. package/packages/mcp-tools/dist/layer2/yor/task-deliver.js +35 -0
  133. package/packages/mcp-tools/dist/layer2/yor/task-progress.js +21 -0
  134. package/packages/mcp-tools/dist/layer3/adapters/feishu-adapter.js +191 -0
  135. package/packages/mcp-tools/dist/layer3/adapters/types.js +28 -0
  136. package/packages/mcp-tools/dist/layer3/channel-receive.js +11 -0
  137. package/packages/mcp-tools/dist/layer3/channel-send.js +90 -0
  138. package/packages/mcp-tools/dist/layer3/file-upload.js +44 -0
  139. package/packages/mcp-tools/dist/registry.js +779 -0
  140. package/packages/mcp-tools/package.json +13 -0
  141. package/workspace/.claude/settings.local.json +9 -0
  142. package/workspace/.mcp.json +12 -0
  143. package/workspace/CHARTER.md +73 -0
  144. package/workspace/CLAUDE.md +49 -0
  145. package/workspace/PROTOCOL.md +126 -0
  146. package/workspace/TOOLS.md +464 -0
  147. package/workspace/audit/.gitkeep +0 -0
  148. package/workspace/loid/CLAUDE.md +12 -0
  149. package/workspace/loid/PLAYBOOK.md +198 -0
  150. package/workspace/loid/PROFILE.md +78 -0
  151. package/workspace/memory/commitments/.gitkeep +0 -0
  152. package/workspace/memory/execution/.gitkeep +0 -0
  153. package/workspace/memory/people/.gitkeep +0 -0
  154. package/workspace/memory/projects/.gitkeep +0 -0
  155. package/workspace/memory/self/.gitkeep +0 -0
  156. package/workspace/reference/identity/.gitkeep +0 -0
  157. package/workspace/reference/org/escalation.yaml +24 -0
  158. package/workspace/reference/org/ownership.yaml +28 -0
  159. package/workspace/reports/.gitkeep +0 -0
  160. package/workspace/yor/CLAUDE.md +22 -0
  161. package/workspace/yor/PLAYBOOK.md +73 -0
  162. package/workspace/yor/PROFILE.md +52 -0
  163. package/workspace/yor/SELF-HEAL.md +39 -0
@@ -0,0 +1,263 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join, relative } from 'node:path';
3
+ // BM25 参数
4
+ const BM25_K1 = 1.5;
5
+ const BM25_B = 0.75;
6
+ /**
7
+ * 搜索长期记忆
8
+ *
9
+ * 支持两种模式:
10
+ * - keyword: 简单关键词匹配(默认)
11
+ * - vector: BM25 评分(更精确的相关度排序,预留 embedding API 接口)
12
+ *
13
+ * 通过 options.mode 或环境变量 MEMORY_SEARCH_MODE 切换
14
+ */
15
+ export async function memorySearch(workspacePath, input, options) {
16
+ const mode = options?.mode ?? process.env.MEMORY_SEARCH_MODE ?? 'keyword';
17
+ if (mode === 'vector') {
18
+ return bm25Search(workspacePath, input);
19
+ }
20
+ return keywordSearch(workspacePath, input);
21
+ }
22
+ // ── 关键词搜索(原有实现) ──
23
+ async function keywordSearch(workspacePath, input) {
24
+ const results = [];
25
+ const query = input.query.toLowerCase();
26
+ const keywords = query.split(/\s+/).filter(k => k.length > 1);
27
+ const limit = input.limit ?? 10;
28
+ const searchDirs = [];
29
+ if (input.project_id) {
30
+ searchDirs.push(join(workspacePath, 'memory', 'projects', input.project_id));
31
+ }
32
+ else {
33
+ searchDirs.push(join(workspacePath, 'memory', 'projects'));
34
+ }
35
+ for (const dir of searchDirs) {
36
+ const files = await findMdFiles(dir);
37
+ for (const file of files) {
38
+ if (input.types && input.types.length > 0) {
39
+ const fileName = file.split('/').pop() ?? '';
40
+ const matchesType = input.types.some(t => fileName.includes(t));
41
+ if (!matchesType)
42
+ continue;
43
+ }
44
+ const content = await readFile(file, 'utf-8');
45
+ const paragraphs = splitIntoParagraphs(content);
46
+ for (const paragraph of paragraphs) {
47
+ const relevance = calculateKeywordRelevance(paragraph, keywords);
48
+ if (relevance > 0) {
49
+ const type = inferType(file);
50
+ // 按 type 过滤(新增)
51
+ if (input.type && type !== input.type)
52
+ continue;
53
+ // 按 tags 过滤(新增):段落中必须包含所有指定的 tag
54
+ if (input.tags && input.tags.length > 0) {
55
+ const lower = paragraph.toLowerCase();
56
+ const allTagsMatch = input.tags.every(tag => lower.includes(tag.toLowerCase()));
57
+ if (!allTagsMatch)
58
+ continue;
59
+ }
60
+ results.push({
61
+ type,
62
+ content: paragraph.trim(),
63
+ source: relative(workspacePath, file),
64
+ relevance,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ }
70
+ results.sort((a, b) => b.relevance - a.relevance);
71
+ return { results: results.slice(0, limit) };
72
+ }
73
+ // ── BM25 搜索 ──
74
+ async function bm25Search(workspacePath, input) {
75
+ const limit = input.limit ?? 10;
76
+ // 构建索引
77
+ const index = await buildSearchIndex(workspacePath, input.project_id);
78
+ if (index.totalDocCount === 0) {
79
+ return { results: [] };
80
+ }
81
+ // 分词查询
82
+ const queryTerms = tokenize(input.query);
83
+ if (queryTerms.length === 0) {
84
+ return { results: [] };
85
+ }
86
+ // 计算 BM25 得分
87
+ const scored = [];
88
+ for (const doc of index.documents) {
89
+ // 按 type 过滤(新增单类型过滤)
90
+ if (input.type && doc.type !== input.type)
91
+ continue;
92
+ // 按 types 过滤(保留向后兼容)
93
+ if (input.types && input.types.length > 0) {
94
+ if (!input.types.includes(doc.type))
95
+ continue;
96
+ }
97
+ const score = computeBM25Score(queryTerms, doc, index);
98
+ if (score > 0) {
99
+ scored.push({ doc, score });
100
+ }
101
+ }
102
+ // 排序
103
+ scored.sort((a, b) => b.score - a.score);
104
+ // 转换结果
105
+ const results = scored.slice(0, limit).map(({ doc, score }) => ({
106
+ type: doc.type,
107
+ content: doc.content,
108
+ source: doc.source,
109
+ relevance: score,
110
+ }));
111
+ return { results };
112
+ }
113
+ /**
114
+ * 构建 BM25 搜索索引
115
+ */
116
+ export async function buildSearchIndex(workspacePath, projectId) {
117
+ const documents = [];
118
+ const searchDirs = [];
119
+ if (projectId) {
120
+ searchDirs.push(join(workspacePath, 'memory', 'projects', projectId));
121
+ }
122
+ else {
123
+ searchDirs.push(join(workspacePath, 'memory', 'projects'));
124
+ }
125
+ // 也搜索 identity 和 self 目录
126
+ searchDirs.push(join(workspacePath, 'memory', 'identity'));
127
+ searchDirs.push(join(workspacePath, 'memory', 'self'));
128
+ for (const dir of searchDirs) {
129
+ const files = await findMdFiles(dir);
130
+ for (const file of files) {
131
+ const content = await readFile(file, 'utf-8');
132
+ const paragraphs = splitIntoParagraphs(content);
133
+ for (const paragraph of paragraphs) {
134
+ const trimmed = paragraph.trim();
135
+ const terms = tokenize(trimmed);
136
+ if (terms.length === 0)
137
+ continue;
138
+ const termFreqs = {};
139
+ for (const term of terms) {
140
+ termFreqs[term] = (termFreqs[term] || 0) + 1;
141
+ }
142
+ documents.push({
143
+ content: trimmed,
144
+ source: relative(workspacePath, file),
145
+ type: inferType(file),
146
+ termFreqs,
147
+ docLength: terms.length,
148
+ });
149
+ }
150
+ }
151
+ }
152
+ // 计算 IDF
153
+ const idf = {};
154
+ const totalDocCount = documents.length;
155
+ if (totalDocCount > 0) {
156
+ // 统计每个词出现在多少个文档中
157
+ const docFreq = {};
158
+ for (const doc of documents) {
159
+ for (const term of Object.keys(doc.termFreqs)) {
160
+ docFreq[term] = (docFreq[term] || 0) + 1;
161
+ }
162
+ }
163
+ // IDF = ln((N - n + 0.5) / (n + 0.5) + 1)
164
+ for (const [term, df] of Object.entries(docFreq)) {
165
+ idf[term] = Math.log((totalDocCount - df + 0.5) / (df + 0.5) + 1);
166
+ }
167
+ }
168
+ // 平均文档长度
169
+ const avgDocLength = totalDocCount > 0
170
+ ? documents.reduce((sum, d) => sum + d.docLength, 0) / totalDocCount
171
+ : 0;
172
+ return {
173
+ documents,
174
+ idf,
175
+ avgDocLength,
176
+ totalDocCount,
177
+ };
178
+ }
179
+ /**
180
+ * 计算单个文档的 BM25 得分
181
+ */
182
+ function computeBM25Score(queryTerms, doc, index) {
183
+ let score = 0;
184
+ for (const term of queryTerms) {
185
+ const tf = doc.termFreqs[term] || 0;
186
+ if (tf === 0)
187
+ continue;
188
+ const idf = index.idf[term] || 0;
189
+ const numerator = tf * (BM25_K1 + 1);
190
+ const denominator = tf + BM25_K1 * (1 - BM25_B + BM25_B * doc.docLength / index.avgDocLength);
191
+ score += idf * (numerator / denominator);
192
+ }
193
+ return score;
194
+ }
195
+ // ── 工具函数 ──
196
+ /**
197
+ * 简单的中英文分词器
198
+ * 中文按字符切分(1-2 字符组合),英文按空格/标点切分
199
+ */
200
+ function tokenize(text) {
201
+ const tokens = [];
202
+ const lower = text.toLowerCase();
203
+ // 英文词(提取连续的字母/数字序列)
204
+ const englishWords = lower.match(/[a-z0-9_]+/g) || [];
205
+ for (const word of englishWords) {
206
+ if (word.length > 1) {
207
+ tokens.push(word);
208
+ }
209
+ }
210
+ // 中文字符处理(提取连续中文序列,然后做 bigram 分词)
211
+ const chineseSegments = lower.match(/[\u4e00-\u9fff]+/g) || [];
212
+ for (const segment of chineseSegments) {
213
+ // 单字
214
+ for (const char of segment) {
215
+ tokens.push(char);
216
+ }
217
+ // Bigram(2 字组合)
218
+ for (let i = 0; i < segment.length - 1; i++) {
219
+ tokens.push(segment.slice(i, i + 2));
220
+ }
221
+ }
222
+ return tokens;
223
+ }
224
+ function splitIntoParagraphs(content) {
225
+ // 按标题(##)或空行分割成段落
226
+ return content.split(/\n(?=##?\s)|\n{2,}/).filter(p => p.trim().length > 20);
227
+ }
228
+ function calculateKeywordRelevance(text, keywords) {
229
+ const lower = text.toLowerCase();
230
+ let hits = 0;
231
+ for (const kw of keywords) {
232
+ if (lower.includes(kw))
233
+ hits++;
234
+ }
235
+ return hits / keywords.length;
236
+ }
237
+ function inferType(filePath) {
238
+ if (filePath.includes('pitfall'))
239
+ return 'pitfall';
240
+ if (filePath.includes('decision'))
241
+ return 'decision';
242
+ if (filePath.includes('convention'))
243
+ return 'convention';
244
+ if (filePath.includes('overview'))
245
+ return 'overview';
246
+ return 'general';
247
+ }
248
+ async function findMdFiles(dir) {
249
+ const files = [];
250
+ try {
251
+ const entries = await readdir(dir, { withFileTypes: true, recursive: true });
252
+ for (const entry of entries) {
253
+ if (entry.isFile() && entry.name.endsWith('.md')) {
254
+ files.push(join(entry.parentPath, entry.name));
255
+ }
256
+ }
257
+ }
258
+ catch {
259
+ // 目录不存在,返回空
260
+ }
261
+ return files;
262
+ }
263
+ //# sourceMappingURL=memory-search.js.map
@@ -0,0 +1,21 @@
1
+ import { writeFile, appendFile, mkdir } from 'node:fs/promises';
2
+ import { join, dirname } from 'node:path';
3
+ /** @deprecated 使用 memoryLearn 替代 */
4
+ export async function memoryWrite(workspacePath, input) {
5
+ const fullPath = join(workspacePath, input.target);
6
+ // 确保目录存在
7
+ await mkdir(dirname(fullPath), { recursive: true });
8
+ // 格式化内容
9
+ let formattedContent = input.content;
10
+ if (input.source_task_id) {
11
+ formattedContent += `\n<!-- source: ${input.source_task_id} -->\n`;
12
+ }
13
+ if (input.mode === 'append') {
14
+ await appendFile(fullPath, '\n' + formattedContent);
15
+ }
16
+ else {
17
+ await writeFile(fullPath, formattedContent, 'utf-8');
18
+ }
19
+ return { success: true, path: input.target };
20
+ }
21
+ //# sourceMappingURL=memory-write.js.map
@@ -0,0 +1,47 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getOrgMember, getOwnershipByTarget, getEscalationRules } from '@team-anya/db';
4
+ export async function orgLookup(db, workspacePath, input) {
5
+ const output = {};
6
+ // 查成员
7
+ if (input.member_id) {
8
+ const member = getOrgMember(db, input.member_id);
9
+ if (member) {
10
+ output.member = {
11
+ member_id: member.member_id,
12
+ name: member.name,
13
+ role: member.role,
14
+ team: member.team,
15
+ timezone: member.timezone,
16
+ };
17
+ // 读协作者画像文件
18
+ if (member.profile_path) {
19
+ try {
20
+ output.collaborator_profile = await readFile(join(workspacePath, member.profile_path), 'utf-8');
21
+ }
22
+ catch {
23
+ // 文件不存在,忽略
24
+ }
25
+ }
26
+ }
27
+ }
28
+ // 查归属
29
+ if (input.target) {
30
+ const ownership = getOwnershipByTarget(db, input.target);
31
+ output.ownership = ownership.map(o => ({
32
+ scope: o.scope,
33
+ target: o.target,
34
+ member_id: o.member_id,
35
+ role: o.role,
36
+ }));
37
+ }
38
+ // 查升级路径
39
+ const rules = getEscalationRules(db);
40
+ output.escalation_path = rules.map(r => ({
41
+ after_minutes: r.after_minutes,
42
+ action: r.action,
43
+ targets: JSON.parse(r.targets),
44
+ }));
45
+ return output;
46
+ }
47
+ //# sourceMappingURL=org-lookup.js.map
@@ -0,0 +1,28 @@
1
+ import { getProject, getProjectRepos } from '@team-anya/db';
2
+ export async function projectGet(db, _workspacePath, input) {
3
+ const row = getProject(db, input.project_id);
4
+ if (!row) {
5
+ return { found: false };
6
+ }
7
+ const repos = getProjectRepos(db, input.project_id);
8
+ return {
9
+ found: true,
10
+ project: {
11
+ project_id: row.project_id,
12
+ name: row.name,
13
+ description: row.description ?? null,
14
+ platform: row.platform ?? 'local',
15
+ claude_md: row.claude_md ?? null,
16
+ deleted_at: row.deleted_at ?? null,
17
+ created_at: row.created_at,
18
+ updated_at: row.updated_at,
19
+ repos: repos.map(r => ({
20
+ name: r.name,
21
+ git_url: r.git_url ?? null,
22
+ repo_path: r.repo_path ?? null,
23
+ default_branch: r.default_branch ?? 'main',
24
+ })),
25
+ },
26
+ };
27
+ }
28
+ //# sourceMappingURL=project-get.js.map
@@ -0,0 +1,20 @@
1
+ import { getAllProjects, getProjectRepos } from '@team-anya/db';
2
+ export async function projectList(db, _workspacePath, input) {
3
+ const includeDeleted = input.include_deleted ?? false;
4
+ let rows = getAllProjects(db, includeDeleted);
5
+ if (input.platform) {
6
+ rows = rows.filter(p => p.platform === input.platform);
7
+ }
8
+ const projects = rows.map(p => ({
9
+ project_id: p.project_id,
10
+ name: p.name,
11
+ description: p.description ?? null,
12
+ platform: p.platform ?? 'local',
13
+ repo_count: getProjectRepos(db, p.project_id).length,
14
+ deleted_at: p.deleted_at ?? null,
15
+ created_at: p.created_at,
16
+ updated_at: p.updated_at,
17
+ }));
18
+ return { projects };
19
+ }
20
+ //# sourceMappingURL=project-list.js.map
@@ -0,0 +1,68 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getAllTasks, getActiveCommitments, getOpenOpportunities } from '@team-anya/db';
4
+ export async function reportDaily(db, workspacePath, input) {
5
+ const allTasks = getAllTasks(db);
6
+ const commitments = getActiveCommitments(db);
7
+ const opportunities = getOpenOpportunities(db);
8
+ // 分类任务
9
+ const done = allTasks.filter(t => t.status === 'DONE' && t.updated_at?.startsWith(input.date));
10
+ const inProgress = allTasks.filter(t => ['IN_PROGRESS', 'TESTING', 'FIXING'].includes(t.status));
11
+ const blocked = allTasks.filter(t => t.status === 'BLOCKED');
12
+ const prOpen = allTasks.filter(t => ['PR_OPEN', 'WAITING_REVIEW'].includes(t.status));
13
+ // 组装日报
14
+ const lines = [];
15
+ lines.push(`# Anya 日报 ${input.date}\n`);
16
+ if (done.length > 0) {
17
+ lines.push('## 今日完成');
18
+ for (const t of done) {
19
+ const pr = t.pr_url ? ` → ${t.pr_url}` : '';
20
+ lines.push(`- [${t.task_id}] ${t.title}${pr}`);
21
+ }
22
+ lines.push('');
23
+ }
24
+ if (prOpen.length > 0) {
25
+ lines.push('## 等待 Review');
26
+ for (const t of prOpen) {
27
+ lines.push(`- [${t.task_id}] ${t.title} (${t.status})${t.pr_url ? ` → ${t.pr_url}` : ''}`);
28
+ }
29
+ lines.push('');
30
+ }
31
+ if (inProgress.length > 0) {
32
+ lines.push('## 进行中');
33
+ for (const t of inProgress) {
34
+ lines.push(`- [${t.task_id}] ${t.title} (${t.status}, assignee: ${t.assignee ?? 'unassigned'})`);
35
+ }
36
+ lines.push('');
37
+ }
38
+ if (blocked.length > 0) {
39
+ lines.push('## 阻塞项');
40
+ for (const t of blocked) {
41
+ lines.push(`- [${t.task_id}] ${t.title} — ${t.blocked_reason ?? '原因未知'}`);
42
+ }
43
+ lines.push('');
44
+ }
45
+ if (commitments.length > 0) {
46
+ lines.push('## 活跃承诺');
47
+ for (const c of commitments) {
48
+ const deadline = c.deadline ? ` (截止: ${c.deadline})` : '';
49
+ lines.push(`- [${c.task_id}] 向 ${c.promised_to}: ${c.promise}${deadline}`);
50
+ }
51
+ lines.push('');
52
+ }
53
+ if (opportunities.length > 0) {
54
+ lines.push('## 待处理机会');
55
+ for (const o of opportunities) {
56
+ lines.push(`- ${o.summary} (评分: ${o.total_score?.toFixed(2) ?? 'N/A'})`);
57
+ }
58
+ lines.push('');
59
+ }
60
+ const content = lines.join('\n');
61
+ // 写入文件
62
+ const reportsDir = join(workspacePath, 'reports');
63
+ await mkdir(reportsDir, { recursive: true });
64
+ const filePath = join(reportsDir, `daily-${input.date}.md`);
65
+ await writeFile(filePath, content, 'utf-8');
66
+ return { content, path: `reports/daily-${input.date}.md` };
67
+ }
68
+ //# sourceMappingURL=report-daily.js.map
@@ -0,0 +1,29 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getTask, getClarifications, getCommitmentByTask, getAuditEventsByTask } from '@team-anya/db';
4
+ import { TaskNotFoundError } from '@team-anya/core';
5
+ export async function taskGet(db, workspacePath, input) {
6
+ const task = getTask(db, input.task_id);
7
+ if (!task)
8
+ throw new TaskNotFoundError(input.task_id);
9
+ const clarifications = getClarifications(db, input.task_id);
10
+ const commitment = getCommitmentByTask(db, input.task_id);
11
+ const events = getAuditEventsByTask(db, input.task_id);
12
+ // 读取文件内容
13
+ const taskDir = join(workspacePath, 'yor', input.task_id);
14
+ const brief = await readFileSafe(join(taskDir, 'brief.md'));
15
+ const result = await readFileSafe(join(taskDir, 'result.md'));
16
+ const handoff = await readFileSafe(join(taskDir, 'handoff.md'));
17
+ // 只返回最近 20 条事件
18
+ const recent_events = events.slice(-20);
19
+ return { task, clarifications, commitment, recent_events, brief, result, handoff };
20
+ }
21
+ async function readFileSafe(path) {
22
+ try {
23
+ return await readFile(path, 'utf-8');
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ //# sourceMappingURL=task-get.js.map
@@ -0,0 +1,34 @@
1
+ import { getTask, updateTask, insertAuditEvent, } from '@team-anya/db';
2
+ import { assertTransition, TaskNotFoundError } from '@team-anya/core';
3
+ /**
4
+ * task.update - 原子级任务状态更新
5
+ *
6
+ * 使用 core state machine 校验转换合法性,同时写 audit log。
7
+ * Loid 和 Yor 均可使用。
8
+ */
9
+ export async function taskUpdate(db, input) {
10
+ const task = getTask(db, input.task_id);
11
+ if (!task)
12
+ throw new TaskNotFoundError(input.task_id);
13
+ const fromStatus = task.status;
14
+ const toStatus = input.status;
15
+ // 校验状态转换合法性(非法会抛 InvalidTransitionError)
16
+ assertTransition(fromStatus, toStatus);
17
+ // 执行更新
18
+ updateTask(db, input.task_id, { status: toStatus });
19
+ // 写审计日志
20
+ insertAuditEvent(db, {
21
+ event_type: 'task_status_changed',
22
+ actor: 'system',
23
+ task_id: input.task_id,
24
+ summary: `${fromStatus} → ${toStatus}${input.reason ? `: ${input.reason}` : ''}`,
25
+ detail: JSON.stringify({ from: fromStatus, to: toStatus, reason: input.reason }),
26
+ });
27
+ return {
28
+ task_id: input.task_id,
29
+ from: fromStatus,
30
+ to: toStatus,
31
+ success: true,
32
+ };
33
+ }
34
+ //# sourceMappingURL=task-update.js.map
@@ -0,0 +1,15 @@
1
+ import { insertAuditEvent } from '@team-anya/db';
2
+ /**
3
+ * 记录决策理由到审计日志
4
+ */
5
+ export async function decisionLog(db, input) {
6
+ insertAuditEvent(db, {
7
+ event_type: 'loid_decision',
8
+ actor: 'loid',
9
+ task_id: input.task_id ?? undefined,
10
+ summary: input.decision,
11
+ detail: JSON.stringify({ reasoning: input.reasoning }),
12
+ });
13
+ return { logged: true };
14
+ }
15
+ //# sourceMappingURL=decision-log.js.map
@@ -0,0 +1,15 @@
1
+ import { insertAuditEvent } from '@team-anya/db';
2
+ /**
3
+ * 不处理此消息,记录原因
4
+ *
5
+ * 仅用于环境消息(非定向触达)。
6
+ */
7
+ export async function decisionNoAction(db, input) {
8
+ insertAuditEvent(db, {
9
+ event_type: 'loid_no_action',
10
+ actor: 'loid',
11
+ summary: `不处理: ${input.reason}`,
12
+ });
13
+ return { acknowledged: true };
14
+ }
15
+ //# sourceMappingURL=decision-no-action.js.map
@@ -0,0 +1,30 @@
1
+ import { execFile as execFileCb } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { updateTask, insertAuditEvent } from '@team-anya/db';
4
+ const execFile = promisify(execFileCb);
5
+ export async function deliveryCreatePr(deps, input) {
6
+ const { db, logger } = deps;
7
+ const args = ['pr', 'create', '--title', input.title, '--body', input.description];
8
+ if (input.base_branch) {
9
+ args.push('--base', input.base_branch);
10
+ }
11
+ logger?.info(`[anya:pipeline] [Loid] delivery.create_pr ${input.task_id} | title="${input.title}"`);
12
+ const { stdout } = await execFile('gh', args, {
13
+ cwd: input.working_dir,
14
+ });
15
+ const prUrl = stdout.trim();
16
+ // 更新任务的 PR 链接
17
+ updateTask(db, input.task_id, { pr_url: prUrl });
18
+ insertAuditEvent(db, {
19
+ event_type: 'pr_created',
20
+ actor: 'loid',
21
+ task_id: input.task_id,
22
+ summary: `PR 已创建: ${input.title}`,
23
+ detail: JSON.stringify({ pr_url: prUrl, base_branch: input.base_branch }),
24
+ });
25
+ return {
26
+ task_id: input.task_id,
27
+ pr_url: prUrl,
28
+ };
29
+ }
30
+ //# sourceMappingURL=delivery-create-pr.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * delivery.share - 分享链接/产物
3
+ *
4
+ * 当前为 stub,返回 not_implemented 提示。
5
+ */
6
+ export async function deliveryShare(_input) {
7
+ return {
8
+ status: 'not_implemented',
9
+ message: 'delivery.share 尚未实现,将在后续版本支持分享功能。',
10
+ };
11
+ }
12
+ //# sourceMappingURL=delivery-share.js.map
@@ -0,0 +1,77 @@
1
+ import { execFile as execFileCb } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { updateTask, insertAuditEvent, getTask } from '@team-anya/db';
4
+ const execFile = promisify(execFileCb);
5
+ export async function deliverySubmit(deps, input) {
6
+ const { db, logger } = deps;
7
+ // 确定平台:通过任务的 project_id 查项目配置
8
+ const task = getTask(db, input.task_id);
9
+ let platform = 'local';
10
+ if (task?.project_id && deps.getProjectConfig) {
11
+ const config = await deps.getProjectConfig(task.project_id);
12
+ platform = config.platform;
13
+ }
14
+ logger?.info(`[anya:pipeline] [Loid] delivery.submit ${input.task_id} | platform=${platform} title="${input.title}"`);
15
+ switch (platform) {
16
+ case 'github': {
17
+ const args = ['pr', 'create', '--title', input.title, '--body', input.description];
18
+ if (input.base_branch) {
19
+ args.push('--base', input.base_branch);
20
+ }
21
+ const { stdout } = await execFile('gh', args, { cwd: input.working_dir });
22
+ const prUrl = stdout.trim();
23
+ updateTask(db, input.task_id, { pr_url: prUrl });
24
+ insertAuditEvent(db, {
25
+ event_type: 'pr_created',
26
+ actor: 'loid',
27
+ task_id: input.task_id,
28
+ summary: `GitHub PR 已创建: ${input.title}`,
29
+ detail: JSON.stringify({ pr_url: prUrl, base_branch: input.base_branch, platform }),
30
+ });
31
+ return { task_id: input.task_id, pr_url: prUrl, platform };
32
+ }
33
+ case 'gitlab': {
34
+ const args = [
35
+ 'mr', 'create',
36
+ '--title', input.title,
37
+ '--description', input.description,
38
+ '--yes',
39
+ ];
40
+ if (input.base_branch) {
41
+ args.push('--target-branch', input.base_branch);
42
+ }
43
+ const { stdout } = await execFile('glab', args, { cwd: input.working_dir });
44
+ // glab 输出格式:通常包含 MR URL
45
+ const mrUrl = stdout.trim().split('\n').pop() ?? stdout.trim();
46
+ updateTask(db, input.task_id, { pr_url: mrUrl });
47
+ insertAuditEvent(db, {
48
+ event_type: 'mr_created',
49
+ actor: 'loid',
50
+ task_id: input.task_id,
51
+ summary: `GitLab MR 已创建: ${input.title}`,
52
+ detail: JSON.stringify({ mr_url: mrUrl, base_branch: input.base_branch, platform }),
53
+ });
54
+ return { task_id: input.task_id, pr_url: mrUrl, platform };
55
+ }
56
+ case 'local': {
57
+ // 纯本地任务,记录最终 commit hash
58
+ let commitHash = 'unknown';
59
+ try {
60
+ const { stdout } = await execFile('git', ['-C', input.working_dir, 'log', '-1', '--format=%H']);
61
+ commitHash = stdout.trim();
62
+ }
63
+ catch {
64
+ // git 命令失败,忽略
65
+ }
66
+ insertAuditEvent(db, {
67
+ event_type: 'delivery_local',
68
+ actor: 'loid',
69
+ task_id: input.task_id,
70
+ summary: `本地交付完成: ${input.title}`,
71
+ detail: JSON.stringify({ commit_hash: commitHash, platform }),
72
+ });
73
+ return { task_id: input.task_id, commit_hash: commitHash, platform };
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=delivery-submit.js.map