pi-harness-runtime 1.1.95 → 1.1.97

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 (226) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/harness/loop-runtime.ts +3 -0
  3. package/index.ts +130 -29
  4. package/package.json +2 -1
  5. package/packages/a2a-adapter/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/package.json +1 -1
  16. package/packages/code-generation/package.json +1 -1
  17. package/packages/code-review/package.json +1 -1
  18. package/packages/codex-adapter/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/context-compiler/package.json +1 -1
  21. package/packages/context-discovery/package.json +1 -1
  22. package/packages/context-manager/package.json +1 -1
  23. package/packages/cookie-sanitizer/package.json +1 -1
  24. package/packages/cost-optimizer/package.json +1 -1
  25. package/packages/dependency-analyzer/package.json +1 -1
  26. package/packages/django-plugin/package.json +1 -1
  27. package/packages/doc-generator/package.json +1 -1
  28. package/packages/evaluation-engine/package.json +1 -1
  29. package/packages/evaluation-runner/package.json +1 -1
  30. package/packages/event-bus/package.json +1 -1
  31. package/packages/event-store/package.json +1 -1
  32. package/packages/experience-replay/package.json +1 -1
  33. package/packages/feedback-collector/package.json +1 -1
  34. package/packages/file-copy-helper/package.json +1 -1
  35. package/packages/framework-detector/package.json +1 -1
  36. package/packages/framework-plugin-sdk/package.json +1 -1
  37. package/packages/frappe-plugin/package.json +1 -1
  38. package/packages/generic-web-plugin/package.json +1 -1
  39. package/packages/health-monitor/package.json +1 -1
  40. package/packages/intent-analyzer/package.json +1 -1
  41. package/packages/jev-judge/package.json +1 -1
  42. package/packages/knowledge-graph/package.json +1 -1
  43. package/packages/knowledge-retrieval/package.json +1 -1
  44. package/packages/laravel-plugin/package.json +1 -1
  45. package/packages/learning-engine/package.json +1 -1
  46. package/packages/mcp-adapter/package.json +1 -1
  47. package/packages/memory-engine/package.json +1 -1
  48. package/packages/milestone-manager/package.json +1 -1
  49. package/packages/model-registry/package.json +1 -1
  50. package/packages/nextjs-plugin/package.json +1 -1
  51. package/packages/notification/package.json +1 -1
  52. package/packages/observability/package.json +1 -1
  53. package/packages/okf-indexer/package.json +1 -1
  54. package/packages/performance-optimizer/package.json +1 -1
  55. package/packages/privilege-broker/package.json +1 -1
  56. package/packages/project-analyzer/package.json +1 -1
  57. package/packages/project-bootstrap/package.json +1 -1
  58. package/packages/projection-engine/package.json +1 -1
  59. package/packages/prompt-compiler/package.json +1 -1
  60. package/packages/prompt-versioning/package.json +1 -1
  61. package/packages/provider-adapter-sdk/package.json +1 -1
  62. package/packages/provider-router/package.json +1 -1
  63. package/packages/provider-selector/package.json +1 -1
  64. package/packages/providers/package.json +1 -1
  65. package/packages/qdrant-skills/dist/client.d.ts +51 -0
  66. package/packages/qdrant-skills/dist/client.d.ts.map +1 -0
  67. package/packages/qdrant-skills/dist/client.js +105 -0
  68. package/packages/qdrant-skills/dist/client.js.map +1 -0
  69. package/packages/qdrant-skills/dist/embedder.d.ts +39 -0
  70. package/packages/qdrant-skills/dist/embedder.d.ts.map +1 -0
  71. package/packages/qdrant-skills/dist/embedder.js +101 -0
  72. package/packages/qdrant-skills/dist/embedder.js.map +1 -0
  73. package/packages/qdrant-skills/dist/index.d.ts +25 -0
  74. package/packages/qdrant-skills/dist/index.d.ts.map +1 -0
  75. package/packages/qdrant-skills/dist/index.js +25 -0
  76. package/packages/qdrant-skills/dist/index.js.map +1 -0
  77. package/packages/qdrant-skills/package.json +27 -0
  78. package/packages/qdrant-skills/src/client.ts +178 -0
  79. package/packages/qdrant-skills/src/embedder.ts +137 -0
  80. package/packages/qdrant-skills/src/index.ts +42 -0
  81. package/packages/quota-manager/package.json +1 -1
  82. package/packages/rate-limiter/package.json +1 -1
  83. package/packages/react-vite-plugin/package.json +1 -1
  84. package/packages/release-manager/package.json +1 -1
  85. package/packages/requirement-compiler/package.json +1 -1
  86. package/packages/runtime/package.json +1 -1
  87. package/packages/scheduler/package.json +1 -1
  88. package/packages/scheduler-adapter/package.json +1 -1
  89. package/packages/session/package.json +1 -1
  90. package/packages/session-api/package.json +1 -1
  91. package/packages/session-export/package.json +1 -1
  92. package/packages/shared-context/package.json +1 -1
  93. package/packages/skill-mcp-client/package.json +1 -1
  94. package/packages/skill-registry/package.json +1 -1
  95. package/packages/skills/dist/index.d.ts +40 -0
  96. package/packages/skills/dist/index.d.ts.map +1 -0
  97. package/packages/skills/dist/index.js +84 -0
  98. package/packages/skills/dist/index.js.map +1 -0
  99. package/packages/skills/dist/linter.d.ts +38 -0
  100. package/packages/skills/dist/linter.d.ts.map +1 -0
  101. package/packages/skills/dist/linter.js +323 -0
  102. package/packages/skills/dist/linter.js.map +1 -0
  103. package/packages/skills/dist/loader.d.ts +74 -0
  104. package/packages/skills/dist/loader.d.ts.map +1 -0
  105. package/packages/skills/dist/loader.js +280 -0
  106. package/packages/skills/dist/loader.js.map +1 -0
  107. package/packages/skills/dist/parser.d.ts +32 -0
  108. package/packages/skills/dist/parser.d.ts.map +1 -0
  109. package/packages/skills/dist/parser.js +459 -0
  110. package/packages/skills/dist/parser.js.map +1 -0
  111. package/packages/skills/dist/scanner.d.ts +33 -0
  112. package/packages/skills/dist/scanner.d.ts.map +1 -0
  113. package/packages/skills/dist/scanner.js +257 -0
  114. package/packages/skills/dist/scanner.js.map +1 -0
  115. package/packages/skills/dist/skill-commands.d.ts +33 -0
  116. package/packages/skills/dist/skill-commands.d.ts.map +1 -0
  117. package/packages/skills/dist/skill-commands.js +239 -0
  118. package/packages/skills/dist/skill-commands.js.map +1 -0
  119. package/packages/skills/dist/skill-tool.d.ts +120 -0
  120. package/packages/skills/dist/skill-tool.d.ts.map +1 -0
  121. package/packages/skills/dist/skill-tool.js +239 -0
  122. package/packages/skills/dist/skill-tool.js.map +1 -0
  123. package/packages/skills/dist/skill-versioning.d.ts +123 -0
  124. package/packages/skills/dist/skill-versioning.d.ts.map +1 -0
  125. package/packages/skills/dist/skill-versioning.js +240 -0
  126. package/packages/skills/dist/skill-versioning.js.map +1 -0
  127. package/packages/skills/dist/types.d.ts +226 -0
  128. package/packages/skills/dist/types.d.ts.map +1 -0
  129. package/packages/skills/dist/types.js +21 -0
  130. package/packages/skills/dist/types.js.map +1 -0
  131. package/packages/skills/package.json +46 -0
  132. package/packages/skills/src/index.ts +95 -0
  133. package/packages/skills/src/linter.ts +371 -0
  134. package/packages/skills/src/loader.ts +335 -0
  135. package/packages/skills/src/parser.ts +548 -0
  136. package/packages/skills/src/scanner.ts +322 -0
  137. package/packages/skills/src/skill-commands.ts +318 -0
  138. package/packages/skills/src/skill-tool.ts +324 -0
  139. package/packages/skills/src/skill-versioning.ts +345 -0
  140. package/packages/skills/src/types.ts +259 -0
  141. package/packages/skills/test/init-skills.test.ts +118 -0
  142. package/packages/sprint-planner/package.json +1 -1
  143. package/packages/subscription-engine/package.json +1 -1
  144. package/packages/task-compiler/package.json +1 -1
  145. package/packages/tencentdb-memory/package.json +1 -1
  146. package/packages/tencentdb-sync/package.json +1 -1
  147. package/packages/test-data-generator/package.json +1 -1
  148. package/packages/test-generator/package.json +1 -1
  149. package/packages/todo-bd-sync/package.json +1 -1
  150. package/packages/token-estimation/package.json +1 -1
  151. package/packages/token-optimizer/package.json +1 -1
  152. package/packages/trajectory/dist/src/curator.d.ts +189 -0
  153. package/packages/trajectory/dist/src/curator.d.ts.map +1 -0
  154. package/packages/trajectory/dist/src/curator.js +340 -0
  155. package/packages/trajectory/dist/src/curator.js.map +1 -0
  156. package/packages/trajectory/dist/src/journey-renderer.d.ts +53 -0
  157. package/packages/trajectory/dist/src/journey-renderer.d.ts.map +1 -0
  158. package/packages/trajectory/dist/src/journey-renderer.js +324 -0
  159. package/packages/trajectory/dist/src/journey-renderer.js.map +1 -0
  160. package/packages/trajectory/dist/src/learning-graph.d.ts +159 -0
  161. package/packages/trajectory/dist/src/learning-graph.d.ts.map +1 -0
  162. package/packages/trajectory/dist/src/learning-graph.js +258 -0
  163. package/packages/trajectory/dist/src/learning-graph.js.map +1 -0
  164. package/packages/trajectory/dist/src/memory-store.d.ts +146 -0
  165. package/packages/trajectory/dist/src/memory-store.d.ts.map +1 -0
  166. package/packages/trajectory/dist/src/memory-store.js +472 -0
  167. package/packages/trajectory/dist/src/memory-store.js.map +1 -0
  168. package/packages/trajectory/dist/src/review-fork.d.ts +164 -0
  169. package/packages/trajectory/dist/src/review-fork.d.ts.map +1 -0
  170. package/packages/trajectory/dist/src/review-fork.js +332 -0
  171. package/packages/trajectory/dist/src/review-fork.js.map +1 -0
  172. package/packages/trajectory/dist/src/write-approval.d.ts +136 -0
  173. package/packages/trajectory/dist/src/write-approval.d.ts.map +1 -0
  174. package/packages/trajectory/dist/src/write-approval.js +281 -0
  175. package/packages/trajectory/dist/src/write-approval.js.map +1 -0
  176. package/packages/trajectory/package.json +1 -1
  177. package/packages/trajectory/src/curator.ts +485 -0
  178. package/packages/trajectory/src/journey-renderer.ts +399 -0
  179. package/packages/trajectory/src/learning-graph.ts +363 -0
  180. package/packages/trajectory/src/memory-store.ts +578 -0
  181. package/packages/trajectory/src/review-fork.ts +450 -0
  182. package/packages/trajectory/src/write-approval.ts +372 -0
  183. package/packages/tui/package.json +1 -1
  184. package/packages/types/package.json +1 -1
  185. package/packages/workflow-events/package.json +1 -1
  186. package/packages/workspace-scanner/package.json +1 -1
  187. package/packages/worktree/package.json +1 -1
  188. package/packages/write-review/dist/blackboard.d.ts +98 -0
  189. package/packages/write-review/dist/blackboard.d.ts.map +1 -0
  190. package/packages/write-review/dist/blackboard.js +289 -0
  191. package/packages/write-review/dist/blackboard.js.map +1 -0
  192. package/packages/write-review/dist/event-bus.d.ts +106 -0
  193. package/packages/write-review/dist/event-bus.d.ts.map +1 -0
  194. package/packages/write-review/dist/event-bus.js +294 -0
  195. package/packages/write-review/dist/event-bus.js.map +1 -0
  196. package/packages/write-review/dist/gate.d.ts +37 -0
  197. package/packages/write-review/dist/gate.d.ts.map +1 -0
  198. package/packages/write-review/dist/gate.js +138 -0
  199. package/packages/write-review/dist/gate.js.map +1 -0
  200. package/packages/write-review/dist/index.d.ts +22 -0
  201. package/packages/write-review/dist/index.d.ts.map +1 -0
  202. package/packages/write-review/dist/index.js +60 -0
  203. package/packages/write-review/dist/index.js.map +1 -0
  204. package/packages/write-review/dist/injection.d.ts +40 -0
  205. package/packages/write-review/dist/injection.d.ts.map +1 -0
  206. package/packages/write-review/dist/injection.js +179 -0
  207. package/packages/write-review/dist/injection.js.map +1 -0
  208. package/packages/write-review/dist/review.d.ts +54 -0
  209. package/packages/write-review/dist/review.d.ts.map +1 -0
  210. package/packages/write-review/dist/review.js +154 -0
  211. package/packages/write-review/dist/review.js.map +1 -0
  212. package/packages/write-review/dist/trigger.d.ts +39 -0
  213. package/packages/write-review/dist/trigger.d.ts.map +1 -0
  214. package/packages/write-review/dist/trigger.js +107 -0
  215. package/packages/write-review/dist/trigger.js.map +1 -0
  216. package/packages/write-review/dist/types.d.ts +78 -0
  217. package/packages/write-review/dist/types.d.ts.map +1 -0
  218. package/packages/write-review/dist/types.js +26 -0
  219. package/packages/write-review/dist/types.js.map +1 -0
  220. package/packages/write-review/package.json +1 -1
  221. package/packages/write-review/src/event-bus.ts +351 -0
  222. package/packages/write-review/src/index.ts +1 -0
  223. package/packages/write-review/src/injection.ts +25 -3
  224. package/proactive-compact.ts +51 -7
  225. package/scripts/sync-skills.ts +275 -0
  226. package/scripts/sync-to-qdrant.ts +398 -0
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Curator - Consolidation and Quality Enforcement
3
+ *
4
+ * Periodic pass that:
5
+ * - Merges fragmented memory into skills
6
+ * - Extracts common patterns into skills
7
+ * - Prunes outdated items
8
+ * - Enforces quality standards
9
+ *
10
+ * Based on Hermes Agent's curator system.
11
+ */
12
+
13
+ import type { Trajectory } from "./types.js";
14
+ import type { MemoryEntry } from "./memory-store.js";
15
+
16
+ /**
17
+ * Local Skill type to avoid circular dependency
18
+ */
19
+ interface LocalSkill {
20
+ id: string;
21
+ frontmatter: { name: string; description: string };
22
+ hermes?: {
23
+ author?: string;
24
+ created_at?: string;
25
+ usage_count?: number;
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Local lint result type
31
+ */
32
+ interface LocalLintResult {
33
+ rule: string;
34
+ severity: "error" | "warning" | "info";
35
+ message: string;
36
+ }
37
+
38
+ /**
39
+ * Curator configuration
40
+ */
41
+ export interface CuratorConfig {
42
+ /** Enable curator */
43
+ enabled: boolean;
44
+ /** Days between consolidation passes */
45
+ consolidationIntervalDays: number;
46
+ /** Max memory entries before consolidation */
47
+ memoryThreshold: number;
48
+ /** Min similar entries to trigger merge */
49
+ similarityThreshold: number;
50
+ /** Archive skills older than this */
51
+ archiveAgeDays: number;
52
+ }
53
+
54
+ /**
55
+ * Default curator configuration
56
+ */
57
+ export const DEFAULT_CURATOR_CONFIG: CuratorConfig = {
58
+ enabled: true,
59
+ consolidationIntervalDays: 7,
60
+ memoryThreshold: 50,
61
+ similarityThreshold: 3,
62
+ archiveAgeDays: 90,
63
+ };
64
+
65
+ /**
66
+ * Curator action types
67
+ */
68
+ export type CuratorAction =
69
+ | { type: "merge_memory"; entries: MemoryEntry[]; into: string }
70
+ | { type: "extract_skill"; pattern: string; from: string[] }
71
+ | { type: "archive_memory"; id: string }
72
+ | { type: "archive_skill"; id: string }
73
+ | { type: "patch_skill"; id: string; patch: Partial<LocalSkill> }
74
+ | { type: "prune_low_confidence"; items: { id: string; type: "memory" | "skill"; confidence: number }[] };
75
+
76
+ /**
77
+ * Curator event types
78
+ */
79
+ export type CuratorEvent =
80
+ | { type: "curator.started" }
81
+ | { type: "curator.completed"; actions: CuratorAction[] }
82
+ | { type: "curator.action"; action: CuratorAction }
83
+ | { type: "curator.error"; error: string };
84
+
85
+ /**
86
+ * Curator event handler
87
+ */
88
+ export type CuratorEventHandler = (event: CuratorEvent) => void;
89
+
90
+ /**
91
+ * Similarity result
92
+ */
93
+ export interface SimilarityResult {
94
+ entries: MemoryEntry[];
95
+ similarity: number; // 0-1
96
+ commonTheme: string;
97
+ }
98
+
99
+ /**
100
+ * Curator class
101
+ */
102
+ export class Curator {
103
+ private config: CuratorConfig;
104
+ private handlers: Set<CuratorEventHandler> = new Set();
105
+ private lastConsolidation: number = Date.now();
106
+
107
+ constructor(config?: Partial<CuratorConfig>) {
108
+ this.config = { ...DEFAULT_CURATOR_CONFIG, ...config };
109
+ }
110
+
111
+ /**
112
+ * Register event handler
113
+ */
114
+ on(handler: CuratorEventHandler): () => void {
115
+ this.handlers.add(handler);
116
+ return () => this.handlers.delete(handler);
117
+ }
118
+
119
+ /**
120
+ * Emit event
121
+ */
122
+ private emit(event: CuratorEvent): void {
123
+ for (const handler of this.handlers) {
124
+ try {
125
+ handler(event);
126
+ } catch (err) {
127
+ console.error("[Curator] Handler error:", err);
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Check if consolidation should run
134
+ */
135
+ shouldConsolidate(): boolean {
136
+ if (!this.config.enabled) return false;
137
+
138
+ const daysSinceConsolidation =
139
+ (Date.now() - this.lastConsolidation) / (1000 * 60 * 60 * 24);
140
+
141
+ return daysSinceConsolidation >= this.config.consolidationIntervalDays;
142
+ }
143
+
144
+ /**
145
+ * Run consolidation pass
146
+ */
147
+ async consolidate(
148
+ memory: MemoryEntry[],
149
+ skills: LocalSkill[],
150
+ trajectories: Trajectory[]
151
+ ): Promise<CuratorAction[]> {
152
+ this.emit({ type: "curator.started" });
153
+ const actions: CuratorAction[] = [];
154
+
155
+ try {
156
+ // 1. Find similar memory entries
157
+ const similarGroups = this.findSimilarMemory(memory);
158
+ for (const group of similarGroups) {
159
+ if (group.entries.length >= this.config.similarityThreshold) {
160
+ const action = this.suggestMerge(group);
161
+ if (action) {
162
+ actions.push(action);
163
+ this.emit({ type: "curator.action", action });
164
+ }
165
+ }
166
+ }
167
+
168
+ // 2. Extract patterns from trajectories
169
+ const patterns = this.extractPatterns(trajectories);
170
+ for (const pattern of patterns) {
171
+ const action = this.suggestSkillExtraction(pattern);
172
+ if (action) {
173
+ actions.push(action);
174
+ this.emit({ type: "curator.action", action });
175
+ }
176
+ }
177
+
178
+ // 3. Check for outdated items
179
+ const outdated = this.findOutdatedItems(memory, skills);
180
+ for (const item of outdated) {
181
+ const action = this.suggestArchive(item);
182
+ if (action) {
183
+ actions.push(action);
184
+ this.emit({ type: "curator.action", action });
185
+ }
186
+ }
187
+
188
+ // 4. Lint skills for quality
189
+ for (const skill of skills) {
190
+ const results: LocalLintResult[] = []; // Linting would require skills package
191
+ const errors: LocalLintResult[] = results.filter((r) => r.severity === "error");
192
+ if (errors.length > 0) {
193
+ // Suggest patch to fix errors
194
+ const action = this.suggestSkillPatch(skill, errors);
195
+ if (action) {
196
+ actions.push(action);
197
+ this.emit({ type: "curator.action", action });
198
+ }
199
+ }
200
+ }
201
+
202
+ // Update last consolidation time
203
+ this.lastConsolidation = Date.now();
204
+
205
+ this.emit({ type: "curator.completed", actions });
206
+ } catch (err) {
207
+ const errorMsg = err instanceof Error ? err.message : String(err);
208
+ this.emit({ type: "curator.error", error: errorMsg });
209
+ }
210
+
211
+ return actions;
212
+ }
213
+
214
+ /**
215
+ * Find similar memory entries
216
+ */
217
+ findSimilarMemory(memory: MemoryEntry[]): SimilarityResult[] {
218
+ const groups: Map<string, MemoryEntry[]> = new Map();
219
+
220
+ for (const entry of memory) {
221
+ // Extract key terms from content
222
+ const terms = this.extractTerms(entry.content);
223
+
224
+ for (const term of terms) {
225
+ const key = term.toLowerCase();
226
+ if (!groups.has(key)) {
227
+ groups.set(key, []);
228
+ }
229
+ groups.get(key)!.push(entry);
230
+ }
231
+ }
232
+
233
+ // Filter to groups with multiple entries
234
+ const results: SimilarityResult[] = [];
235
+ for (const [theme, entries] of groups) {
236
+ // Deduplicate entries
237
+ const unique = Array.from(new Set(entries.map((e) => e.id)))
238
+ .map((id) => entries.find((e) => e.id === id)!);
239
+
240
+ if (unique.length >= 2) {
241
+ results.push({
242
+ entries: unique,
243
+ similarity: Math.min(1, unique.length / 10),
244
+ commonTheme: theme,
245
+ });
246
+ }
247
+ }
248
+
249
+ // Sort by similarity descending
250
+ results.sort((a, b) => b.similarity - a.similarity);
251
+
252
+ return results;
253
+ }
254
+
255
+ /**
256
+ * Extract key terms from content
257
+ */
258
+ private extractTerms(content: string): string[] {
259
+ // Simple term extraction
260
+ const words = content
261
+ .toLowerCase()
262
+ .replace(/[^\w\s]/g, " ")
263
+ .split(/\s+/)
264
+ .filter((w) => w.length > 4);
265
+
266
+ // Get most common words
267
+ const freq: Record<string, number> = {};
268
+ for (const word of words) {
269
+ freq[word] = (freq[word] || 0) + 1;
270
+ }
271
+
272
+ // Return top terms
273
+ return Object.entries(freq)
274
+ .sort((a, b) => b[1] - a[1])
275
+ .slice(0, 5)
276
+ .map(([word]) => word);
277
+ }
278
+
279
+ /**
280
+ * Suggest memory merge
281
+ */
282
+ private suggestMerge(group: SimilarityResult): CuratorAction | null {
283
+ // Find the most recent/relevant entry to keep
284
+ const keeper = group.entries.reduce((a, b) =>
285
+ a.updated_at > b.updated_at ? a : b
286
+ );
287
+
288
+ return {
289
+ type: "merge_memory",
290
+ entries: group.entries.filter((e) => e.id !== keeper.id),
291
+ into: keeper.id,
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Extract patterns from trajectories
297
+ */
298
+ extractPatterns(trajectories: Trajectory[]): string[] {
299
+ const patterns: string[] = [];
300
+
301
+ // Group by verdict
302
+ const byVerdict: Record<string, Trajectory[]> = {};
303
+ for (const t of trajectories) {
304
+ if (!byVerdict[t.verdict]) {
305
+ byVerdict[t.verdict] = [];
306
+ }
307
+ byVerdict[t.verdict].push(t);
308
+ }
309
+
310
+ // Find converged patterns (approved with low iterations)
311
+ const fastConverged = trajectories.filter(
312
+ (t) => t.verdict === "approved" && t.iterations <= 2
313
+ );
314
+
315
+ for (const t of fastConverged) {
316
+ const taskType = this.categorizeTask(t.taskRequest);
317
+ patterns.push(`fast-${taskType}`);
318
+ }
319
+
320
+ // Find stuck patterns (high iterations)
321
+ const stuck = trajectories.filter(
322
+ (t) => t.iterations >= 5 && t.verdict !== "approved"
323
+ );
324
+
325
+ for (const t of stuck) {
326
+ const taskType = this.categorizeTask(t.taskRequest);
327
+ patterns.push(`stuck-${taskType}`);
328
+ }
329
+
330
+ return [...new Set(patterns)];
331
+ }
332
+
333
+ /**
334
+ * Categorize task from request
335
+ */
336
+ private categorizeTask(request: string): string {
337
+ const lower = request.toLowerCase();
338
+
339
+ if (lower.includes("test")) return "testing";
340
+ if (lower.includes("fix") || lower.includes("bug")) return "bugfix";
341
+ if (lower.includes("refactor")) return "refactor";
342
+ if (lower.includes("api")) return "api";
343
+ if (lower.includes("config") || lower.includes("setup")) return "config";
344
+
345
+ return "general";
346
+ }
347
+
348
+ /**
349
+ * Suggest skill extraction
350
+ */
351
+ private suggestSkillExtraction(pattern: string): CuratorAction | null {
352
+ return {
353
+ type: "extract_skill",
354
+ pattern,
355
+ from: [], // Would be populated from trajectories
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Find outdated items
361
+ */
362
+ findOutdatedItems(
363
+ memory: MemoryEntry[],
364
+ skills: LocalSkill[]
365
+ ): Array<{ id: string; type: "memory" | "skill"; ageDays: number }> {
366
+ const outdated: Array<{ id: string; type: "memory" | "skill"; ageDays: number }> = [];
367
+ const now = Date.now();
368
+
369
+ for (const entry of memory) {
370
+ const ageMs = now - new Date(entry.created_at).getTime();
371
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
372
+
373
+ if (ageDays > this.config.archiveAgeDays) {
374
+ outdated.push({ id: entry.id, type: "memory", ageDays });
375
+ }
376
+ }
377
+
378
+ for (const skill of skills) {
379
+ const created = skill.hermes?.created_at
380
+ ? new Date(skill.hermes.created_at).getTime()
381
+ : now;
382
+ const ageMs = now - created;
383
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
384
+
385
+ // Only archive agent-created skills, not user skills
386
+ if (ageDays > this.config.archiveAgeDays && skill.hermes?.author === "agent") {
387
+ const usageCount = skill.hermes?.usage_count ?? 0;
388
+ if (usageCount === 0) {
389
+ outdated.push({ id: skill.id, type: "skill", ageDays });
390
+ }
391
+ }
392
+ }
393
+
394
+ return outdated;
395
+ }
396
+
397
+ /**
398
+ * Suggest archive action
399
+ */
400
+ private suggestArchive(
401
+ item: { id: string; type: "memory" | "skill"; ageDays: number }
402
+ ): CuratorAction {
403
+ return {
404
+ type: item.type === "memory" ? "archive_memory" : "archive_skill",
405
+ id: item.id,
406
+ } as CuratorAction;
407
+ }
408
+
409
+ /**
410
+ * Suggest skill patch
411
+ */
412
+ private suggestSkillPatch(
413
+ skill: LocalSkill,
414
+ errors: Array<{ rule: string; message: string }>
415
+ ): CuratorAction {
416
+ // Generate patch suggestion based on errors
417
+ const patches: Partial<LocalSkill> = {};
418
+
419
+ for (const error of errors) {
420
+ switch (error.rule) {
421
+ case "missing-description":
422
+ // Would need to generate a description
423
+ break;
424
+ case "oversized-body":
425
+ // Would need to suggest splitting
426
+ break;
427
+ case "incident-log-shape":
428
+ // Would need to clean up content
429
+ break;
430
+ }
431
+ }
432
+
433
+ return {
434
+ type: "patch_skill",
435
+ id: skill.id,
436
+ patch: patches,
437
+ };
438
+ }
439
+
440
+ /**
441
+ * Get quality summary
442
+ */
443
+ getQualitySummary(skills: LocalSkill[]): {
444
+ total: number;
445
+ errors: number;
446
+ warnings: number;
447
+ byRule: Record<string, number>;
448
+ } {
449
+ // Note: Full linting requires skills package - returning placeholder
450
+ return {
451
+ total: skills.length,
452
+ errors: 0,
453
+ warnings: 0,
454
+ byRule: {},
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Update configuration
460
+ */
461
+ updateConfig(config: Partial<CuratorConfig>): void {
462
+ this.config = { ...this.config, ...config };
463
+ }
464
+
465
+ /**
466
+ * Get configuration
467
+ */
468
+ getConfig(): CuratorConfig {
469
+ return { ...this.config };
470
+ }
471
+ }
472
+
473
+ // Global instance
474
+ let globalCurator: Curator | null = null;
475
+
476
+ export function getGlobalCurator(): Curator {
477
+ if (!globalCurator) {
478
+ globalCurator = new Curator();
479
+ }
480
+ return globalCurator;
481
+ }
482
+
483
+ export function createCurator(config?: Partial<CuratorConfig>): Curator {
484
+ return new Curator(config);
485
+ }