@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,804 @@
1
+ /**
2
+ * KnowledgeLibrary Manager
3
+ *
4
+ * Manages the KnowledgeLibrary directory structure and agent state.
5
+ */
6
+
7
+ import {
8
+ existsSync,
9
+ mkdirSync,
10
+ writeFileSync,
11
+ readFileSync,
12
+ readdirSync,
13
+ renameSync,
14
+ statSync,
15
+ } from 'fs';
16
+ import { join, basename } from 'path';
17
+ import type {
18
+ AgentDirectories,
19
+ AgentKnowledgeState,
20
+ InitKnowledgeLibraryOptions,
21
+ InitKnowledgeLibraryResult,
22
+ KnowledgeLibraryState,
23
+ Message,
24
+ MessageMetadata,
25
+ ReadMessagesOptions,
26
+ WriteMessageOptions,
27
+ AgentContext,
28
+ } from './types.js';
29
+
30
+ /**
31
+ * Default KnowledgeLibrary base path
32
+ */
33
+ export const DEFAULT_KNOWLEDGE_LIBRARY_PATH = 'KnowledgeLibrary';
34
+
35
+ /**
36
+ * Standard files in a KnowledgeLibrary
37
+ */
38
+ export const STANDARD_FILES = {
39
+ context: 'context.txt',
40
+ architecture: 'architecture.txt',
41
+ prd: 'prd.txt',
42
+ } as const;
43
+
44
+ /**
45
+ * Standard directories for each agent
46
+ */
47
+ export const AGENT_DIRECTORIES = [
48
+ 'inbox',
49
+ 'inbox/processed',
50
+ 'outbox',
51
+ 'context',
52
+ 'control',
53
+ 'history',
54
+ 'tech',
55
+ ] as const;
56
+
57
+ /**
58
+ * Standard files in an agent's control directory
59
+ */
60
+ export const CONTROL_FILES = {
61
+ objectives: 'objectives.txt',
62
+ decisions: 'decisions.txt',
63
+ dependencies: 'dependencies.txt',
64
+ } as const;
65
+
66
+ /**
67
+ * Get the path to an agent's directories
68
+ */
69
+ export function getAgentDirectories(basePath: string, agentName: string): AgentDirectories {
70
+ const root = join(basePath, agentName);
71
+ return {
72
+ root,
73
+ inbox: join(root, 'inbox'),
74
+ inboxProcessed: join(root, 'inbox', 'processed'),
75
+ outbox: join(root, 'outbox'),
76
+ context: join(root, 'context'),
77
+ control: join(root, 'control'),
78
+ history: join(root, 'history'),
79
+ tech: join(root, 'tech'),
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Initialize the KnowledgeLibrary base structure
85
+ */
86
+ export function initKnowledgeLibrary(
87
+ options: InitKnowledgeLibraryOptions = {}
88
+ ): InitKnowledgeLibraryResult {
89
+ const projectRoot = options.projectRoot ?? process.cwd();
90
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
91
+ const createDefaults = options.createDefaults ?? true;
92
+
93
+ const createdDirs: string[] = [];
94
+ const createdFiles: string[] = [];
95
+
96
+ try {
97
+ // Create base directory
98
+ if (!existsSync(basePath)) {
99
+ mkdirSync(basePath, { recursive: true });
100
+ createdDirs.push(basePath);
101
+ }
102
+
103
+ // Create default global files if requested
104
+ if (createDefaults) {
105
+ // context.txt
106
+ const contextPath = join(basePath, STANDARD_FILES.context);
107
+ if (!existsSync(contextPath)) {
108
+ writeFileSync(
109
+ contextPath,
110
+ `# Project Context
111
+
112
+ ## Current Sprint
113
+ - Sprint: [Sprint name/number]
114
+ - Goals: [Sprint goals]
115
+ - Deadline: [Sprint deadline]
116
+
117
+ ## Priorities
118
+ 1. [Priority 1]
119
+ 2. [Priority 2]
120
+ 3. [Priority 3]
121
+
122
+ ## Notes
123
+ [Project-wide notes and context]
124
+ `,
125
+ 'utf-8'
126
+ );
127
+ createdFiles.push(contextPath);
128
+ }
129
+
130
+ // architecture.txt
131
+ const architecturePath = join(basePath, STANDARD_FILES.architecture);
132
+ if (!existsSync(architecturePath)) {
133
+ writeFileSync(
134
+ architecturePath,
135
+ `# Architecture Notes
136
+
137
+ ## Technical Decisions
138
+ [Record major technical decisions here]
139
+
140
+ ## System Overview
141
+ [High-level system architecture]
142
+
143
+ ## Key Components
144
+ [Important system components]
145
+ `,
146
+ 'utf-8'
147
+ );
148
+ createdFiles.push(architecturePath);
149
+ }
150
+
151
+ // prd.txt
152
+ const prdPath = join(basePath, STANDARD_FILES.prd);
153
+ if (!existsSync(prdPath)) {
154
+ writeFileSync(
155
+ prdPath,
156
+ `# Product Requirements Document
157
+
158
+ ## Overview
159
+ [Product overview]
160
+
161
+ ## Goals
162
+ [Product goals]
163
+
164
+ ## Features
165
+ [Feature list]
166
+
167
+ ## Requirements
168
+ [Detailed requirements]
169
+ `,
170
+ 'utf-8'
171
+ );
172
+ createdFiles.push(prdPath);
173
+ }
174
+ }
175
+
176
+ return {
177
+ success: true,
178
+ createdDirs,
179
+ createdFiles,
180
+ };
181
+ } catch (error) {
182
+ return {
183
+ success: false,
184
+ createdDirs,
185
+ createdFiles,
186
+ error: error instanceof Error ? error.message : String(error),
187
+ };
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Initialize an agent's KnowledgeLibrary directories
193
+ */
194
+ export function initAgentKnowledgeLibrary(
195
+ agentName: string,
196
+ options: InitKnowledgeLibraryOptions = {}
197
+ ): InitKnowledgeLibraryResult {
198
+ const projectRoot = options.projectRoot ?? process.cwd();
199
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
200
+ const createDefaults = options.createDefaults ?? true;
201
+
202
+ const createdDirs: string[] = [];
203
+ const createdFiles: string[] = [];
204
+
205
+ try {
206
+ // Ensure base KnowledgeLibrary exists
207
+ if (!existsSync(basePath)) {
208
+ const baseResult = initKnowledgeLibrary(options);
209
+ if (!baseResult.success) {
210
+ return baseResult;
211
+ }
212
+ createdDirs.push(...baseResult.createdDirs);
213
+ createdFiles.push(...baseResult.createdFiles);
214
+ }
215
+
216
+ // Get agent directories
217
+ const dirs = getAgentDirectories(basePath, agentName);
218
+
219
+ // Create all agent directories
220
+ for (const dir of AGENT_DIRECTORIES) {
221
+ const dirPath = join(dirs.root, dir);
222
+ if (!existsSync(dirPath)) {
223
+ mkdirSync(dirPath, { recursive: true });
224
+ createdDirs.push(dirPath);
225
+ }
226
+ }
227
+
228
+ // Create default files if requested
229
+ if (createDefaults) {
230
+ // context/current.txt
231
+ const currentContextPath = join(dirs.context, 'current.txt');
232
+ if (!existsSync(currentContextPath)) {
233
+ writeFileSync(
234
+ currentContextPath,
235
+ `# ${agentName} Current Context
236
+
237
+ ## Status
238
+ - State: idle
239
+ - Current Task: None
240
+ - Last Updated: ${new Date().toISOString()}
241
+
242
+ ## Notes
243
+ [Agent-specific context notes]
244
+ `,
245
+ 'utf-8'
246
+ );
247
+ createdFiles.push(currentContextPath);
248
+ }
249
+
250
+ // control/objectives.txt
251
+ const objectivesPath = join(dirs.control, CONTROL_FILES.objectives);
252
+ if (!existsSync(objectivesPath)) {
253
+ writeFileSync(
254
+ objectivesPath,
255
+ `# ${agentName} Objectives
256
+
257
+ ## Current Objectives
258
+ [None]
259
+
260
+ ## Success Criteria
261
+ [Define success criteria for objectives]
262
+ `,
263
+ 'utf-8'
264
+ );
265
+ createdFiles.push(objectivesPath);
266
+ }
267
+
268
+ // control/decisions.txt
269
+ const decisionsPath = join(dirs.control, CONTROL_FILES.decisions);
270
+ if (!existsSync(decisionsPath)) {
271
+ writeFileSync(
272
+ decisionsPath,
273
+ `# ${agentName} Decision Log
274
+
275
+ ## Decisions
276
+ [Record important decisions here]
277
+
278
+ Format:
279
+ - Date: YYYY-MM-DD
280
+ - Decision: [What was decided]
281
+ - Reasoning: [Why]
282
+ - Related: [Ticket or task reference]
283
+ `,
284
+ 'utf-8'
285
+ );
286
+ createdFiles.push(decisionsPath);
287
+ }
288
+
289
+ // control/dependencies.txt
290
+ const dependenciesPath = join(dirs.control, CONTROL_FILES.dependencies);
291
+ if (!existsSync(dependenciesPath)) {
292
+ writeFileSync(
293
+ dependenciesPath,
294
+ `# ${agentName} Dependencies
295
+
296
+ ## Blocked By
297
+ [Tasks/agents blocking this agent]
298
+
299
+ ## Blocking
300
+ [Tasks/agents this agent is blocking]
301
+
302
+ Last Updated: ${new Date().toISOString()}
303
+ `,
304
+ 'utf-8'
305
+ );
306
+ createdFiles.push(dependenciesPath);
307
+ }
308
+ }
309
+
310
+ return {
311
+ success: true,
312
+ createdDirs,
313
+ createdFiles,
314
+ };
315
+ } catch (error) {
316
+ return {
317
+ success: false,
318
+ createdDirs,
319
+ createdFiles,
320
+ error: error instanceof Error ? error.message : String(error),
321
+ };
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Check if a KnowledgeLibrary exists for an agent
327
+ */
328
+ export function agentKnowledgeLibraryExists(
329
+ agentName: string,
330
+ options: { projectRoot?: string; basePath?: string } = {}
331
+ ): boolean {
332
+ const projectRoot = options.projectRoot ?? process.cwd();
333
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
334
+ const agentRoot = join(basePath, agentName);
335
+
336
+ return existsSync(agentRoot) && existsSync(join(agentRoot, 'inbox'));
337
+ }
338
+
339
+ /**
340
+ * Get the KnowledgeLibrary state for an agent
341
+ */
342
+ export function getAgentKnowledgeState(
343
+ agentName: string,
344
+ options: { projectRoot?: string; basePath?: string } = {}
345
+ ): AgentKnowledgeState {
346
+ const projectRoot = options.projectRoot ?? process.cwd();
347
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
348
+ const dirs = getAgentDirectories(basePath, agentName);
349
+
350
+ const initialized = agentKnowledgeLibraryExists(agentName, options);
351
+
352
+ // Get pending messages
353
+ const pendingMessages = initialized ? readInboxMessages(agentName, options) : [];
354
+
355
+ // Get context
356
+ let context: AgentContext | undefined;
357
+ const currentContextPath = join(dirs.context, 'current.txt');
358
+ if (existsSync(currentContextPath)) {
359
+ const content = readFileSync(currentContextPath, 'utf-8');
360
+ context = parseContextFile(content);
361
+ }
362
+
363
+ const state: AgentKnowledgeState = {
364
+ agentName,
365
+ directories: dirs,
366
+ pendingMessages,
367
+ initialized,
368
+ };
369
+
370
+ if (context) {
371
+ state.context = context;
372
+ }
373
+
374
+ return state;
375
+ }
376
+
377
+ /**
378
+ * Parse a context file into AgentContext
379
+ */
380
+ function parseContextFile(content: string): AgentContext {
381
+ const context: AgentContext = {
382
+ lastUpdated: new Date(),
383
+ };
384
+
385
+ // Simple parsing - look for key patterns
386
+ const statusMatch = content.match(/State:\s*(.+)/i);
387
+ if (statusMatch && statusMatch[1]) {
388
+ context.status = statusMatch[1].trim();
389
+ }
390
+
391
+ const taskMatch = content.match(/Current Task:\s*(.+)/i);
392
+ if (taskMatch && taskMatch[1] && taskMatch[1].trim() !== 'None') {
393
+ context.currentTask = taskMatch[1].trim();
394
+ }
395
+
396
+ const ticketMatch = content.match(/Current Ticket:\s*([A-Z]+-\d+)/i);
397
+ if (ticketMatch && ticketMatch[1]) {
398
+ context.currentTicket = ticketMatch[1];
399
+ }
400
+
401
+ const updatedMatch = content.match(/Last Updated:\s*(.+)/i);
402
+ if (updatedMatch) {
403
+ const dateStr = updatedMatch[1]?.trim();
404
+ if (dateStr) {
405
+ const parsed = new Date(dateStr);
406
+ if (!isNaN(parsed.getTime())) {
407
+ context.lastUpdated = parsed;
408
+ }
409
+ }
410
+ }
411
+
412
+ return context;
413
+ }
414
+
415
+ /**
416
+ * Generate a message filename
417
+ */
418
+ export function generateMessageFilename(from: string, subject: string): string {
419
+ const now = new Date();
420
+ const timestamp = now.toISOString().replace(/[-:T]/g, '').slice(0, 12); // YYYYMMDDHHMM
421
+ const sanitizedSubject = subject
422
+ .toLowerCase()
423
+ .replace(/[^a-z0-9]+/g, '-')
424
+ .slice(0, 30);
425
+ return `${timestamp}-${from}-${sanitizedSubject}.md`;
426
+ }
427
+
428
+ /**
429
+ * Parse message frontmatter
430
+ */
431
+ function parseMessageFrontmatter(content: string): {
432
+ metadata: Partial<MessageMetadata>;
433
+ body: string;
434
+ } {
435
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
436
+
437
+ if (!match) {
438
+ return { metadata: {}, body: content };
439
+ }
440
+
441
+ const frontmatter = match[1] ?? '';
442
+ const body = match[2] ?? '';
443
+ const metadata: Partial<MessageMetadata> = {};
444
+
445
+ for (const line of frontmatter.split(/\r?\n/)) {
446
+ const colonIndex = line.indexOf(':');
447
+ if (colonIndex === -1) continue;
448
+
449
+ const key = line.slice(0, colonIndex).trim();
450
+ const value = line.slice(colonIndex + 1).trim();
451
+
452
+ switch (key) {
453
+ case 'type':
454
+ metadata.type = value as MessageMetadata['type'];
455
+ break;
456
+ case 'from':
457
+ metadata.from = value;
458
+ break;
459
+ case 'to':
460
+ metadata.to = value;
461
+ break;
462
+ case 'date':
463
+ metadata.date = new Date(value);
464
+ break;
465
+ case 'ticket':
466
+ metadata.ticket = value;
467
+ break;
468
+ case 'priority':
469
+ if (value === 'P0' || value === 'P1' || value === 'P2' || value === 'P3') {
470
+ metadata.priority = value;
471
+ }
472
+ break;
473
+ case 'subject':
474
+ metadata.subject = value;
475
+ break;
476
+ }
477
+ }
478
+
479
+ return { metadata, body };
480
+ }
481
+
482
+ /**
483
+ * Read inbox messages for an agent
484
+ */
485
+ export function readInboxMessages(
486
+ agentName: string,
487
+ options: { projectRoot?: string; basePath?: string } & ReadMessagesOptions = {}
488
+ ): Message[] {
489
+ const projectRoot = options.projectRoot ?? process.cwd();
490
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
491
+ const dirs = getAgentDirectories(basePath, agentName);
492
+
493
+ const messages: Message[] = [];
494
+
495
+ // Read inbox
496
+ if (existsSync(dirs.inbox)) {
497
+ const files = readdirSync(dirs.inbox).filter(
498
+ (f) => f.endsWith('.md') && statSync(join(dirs.inbox, f)).isFile()
499
+ );
500
+
501
+ for (const file of files) {
502
+ const filePath = join(dirs.inbox, file);
503
+ const rawContent = readFileSync(filePath, 'utf-8');
504
+ const { metadata, body } = parseMessageFrontmatter(rawContent);
505
+
506
+ // Apply filters
507
+ if (options.type && metadata.type !== options.type) continue;
508
+ if (options.from && metadata.from !== options.from) continue;
509
+ if (options.priority && metadata.priority !== options.priority) continue;
510
+
511
+ messages.push({
512
+ filename: file,
513
+ path: filePath,
514
+ metadata: metadata as MessageMetadata,
515
+ rawContent,
516
+ body,
517
+ });
518
+ }
519
+ }
520
+
521
+ // Include processed if requested
522
+ if (options.includeProcessed && existsSync(dirs.inboxProcessed)) {
523
+ const files = readdirSync(dirs.inboxProcessed).filter(
524
+ (f) => f.endsWith('.md') && statSync(join(dirs.inboxProcessed, f)).isFile()
525
+ );
526
+
527
+ for (const file of files) {
528
+ const filePath = join(dirs.inboxProcessed, file);
529
+ const rawContent = readFileSync(filePath, 'utf-8');
530
+ const { metadata, body } = parseMessageFrontmatter(rawContent);
531
+
532
+ // Apply filters
533
+ if (options.type && metadata.type !== options.type) continue;
534
+ if (options.from && metadata.from !== options.from) continue;
535
+ if (options.priority && metadata.priority !== options.priority) continue;
536
+
537
+ messages.push({
538
+ filename: file,
539
+ path: filePath,
540
+ metadata: metadata as MessageMetadata,
541
+ rawContent,
542
+ body,
543
+ });
544
+ }
545
+ }
546
+
547
+ // Sort by date (newest first)
548
+ messages.sort((a, b) => {
549
+ const dateA = a.metadata.date?.getTime() ?? 0;
550
+ const dateB = b.metadata.date?.getTime() ?? 0;
551
+ return dateB - dateA;
552
+ });
553
+
554
+ // Apply limit
555
+ if (options.limit && messages.length > options.limit) {
556
+ return messages.slice(0, options.limit);
557
+ }
558
+
559
+ return messages;
560
+ }
561
+
562
+ /**
563
+ * Write a message to an agent's inbox
564
+ */
565
+ export function writeInboxMessage(
566
+ options: WriteMessageOptions & { projectRoot?: string; basePath?: string }
567
+ ): { success: boolean; path?: string; error?: string } {
568
+ const projectRoot = options.projectRoot ?? process.cwd();
569
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
570
+ const dirs = getAgentDirectories(basePath, options.to);
571
+
572
+ try {
573
+ // Ensure agent inbox exists
574
+ if (!existsSync(dirs.inbox)) {
575
+ mkdirSync(dirs.inbox, { recursive: true });
576
+ }
577
+
578
+ // Generate filename
579
+ const filename = generateMessageFilename(options.from, options.subject);
580
+ const filePath = join(dirs.inbox, filename);
581
+
582
+ // Format message
583
+ const now = new Date();
584
+ const content = formatMessage(options, now);
585
+
586
+ // Write file
587
+ writeFileSync(filePath, content, 'utf-8');
588
+
589
+ // Also copy to sender's outbox
590
+ const senderDirs = getAgentDirectories(basePath, options.from);
591
+ if (existsSync(senderDirs.outbox)) {
592
+ writeFileSync(join(senderDirs.outbox, filename), content, 'utf-8');
593
+ }
594
+
595
+ return { success: true, path: filePath };
596
+ } catch (error) {
597
+ return {
598
+ success: false,
599
+ error: error instanceof Error ? error.message : String(error),
600
+ };
601
+ }
602
+ }
603
+
604
+ /**
605
+ * Format a message with frontmatter
606
+ */
607
+ function formatMessage(options: WriteMessageOptions, date: Date): string {
608
+ let frontmatter = `---
609
+ type: ${options.type}
610
+ from: ${options.from}
611
+ to: ${options.to}
612
+ date: ${date.toISOString().slice(0, 16).replace('T', ' ')}`;
613
+
614
+ if (options.ticket) {
615
+ frontmatter += `\nticket: ${options.ticket}`;
616
+ }
617
+
618
+ if (options.priority) {
619
+ frontmatter += `\npriority: ${options.priority}`;
620
+ }
621
+
622
+ frontmatter += `\nsubject: ${options.subject}`;
623
+
624
+ frontmatter += `\n---
625
+
626
+ ## ${options.subject}
627
+
628
+ ${options.body}`;
629
+
630
+ return frontmatter;
631
+ }
632
+
633
+ /**
634
+ * Mark a message as processed (move to processed folder)
635
+ */
636
+ export function markMessageProcessed(
637
+ agentName: string,
638
+ messageFilename: string,
639
+ options: { projectRoot?: string; basePath?: string } = {}
640
+ ): { success: boolean; error?: string } {
641
+ const projectRoot = options.projectRoot ?? process.cwd();
642
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
643
+ const dirs = getAgentDirectories(basePath, agentName);
644
+
645
+ const sourcePath = join(dirs.inbox, messageFilename);
646
+ const destPath = join(dirs.inboxProcessed, messageFilename);
647
+
648
+ try {
649
+ if (!existsSync(sourcePath)) {
650
+ return { success: false, error: `Message not found: ${messageFilename}` };
651
+ }
652
+
653
+ if (!existsSync(dirs.inboxProcessed)) {
654
+ mkdirSync(dirs.inboxProcessed, { recursive: true });
655
+ }
656
+
657
+ renameSync(sourcePath, destPath);
658
+ return { success: true };
659
+ } catch (error) {
660
+ return {
661
+ success: false,
662
+ error: error instanceof Error ? error.message : String(error),
663
+ };
664
+ }
665
+ }
666
+
667
+ /**
668
+ * Get global KnowledgeLibrary state
669
+ */
670
+ export function getKnowledgeLibraryState(
671
+ options: { projectRoot?: string; basePath?: string } = {}
672
+ ): KnowledgeLibraryState | null {
673
+ const projectRoot = options.projectRoot ?? process.cwd();
674
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
675
+
676
+ if (!existsSync(basePath)) {
677
+ return null;
678
+ }
679
+
680
+ // Get list of agents
681
+ const agents: string[] = [];
682
+ const entries = readdirSync(basePath);
683
+ for (const entry of entries) {
684
+ const entryPath = join(basePath, entry);
685
+ if (statSync(entryPath).isDirectory() && existsSync(join(entryPath, 'inbox'))) {
686
+ agents.push(entry);
687
+ }
688
+ }
689
+
690
+ return {
691
+ basePath,
692
+ contextPath: join(basePath, STANDARD_FILES.context),
693
+ architecturePath: join(basePath, STANDARD_FILES.architecture),
694
+ prdPath: join(basePath, STANDARD_FILES.prd),
695
+ agents,
696
+ };
697
+ }
698
+
699
+ /**
700
+ * Update agent context
701
+ */
702
+ export function updateAgentContext(
703
+ agentName: string,
704
+ context: Partial<AgentContext>,
705
+ options: { projectRoot?: string; basePath?: string } = {}
706
+ ): { success: boolean; error?: string } {
707
+ const projectRoot = options.projectRoot ?? process.cwd();
708
+ const basePath = join(projectRoot, options.basePath ?? DEFAULT_KNOWLEDGE_LIBRARY_PATH);
709
+ const dirs = getAgentDirectories(basePath, agentName);
710
+ const contextPath = join(dirs.context, 'current.txt');
711
+
712
+ try {
713
+ const now = new Date();
714
+ const content = `# ${agentName} Current Context
715
+
716
+ ## Status
717
+ - State: ${context.status ?? 'idle'}
718
+ - Current Task: ${context.currentTask ?? 'None'}
719
+ ${context.currentTicket ? `- Current Ticket: ${context.currentTicket}` : ''}- Last Updated: ${now.toISOString()}
720
+
721
+ ## Notes
722
+ ${context.notes ?? '[Agent-specific context notes]'}
723
+ `;
724
+
725
+ if (!existsSync(dirs.context)) {
726
+ mkdirSync(dirs.context, { recursive: true });
727
+ }
728
+
729
+ writeFileSync(contextPath, content, 'utf-8');
730
+ return { success: true };
731
+ } catch (error) {
732
+ return {
733
+ success: false,
734
+ error: error instanceof Error ? error.message : String(error),
735
+ };
736
+ }
737
+ }
738
+
739
+ /**
740
+ * Format KnowledgeLibrary state for display
741
+ */
742
+ export function formatKnowledgeLibraryState(state: KnowledgeLibraryState | null): string {
743
+ if (!state) {
744
+ return 'KnowledgeLibrary not initialized.';
745
+ }
746
+
747
+ const lines: string[] = [];
748
+ lines.push(`KnowledgeLibrary: ${state.basePath}\n`);
749
+
750
+ lines.push('Global files:');
751
+ lines.push(` - ${basename(state.contextPath)}`);
752
+ lines.push(` - ${basename(state.architecturePath)}`);
753
+ lines.push(` - ${basename(state.prdPath)}`);
754
+ lines.push('');
755
+
756
+ if (state.agents.length > 0) {
757
+ lines.push(`Agents (${state.agents.length}):`);
758
+ for (const agent of state.agents) {
759
+ lines.push(` - ${agent}`);
760
+ }
761
+ } else {
762
+ lines.push('No agents initialized.');
763
+ }
764
+
765
+ return lines.join('\n');
766
+ }
767
+
768
+ /**
769
+ * Format agent knowledge state for display
770
+ */
771
+ export function formatAgentKnowledgeState(state: AgentKnowledgeState): string {
772
+ const lines: string[] = [];
773
+
774
+ lines.push(`Agent: ${state.agentName}`);
775
+ lines.push(`Status: ${state.initialized ? 'Initialized' : 'Not initialized'}`);
776
+ lines.push('');
777
+
778
+ if (state.context) {
779
+ lines.push('Context:');
780
+ lines.push(` State: ${state.context.status ?? 'unknown'}`);
781
+ if (state.context.currentTask) {
782
+ lines.push(` Current Task: ${state.context.currentTask}`);
783
+ }
784
+ if (state.context.currentTicket) {
785
+ lines.push(` Current Ticket: ${state.context.currentTicket}`);
786
+ }
787
+ lines.push(` Last Updated: ${state.context.lastUpdated.toISOString()}`);
788
+ lines.push('');
789
+ }
790
+
791
+ if (state.pendingMessages.length > 0) {
792
+ lines.push(`Pending Messages (${state.pendingMessages.length}):`);
793
+ for (const msg of state.pendingMessages) {
794
+ const type = msg.metadata.type ?? 'unknown';
795
+ const from = msg.metadata.from ?? 'unknown';
796
+ const subject = msg.metadata.subject ?? msg.filename;
797
+ lines.push(` - [${type}] from ${from}: ${subject}`);
798
+ }
799
+ } else {
800
+ lines.push('No pending messages.');
801
+ }
802
+
803
+ return lines.join('\n');
804
+ }