@deimoscloud/coreai 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 (216) hide show
  1. package/.prettierrc +9 -0
  2. package/AGENT_SPEC.md +347 -0
  3. package/ARCHITECTURE.md +547 -0
  4. package/DRAFT_PRD.md +1440 -0
  5. package/IMPLEMENTATION_PLAN.md +256 -0
  6. package/PRODUCT.md +473 -0
  7. package/README.md +303 -0
  8. package/WORKFLOWS.md +295 -0
  9. package/agents/_templates/ic-engineer.md +185 -0
  10. package/agents/_templates/reviewer.md +182 -0
  11. package/agents/backend-engineer.yaml +72 -0
  12. package/agents/devops-engineer.yaml +72 -0
  13. package/agents/engineering-manager.yaml +70 -0
  14. package/agents/examples/android-engineer.md +302 -0
  15. package/agents/examples/backend-engineer.md +320 -0
  16. package/agents/examples/devops-engineer.md +742 -0
  17. package/agents/examples/engineering-manager.md +469 -0
  18. package/agents/examples/frontend-engineer.md +58 -0
  19. package/agents/examples/product-manager.md +315 -0
  20. package/agents/examples/qa-engineer.md +371 -0
  21. package/agents/examples/security-engineer.md +525 -0
  22. package/agents/examples/solutions-architect.md +351 -0
  23. package/agents/examples/wearos-engineer.md +359 -0
  24. package/agents/frontend-engineer.yaml +72 -0
  25. package/commands/core/check-inbox.md +34 -0
  26. package/commands/core/delegate.md +30 -0
  27. package/commands/core/git-commit.md +144 -0
  28. package/commands/core/pr-create.md +193 -0
  29. package/commands/core/review.md +56 -0
  30. package/commands/core/sprint-status.md +65 -0
  31. package/commands/optional/docs-update.md +200 -0
  32. package/commands/optional/jira-create.md +200 -0
  33. package/commands/optional/jira-transition.md +184 -0
  34. package/commands/optional/worktree-cleanup.md +167 -0
  35. package/commands/optional/worktree-setup.md +110 -0
  36. package/dist/cli/index.js +4037 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/index.d.ts +2978 -0
  39. package/dist/index.js +3867 -0
  40. package/dist/index.js.map +1 -0
  41. package/eslint.config.js +29 -0
  42. package/jest.config.js +22 -0
  43. package/knowledge-library/README.md +118 -0
  44. package/knowledge-library/android-engineer/context/current.txt +42 -0
  45. package/knowledge-library/android-engineer/control/decisions.txt +9 -0
  46. package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
  47. package/knowledge-library/android-engineer/control/objectives.txt +26 -0
  48. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  49. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  50. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  51. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  52. package/knowledge-library/architecture.txt +61 -0
  53. package/knowledge-library/backend-engineer/context/current.txt +42 -0
  54. package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
  55. package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
  56. package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
  57. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  58. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  59. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  60. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  61. package/knowledge-library/context.txt +52 -0
  62. package/knowledge-library/devops-engineer/context/current.txt +42 -0
  63. package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
  64. package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
  65. package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
  66. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  67. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  68. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  69. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  70. package/knowledge-library/engineering-manager/context/current.txt +40 -0
  71. package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
  72. package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
  73. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  74. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  76. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  77. package/knowledge-library/prd.txt +81 -0
  78. package/knowledge-library/product-manager/context/current.txt +42 -0
  79. package/knowledge-library/product-manager/control/decisions.txt +9 -0
  80. package/knowledge-library/product-manager/control/dependencies.txt +19 -0
  81. package/knowledge-library/product-manager/control/objectives.txt +26 -0
  82. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  83. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  84. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  85. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  86. package/knowledge-library/qa-engineer/context/current.txt +42 -0
  87. package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
  88. package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
  89. package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
  90. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  91. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  92. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  93. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  94. package/knowledge-library/security-engineer/context/current.txt +42 -0
  95. package/knowledge-library/security-engineer/control/decisions.txt +9 -0
  96. package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
  97. package/knowledge-library/security-engineer/control/objectives.txt +26 -0
  98. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  99. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  100. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  101. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  102. package/knowledge-library/solutions-architect/context/current.txt +42 -0
  103. package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
  104. package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
  105. package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
  106. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  107. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  108. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  109. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  110. package/knowledge-library/wearos-engineer/context/current.txt +42 -0
  111. package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
  112. package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
  113. package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
  114. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  115. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  116. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  117. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  118. package/package.json +66 -0
  119. package/schemas/agent.schema.json +171 -0
  120. package/schemas/coreai.config.schema.json +257 -0
  121. package/scripts/add-agent.sh +323 -0
  122. package/scripts/install.sh +354 -0
  123. package/src/adapters/factory.test.ts +386 -0
  124. package/src/adapters/factory.ts +305 -0
  125. package/src/adapters/index.ts +113 -0
  126. package/src/adapters/interfaces.ts +268 -0
  127. package/src/adapters/mcp/client.test.ts +130 -0
  128. package/src/adapters/mcp/client.ts +451 -0
  129. package/src/adapters/mcp/discovery.test.ts +315 -0
  130. package/src/adapters/mcp/discovery.ts +340 -0
  131. package/src/adapters/mcp/index.ts +66 -0
  132. package/src/adapters/mcp/mapper.test.ts +218 -0
  133. package/src/adapters/mcp/mapper.ts +536 -0
  134. package/src/adapters/mcp/registry.test.ts +433 -0
  135. package/src/adapters/mcp/registry.ts +550 -0
  136. package/src/adapters/mcp/types.ts +258 -0
  137. package/src/adapters/native/filesystem.test.ts +350 -0
  138. package/src/adapters/native/filesystem.ts +393 -0
  139. package/src/adapters/native/github.test.ts +173 -0
  140. package/src/adapters/native/github.ts +627 -0
  141. package/src/adapters/native/index.ts +22 -0
  142. package/src/adapters/native/selector.test.ts +224 -0
  143. package/src/adapters/native/selector.ts +150 -0
  144. package/src/adapters/types.ts +270 -0
  145. package/src/agents/compiler.test.ts +399 -0
  146. package/src/agents/compiler.ts +359 -0
  147. package/src/agents/index.ts +36 -0
  148. package/src/agents/loader.test.ts +319 -0
  149. package/src/agents/loader.ts +143 -0
  150. package/src/agents/resolver.test.ts +282 -0
  151. package/src/agents/resolver.ts +262 -0
  152. package/src/agents/types.ts +87 -0
  153. package/src/cache/index.ts +38 -0
  154. package/src/cache/interfaces.ts +283 -0
  155. package/src/cache/manager.test.ts +266 -0
  156. package/src/cache/manager.ts +388 -0
  157. package/src/cache/provider.test.ts +485 -0
  158. package/src/cache/provider.ts +745 -0
  159. package/src/cache/types.test.ts +192 -0
  160. package/src/cache/types.ts +313 -0
  161. package/src/cli/commands/build.test.ts +248 -0
  162. package/src/cli/commands/build.ts +244 -0
  163. package/src/cli/commands/cache.test.ts +221 -0
  164. package/src/cli/commands/cache.ts +229 -0
  165. package/src/cli/commands/index.ts +63 -0
  166. package/src/cli/commands/init.test.ts +173 -0
  167. package/src/cli/commands/init.ts +296 -0
  168. package/src/cli/commands/skills.test.ts +272 -0
  169. package/src/cli/commands/skills.ts +348 -0
  170. package/src/cli/commands/status.test.ts +392 -0
  171. package/src/cli/commands/status.ts +332 -0
  172. package/src/cli/commands/sync.test.ts +213 -0
  173. package/src/cli/commands/sync.ts +251 -0
  174. package/src/cli/commands/validate.test.ts +216 -0
  175. package/src/cli/commands/validate.ts +340 -0
  176. package/src/cli/index.test.ts +190 -0
  177. package/src/cli/index.ts +493 -0
  178. package/src/commands/context.test.ts +163 -0
  179. package/src/commands/context.ts +111 -0
  180. package/src/commands/index.ts +56 -0
  181. package/src/commands/loader.test.ts +273 -0
  182. package/src/commands/loader.ts +355 -0
  183. package/src/commands/registry.test.ts +384 -0
  184. package/src/commands/registry.ts +248 -0
  185. package/src/commands/runner.test.ts +297 -0
  186. package/src/commands/runner.ts +222 -0
  187. package/src/commands/types.ts +361 -0
  188. package/src/config/index.ts +19 -0
  189. package/src/config/loader.test.ts +262 -0
  190. package/src/config/loader.ts +188 -0
  191. package/src/config/types.ts +154 -0
  192. package/src/context/index.ts +14 -0
  193. package/src/context/loader.test.ts +334 -0
  194. package/src/context/loader.ts +357 -0
  195. package/src/index.test.ts +13 -0
  196. package/src/index.ts +244 -0
  197. package/src/knowledge-library/index.ts +44 -0
  198. package/src/knowledge-library/manager.test.ts +536 -0
  199. package/src/knowledge-library/manager.ts +804 -0
  200. package/src/knowledge-library/types.ts +432 -0
  201. package/src/skills/generator.test.ts +602 -0
  202. package/src/skills/generator.ts +491 -0
  203. package/src/skills/index.ts +27 -0
  204. package/src/skills/templates.ts +520 -0
  205. package/src/skills/types.ts +251 -0
  206. package/templates/completion-report.md +72 -0
  207. package/templates/feedback.md +56 -0
  208. package/templates/project-files/CLAUDE.md.template +109 -0
  209. package/templates/project-files/coreai.json.example +47 -0
  210. package/templates/project-files/mcp.json.template +20 -0
  211. package/templates/review-complete.md +64 -0
  212. package/templates/review-request.md +67 -0
  213. package/templates/task-assignment.md +51 -0
  214. package/tsconfig.build.json +4 -0
  215. package/tsconfig.json +26 -0
  216. package/tsup.config.ts +23 -0
@@ -0,0 +1,536 @@
1
+ /**
2
+ * KnowledgeLibrary Manager Tests
3
+ */
4
+
5
+ import { promises as fs } from 'fs';
6
+ import { join } from 'path';
7
+ import { tmpdir } from 'os';
8
+ import {
9
+ DEFAULT_KNOWLEDGE_LIBRARY_PATH,
10
+ STANDARD_FILES,
11
+ AGENT_DIRECTORIES,
12
+ CONTROL_FILES,
13
+ getAgentDirectories,
14
+ initKnowledgeLibrary,
15
+ initAgentKnowledgeLibrary,
16
+ agentKnowledgeLibraryExists,
17
+ getAgentKnowledgeState,
18
+ generateMessageFilename,
19
+ readInboxMessages,
20
+ writeInboxMessage,
21
+ markMessageProcessed,
22
+ getKnowledgeLibraryState,
23
+ updateAgentContext,
24
+ formatKnowledgeLibraryState,
25
+ formatAgentKnowledgeState,
26
+ } from './manager.js';
27
+
28
+ describe('KnowledgeLibrary Manager', () => {
29
+ let testDir: string;
30
+
31
+ beforeEach(async () => {
32
+ testDir = join(tmpdir(), `kl-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
33
+ await fs.mkdir(testDir, { recursive: true });
34
+ });
35
+
36
+ afterEach(async () => {
37
+ try {
38
+ await fs.rm(testDir, { recursive: true, force: true });
39
+ } catch {
40
+ // Ignore cleanup errors
41
+ }
42
+ });
43
+
44
+ describe('constants', () => {
45
+ it('should have correct default path', () => {
46
+ expect(DEFAULT_KNOWLEDGE_LIBRARY_PATH).toBe('KnowledgeLibrary');
47
+ });
48
+
49
+ it('should have correct standard files', () => {
50
+ expect(STANDARD_FILES.context).toBe('context.txt');
51
+ expect(STANDARD_FILES.architecture).toBe('architecture.txt');
52
+ expect(STANDARD_FILES.prd).toBe('prd.txt');
53
+ });
54
+
55
+ it('should have correct agent directories', () => {
56
+ expect(AGENT_DIRECTORIES).toContain('inbox');
57
+ expect(AGENT_DIRECTORIES).toContain('inbox/processed');
58
+ expect(AGENT_DIRECTORIES).toContain('outbox');
59
+ expect(AGENT_DIRECTORIES).toContain('context');
60
+ expect(AGENT_DIRECTORIES).toContain('control');
61
+ expect(AGENT_DIRECTORIES).toContain('history');
62
+ expect(AGENT_DIRECTORIES).toContain('tech');
63
+ });
64
+
65
+ it('should have correct control files', () => {
66
+ expect(CONTROL_FILES.objectives).toBe('objectives.txt');
67
+ expect(CONTROL_FILES.decisions).toBe('decisions.txt');
68
+ expect(CONTROL_FILES.dependencies).toBe('dependencies.txt');
69
+ });
70
+ });
71
+
72
+ describe('getAgentDirectories', () => {
73
+ it('should return correct paths', () => {
74
+ const basePath = '/project/KnowledgeLibrary';
75
+ const dirs = getAgentDirectories(basePath, 'backend-engineer');
76
+
77
+ expect(dirs.root).toBe('/project/KnowledgeLibrary/backend-engineer');
78
+ expect(dirs.inbox).toBe('/project/KnowledgeLibrary/backend-engineer/inbox');
79
+ expect(dirs.inboxProcessed).toBe(
80
+ '/project/KnowledgeLibrary/backend-engineer/inbox/processed'
81
+ );
82
+ expect(dirs.outbox).toBe('/project/KnowledgeLibrary/backend-engineer/outbox');
83
+ expect(dirs.context).toBe('/project/KnowledgeLibrary/backend-engineer/context');
84
+ expect(dirs.control).toBe('/project/KnowledgeLibrary/backend-engineer/control');
85
+ expect(dirs.history).toBe('/project/KnowledgeLibrary/backend-engineer/history');
86
+ expect(dirs.tech).toBe('/project/KnowledgeLibrary/backend-engineer/tech');
87
+ });
88
+ });
89
+
90
+ describe('initKnowledgeLibrary', () => {
91
+ it('should create base directory', () => {
92
+ const result = initKnowledgeLibrary({ projectRoot: testDir });
93
+
94
+ expect(result.success).toBe(true);
95
+ expect(result.createdDirs.length).toBeGreaterThan(0);
96
+ });
97
+
98
+ it('should create default files', async () => {
99
+ const result = initKnowledgeLibrary({ projectRoot: testDir, createDefaults: true });
100
+
101
+ expect(result.success).toBe(true);
102
+ expect(result.createdFiles.length).toBe(3);
103
+
104
+ const basePath = join(testDir, DEFAULT_KNOWLEDGE_LIBRARY_PATH);
105
+ const contextExists = await fs
106
+ .stat(join(basePath, 'context.txt'))
107
+ .then(() => true)
108
+ .catch(() => false);
109
+ const archExists = await fs
110
+ .stat(join(basePath, 'architecture.txt'))
111
+ .then(() => true)
112
+ .catch(() => false);
113
+ const prdExists = await fs
114
+ .stat(join(basePath, 'prd.txt'))
115
+ .then(() => true)
116
+ .catch(() => false);
117
+
118
+ expect(contextExists).toBe(true);
119
+ expect(archExists).toBe(true);
120
+ expect(prdExists).toBe(true);
121
+ });
122
+
123
+ it('should not create files when createDefaults is false', () => {
124
+ const result = initKnowledgeLibrary({ projectRoot: testDir, createDefaults: false });
125
+
126
+ expect(result.success).toBe(true);
127
+ expect(result.createdFiles.length).toBe(0);
128
+ });
129
+
130
+ it('should use custom base path', () => {
131
+ const result = initKnowledgeLibrary({
132
+ projectRoot: testDir,
133
+ basePath: 'CustomKL',
134
+ createDefaults: false,
135
+ });
136
+
137
+ expect(result.success).toBe(true);
138
+ expect(result.createdDirs).toContain(join(testDir, 'CustomKL'));
139
+ });
140
+ });
141
+
142
+ describe('initAgentKnowledgeLibrary', () => {
143
+ it('should create agent directories', async () => {
144
+ const result = initAgentKnowledgeLibrary('backend-engineer', {
145
+ projectRoot: testDir,
146
+ });
147
+
148
+ expect(result.success).toBe(true);
149
+
150
+ const basePath = join(testDir, DEFAULT_KNOWLEDGE_LIBRARY_PATH, 'backend-engineer');
151
+ for (const dir of AGENT_DIRECTORIES) {
152
+ const exists = await fs
153
+ .stat(join(basePath, dir))
154
+ .then(() => true)
155
+ .catch(() => false);
156
+ expect(exists).toBe(true);
157
+ }
158
+ });
159
+
160
+ it('should create default control files', async () => {
161
+ const result = initAgentKnowledgeLibrary('backend-engineer', {
162
+ projectRoot: testDir,
163
+ createDefaults: true,
164
+ });
165
+
166
+ expect(result.success).toBe(true);
167
+
168
+ const basePath = join(testDir, DEFAULT_KNOWLEDGE_LIBRARY_PATH, 'backend-engineer');
169
+ const objectivesExists = await fs
170
+ .stat(join(basePath, 'control', 'objectives.txt'))
171
+ .then(() => true)
172
+ .catch(() => false);
173
+ const decisionsExists = await fs
174
+ .stat(join(basePath, 'control', 'decisions.txt'))
175
+ .then(() => true)
176
+ .catch(() => false);
177
+ const depsExists = await fs
178
+ .stat(join(basePath, 'control', 'dependencies.txt'))
179
+ .then(() => true)
180
+ .catch(() => false);
181
+
182
+ expect(objectivesExists).toBe(true);
183
+ expect(decisionsExists).toBe(true);
184
+ expect(depsExists).toBe(true);
185
+ });
186
+
187
+ it('should create current context file', async () => {
188
+ const result = initAgentKnowledgeLibrary('backend-engineer', {
189
+ projectRoot: testDir,
190
+ createDefaults: true,
191
+ });
192
+
193
+ expect(result.success).toBe(true);
194
+
195
+ const contextPath = join(
196
+ testDir,
197
+ DEFAULT_KNOWLEDGE_LIBRARY_PATH,
198
+ 'backend-engineer',
199
+ 'context',
200
+ 'current.txt'
201
+ );
202
+ const exists = await fs
203
+ .stat(contextPath)
204
+ .then(() => true)
205
+ .catch(() => false);
206
+ expect(exists).toBe(true);
207
+ });
208
+ });
209
+
210
+ describe('agentKnowledgeLibraryExists', () => {
211
+ it('should return false for non-existent agent', () => {
212
+ const exists = agentKnowledgeLibraryExists('unknown-agent', { projectRoot: testDir });
213
+ expect(exists).toBe(false);
214
+ });
215
+
216
+ it('should return true for initialized agent', () => {
217
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
218
+ const exists = agentKnowledgeLibraryExists('backend-engineer', { projectRoot: testDir });
219
+ expect(exists).toBe(true);
220
+ });
221
+ });
222
+
223
+ describe('generateMessageFilename', () => {
224
+ it('should generate filename with timestamp and subject', () => {
225
+ const filename = generateMessageFilename('engineering-manager', 'Task Assignment');
226
+
227
+ expect(filename).toMatch(/^\d{12}-engineering-manager-task-assignment\.md$/);
228
+ });
229
+
230
+ it('should sanitize subject', () => {
231
+ const filename = generateMessageFilename('em', 'Special!@#$Characters');
232
+
233
+ expect(filename).toMatch(/^\d{12}-em-special-characters\.md$/);
234
+ });
235
+
236
+ it('should truncate long subjects', () => {
237
+ const filename = generateMessageFilename(
238
+ 'em',
239
+ 'This is a very long subject that should be truncated'
240
+ );
241
+
242
+ expect(filename.length).toBeLessThan(70);
243
+ });
244
+ });
245
+
246
+ describe('writeInboxMessage and readInboxMessages', () => {
247
+ beforeEach(() => {
248
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
249
+ initAgentKnowledgeLibrary('engineering-manager', { projectRoot: testDir });
250
+ });
251
+
252
+ it('should write message to recipient inbox', () => {
253
+ const result = writeInboxMessage({
254
+ projectRoot: testDir,
255
+ type: 'task-assignment',
256
+ from: 'engineering-manager',
257
+ to: 'backend-engineer',
258
+ subject: 'Implement Feature X',
259
+ body: 'Please implement feature X as described in the ticket.',
260
+ ticket: 'PROJ-123',
261
+ priority: 'P1',
262
+ });
263
+
264
+ expect(result.success).toBe(true);
265
+ expect(result.path).toBeDefined();
266
+ });
267
+
268
+ it('should read message from inbox', () => {
269
+ writeInboxMessage({
270
+ projectRoot: testDir,
271
+ type: 'task-assignment',
272
+ from: 'engineering-manager',
273
+ to: 'backend-engineer',
274
+ subject: 'Implement Feature X',
275
+ body: 'Please implement feature X.',
276
+ ticket: 'PROJ-123',
277
+ priority: 'P1',
278
+ });
279
+
280
+ const messages = readInboxMessages('backend-engineer', { projectRoot: testDir });
281
+
282
+ expect(messages.length).toBe(1);
283
+ expect(messages[0]?.metadata.type).toBe('task-assignment');
284
+ expect(messages[0]?.metadata.from).toBe('engineering-manager');
285
+ expect(messages[0]?.metadata.ticket).toBe('PROJ-123');
286
+ expect(messages[0]?.metadata.priority).toBe('P1');
287
+ });
288
+
289
+ it('should filter messages by type', () => {
290
+ writeInboxMessage({
291
+ projectRoot: testDir,
292
+ type: 'task-assignment',
293
+ from: 'em',
294
+ to: 'backend-engineer',
295
+ subject: 'Task 1',
296
+ body: 'Body 1',
297
+ });
298
+
299
+ writeInboxMessage({
300
+ projectRoot: testDir,
301
+ type: 'feedback',
302
+ from: 'em',
303
+ to: 'backend-engineer',
304
+ subject: 'Feedback 1',
305
+ body: 'Body 2',
306
+ });
307
+
308
+ const tasks = readInboxMessages('backend-engineer', {
309
+ projectRoot: testDir,
310
+ type: 'task-assignment',
311
+ });
312
+
313
+ expect(tasks.length).toBe(1);
314
+ expect(tasks[0]?.metadata.type).toBe('task-assignment');
315
+ });
316
+
317
+ it('should filter messages by sender', () => {
318
+ writeInboxMessage({
319
+ projectRoot: testDir,
320
+ type: 'task-assignment',
321
+ from: 'em',
322
+ to: 'backend-engineer',
323
+ subject: 'Task from EM',
324
+ body: 'Body',
325
+ });
326
+
327
+ writeInboxMessage({
328
+ projectRoot: testDir,
329
+ type: 'review-request',
330
+ from: 'qa-engineer',
331
+ to: 'backend-engineer',
332
+ subject: 'Review request',
333
+ body: 'Body',
334
+ });
335
+
336
+ const fromEM = readInboxMessages('backend-engineer', {
337
+ projectRoot: testDir,
338
+ from: 'em',
339
+ });
340
+
341
+ expect(fromEM.length).toBe(1);
342
+ expect(fromEM[0]?.metadata.from).toBe('em');
343
+ });
344
+
345
+ it('should copy message to sender outbox', async () => {
346
+ writeInboxMessage({
347
+ projectRoot: testDir,
348
+ type: 'task-assignment',
349
+ from: 'engineering-manager',
350
+ to: 'backend-engineer',
351
+ subject: 'Test Task',
352
+ body: 'Body',
353
+ });
354
+
355
+ const outboxPath = join(
356
+ testDir,
357
+ DEFAULT_KNOWLEDGE_LIBRARY_PATH,
358
+ 'engineering-manager',
359
+ 'outbox'
360
+ );
361
+ const files = await fs.readdir(outboxPath);
362
+ expect(files.length).toBe(1);
363
+ });
364
+ });
365
+
366
+ describe('markMessageProcessed', () => {
367
+ beforeEach(() => {
368
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
369
+ initAgentKnowledgeLibrary('engineering-manager', { projectRoot: testDir });
370
+ });
371
+
372
+ it('should move message to processed folder', async () => {
373
+ writeInboxMessage({
374
+ projectRoot: testDir,
375
+ type: 'task-assignment',
376
+ from: 'em',
377
+ to: 'backend-engineer',
378
+ subject: 'Task',
379
+ body: 'Body',
380
+ });
381
+
382
+ const messages = readInboxMessages('backend-engineer', { projectRoot: testDir });
383
+ expect(messages.length).toBe(1);
384
+
385
+ const filename = messages[0]?.filename ?? '';
386
+ const result = markMessageProcessed('backend-engineer', filename, { projectRoot: testDir });
387
+
388
+ expect(result.success).toBe(true);
389
+
390
+ // Should not appear in inbox anymore
391
+ const inboxMessages = readInboxMessages('backend-engineer', { projectRoot: testDir });
392
+ expect(inboxMessages.length).toBe(0);
393
+
394
+ // Should appear in processed
395
+ const processedMessages = readInboxMessages('backend-engineer', {
396
+ projectRoot: testDir,
397
+ includeProcessed: true,
398
+ });
399
+ expect(processedMessages.length).toBe(1);
400
+ });
401
+
402
+ it('should fail for non-existent message', () => {
403
+ const result = markMessageProcessed('backend-engineer', 'nonexistent.md', {
404
+ projectRoot: testDir,
405
+ });
406
+
407
+ expect(result.success).toBe(false);
408
+ expect(result.error).toContain('not found');
409
+ });
410
+ });
411
+
412
+ describe('getAgentKnowledgeState', () => {
413
+ it('should return uninitialized state for new agent', () => {
414
+ const state = getAgentKnowledgeState('unknown-agent', { projectRoot: testDir });
415
+
416
+ expect(state.initialized).toBe(false);
417
+ expect(state.pendingMessages).toEqual([]);
418
+ });
419
+
420
+ it('should return correct state for initialized agent', () => {
421
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
422
+ initAgentKnowledgeLibrary('em', { projectRoot: testDir });
423
+
424
+ writeInboxMessage({
425
+ projectRoot: testDir,
426
+ type: 'task-assignment',
427
+ from: 'em',
428
+ to: 'backend-engineer',
429
+ subject: 'Task',
430
+ body: 'Body',
431
+ });
432
+
433
+ const state = getAgentKnowledgeState('backend-engineer', { projectRoot: testDir });
434
+
435
+ expect(state.initialized).toBe(true);
436
+ expect(state.agentName).toBe('backend-engineer');
437
+ expect(state.pendingMessages.length).toBe(1);
438
+ expect(state.context).toBeDefined();
439
+ });
440
+ });
441
+
442
+ describe('updateAgentContext', () => {
443
+ beforeEach(() => {
444
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
445
+ });
446
+
447
+ it('should update agent context', () => {
448
+ const result = updateAgentContext(
449
+ 'backend-engineer',
450
+ {
451
+ status: 'working',
452
+ currentTask: 'Implementing feature X',
453
+ currentTicket: 'PROJ-123',
454
+ },
455
+ { projectRoot: testDir }
456
+ );
457
+
458
+ expect(result.success).toBe(true);
459
+
460
+ const state = getAgentKnowledgeState('backend-engineer', { projectRoot: testDir });
461
+ expect(state.context?.status).toBe('working');
462
+ expect(state.context?.currentTask).toBe('Implementing feature X');
463
+ expect(state.context?.currentTicket).toBe('PROJ-123');
464
+ });
465
+ });
466
+
467
+ describe('getKnowledgeLibraryState', () => {
468
+ it('should return null for uninitialized library', () => {
469
+ const state = getKnowledgeLibraryState({ projectRoot: testDir });
470
+ expect(state).toBeNull();
471
+ });
472
+
473
+ it('should return state with agents', () => {
474
+ initKnowledgeLibrary({ projectRoot: testDir });
475
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
476
+ initAgentKnowledgeLibrary('frontend-engineer', { projectRoot: testDir });
477
+
478
+ const state = getKnowledgeLibraryState({ projectRoot: testDir });
479
+
480
+ expect(state).not.toBeNull();
481
+ expect(state?.agents).toContain('backend-engineer');
482
+ expect(state?.agents).toContain('frontend-engineer');
483
+ });
484
+ });
485
+
486
+ describe('formatKnowledgeLibraryState', () => {
487
+ it('should format null state', () => {
488
+ const output = formatKnowledgeLibraryState(null);
489
+ expect(output).toContain('not initialized');
490
+ });
491
+
492
+ it('should format initialized state', () => {
493
+ initKnowledgeLibrary({ projectRoot: testDir });
494
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
495
+
496
+ const state = getKnowledgeLibraryState({ projectRoot: testDir });
497
+ const output = formatKnowledgeLibraryState(state);
498
+
499
+ expect(output).toContain('KnowledgeLibrary');
500
+ expect(output).toContain('context.txt');
501
+ expect(output).toContain('backend-engineer');
502
+ });
503
+ });
504
+
505
+ describe('formatAgentKnowledgeState', () => {
506
+ it('should format agent state', () => {
507
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
508
+ const state = getAgentKnowledgeState('backend-engineer', { projectRoot: testDir });
509
+
510
+ const output = formatAgentKnowledgeState(state);
511
+
512
+ expect(output).toContain('backend-engineer');
513
+ expect(output).toContain('Initialized');
514
+ });
515
+
516
+ it('should show pending messages', () => {
517
+ initAgentKnowledgeLibrary('backend-engineer', { projectRoot: testDir });
518
+ initAgentKnowledgeLibrary('em', { projectRoot: testDir });
519
+
520
+ writeInboxMessage({
521
+ projectRoot: testDir,
522
+ type: 'task-assignment',
523
+ from: 'em',
524
+ to: 'backend-engineer',
525
+ subject: 'Test Task',
526
+ body: 'Body',
527
+ });
528
+
529
+ const state = getAgentKnowledgeState('backend-engineer', { projectRoot: testDir });
530
+ const output = formatAgentKnowledgeState(state);
531
+
532
+ expect(output).toContain('Pending Messages (1)');
533
+ expect(output).toContain('task-assignment');
534
+ });
535
+ });
536
+ });