opencode-swarm-plugin 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.
- package/.beads/.local_version +1 -0
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +62 -0
- package/.beads/issues.jsonl +549 -0
- package/.beads/metadata.json +4 -0
- package/.gitattributes +3 -0
- package/Dockerfile +30 -0
- package/README.md +312 -0
- package/bun.lock +212 -0
- package/dist/index.js +14627 -0
- package/dist/plugin.js +14562 -0
- package/docker/agent-mail/Dockerfile +23 -0
- package/docker/agent-mail/__pycache__/server.cpython-314.pyc +0 -0
- package/docker/agent-mail/requirements.txt +3 -0
- package/docker/agent-mail/server.py +879 -0
- package/docker-compose.yml +45 -0
- package/package.json +52 -0
- package/scripts/docker-entrypoint.sh +54 -0
- package/src/agent-mail.integration.test.ts +1321 -0
- package/src/agent-mail.ts +665 -0
- package/src/anti-patterns.ts +430 -0
- package/src/beads.integration.test.ts +688 -0
- package/src/beads.ts +603 -0
- package/src/index.ts +267 -0
- package/src/learning.integration.test.ts +1104 -0
- package/src/learning.ts +438 -0
- package/src/pattern-maturity.ts +487 -0
- package/src/plugin.ts +11 -0
- package/src/schemas/bead.ts +152 -0
- package/src/schemas/evaluation.ts +133 -0
- package/src/schemas/index.test.ts +199 -0
- package/src/schemas/index.ts +77 -0
- package/src/schemas/task.ts +129 -0
- package/src/structured.ts +708 -0
- package/src/swarm.integration.test.ts +763 -0
- package/src/swarm.ts +1411 -0
- package/tsconfig.json +28 -0
- package/vitest.integration.config.ts +13 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Maturity Module
|
|
3
|
+
*
|
|
4
|
+
* Tracks decomposition pattern maturity states through lifecycle:
|
|
5
|
+
* candidate → established → proven (or deprecated)
|
|
6
|
+
*
|
|
7
|
+
* Patterns start as candidates until they accumulate enough feedback.
|
|
8
|
+
* Strong positive feedback promotes to proven, strong negative deprecates.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/Dicklesworthstone/cass_memory_system/blob/main/src/scoring.ts#L73-L98
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { calculateDecayedValue } from "./learning";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Schemas
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Maturity state for a decomposition pattern
|
|
21
|
+
*
|
|
22
|
+
* - candidate: Not enough feedback to judge (< minFeedback events)
|
|
23
|
+
* - established: Enough feedback, neither proven nor deprecated
|
|
24
|
+
* - proven: Strong positive signal (high helpful, low harmful ratio)
|
|
25
|
+
* - deprecated: Strong negative signal (high harmful ratio)
|
|
26
|
+
*/
|
|
27
|
+
export const MaturityStateSchema = z.enum([
|
|
28
|
+
"candidate",
|
|
29
|
+
"established",
|
|
30
|
+
"proven",
|
|
31
|
+
"deprecated",
|
|
32
|
+
]);
|
|
33
|
+
export type MaturityState = z.infer<typeof MaturityStateSchema>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Pattern maturity tracking
|
|
37
|
+
*
|
|
38
|
+
* Tracks feedback counts and state transitions for a decomposition pattern.
|
|
39
|
+
*/
|
|
40
|
+
export const PatternMaturitySchema = z.object({
|
|
41
|
+
/** Unique identifier for the pattern */
|
|
42
|
+
pattern_id: z.string(),
|
|
43
|
+
/** Current maturity state */
|
|
44
|
+
state: MaturityStateSchema,
|
|
45
|
+
/** Number of helpful feedback events */
|
|
46
|
+
helpful_count: z.number().int().min(0),
|
|
47
|
+
/** Number of harmful feedback events */
|
|
48
|
+
harmful_count: z.number().int().min(0),
|
|
49
|
+
/** When the pattern was last validated (ISO-8601) */
|
|
50
|
+
last_validated: z.string(),
|
|
51
|
+
/** When the pattern was promoted to proven (ISO-8601) */
|
|
52
|
+
promoted_at: z.string().optional(),
|
|
53
|
+
/** When the pattern was deprecated (ISO-8601) */
|
|
54
|
+
deprecated_at: z.string().optional(),
|
|
55
|
+
});
|
|
56
|
+
export type PatternMaturity = z.infer<typeof PatternMaturitySchema>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Feedback event for maturity tracking
|
|
60
|
+
*/
|
|
61
|
+
export const MaturityFeedbackSchema = z.object({
|
|
62
|
+
/** Pattern this feedback applies to */
|
|
63
|
+
pattern_id: z.string(),
|
|
64
|
+
/** Whether the pattern was helpful or harmful */
|
|
65
|
+
type: z.enum(["helpful", "harmful"]),
|
|
66
|
+
/** When this feedback was recorded (ISO-8601) */
|
|
67
|
+
timestamp: z.string(),
|
|
68
|
+
/** Raw weight before decay (0-1) */
|
|
69
|
+
weight: z.number().min(0).max(1).default(1),
|
|
70
|
+
});
|
|
71
|
+
export type MaturityFeedback = z.infer<typeof MaturityFeedbackSchema>;
|
|
72
|
+
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Configuration
|
|
75
|
+
// ============================================================================
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Configuration for maturity calculations
|
|
79
|
+
*/
|
|
80
|
+
export interface MaturityConfig {
|
|
81
|
+
/** Minimum feedback events before leaving candidate state */
|
|
82
|
+
minFeedback: number;
|
|
83
|
+
/** Minimum decayed helpful score to reach proven state */
|
|
84
|
+
minHelpful: number;
|
|
85
|
+
/** Maximum harmful ratio to reach/maintain proven state */
|
|
86
|
+
maxHarmful: number;
|
|
87
|
+
/** Harmful ratio threshold for deprecation */
|
|
88
|
+
deprecationThreshold: number;
|
|
89
|
+
/** Half-life for decay in days */
|
|
90
|
+
halfLifeDays: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const DEFAULT_MATURITY_CONFIG: MaturityConfig = {
|
|
94
|
+
minFeedback: 3,
|
|
95
|
+
minHelpful: 5,
|
|
96
|
+
maxHarmful: 0.15, // 15% harmful is acceptable for proven
|
|
97
|
+
deprecationThreshold: 0.3, // 30% harmful triggers deprecation
|
|
98
|
+
halfLifeDays: 90,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// Core Functions
|
|
103
|
+
// ============================================================================
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Calculate decayed feedback counts
|
|
107
|
+
*
|
|
108
|
+
* Applies half-life decay to each feedback event based on age.
|
|
109
|
+
*
|
|
110
|
+
* @param feedbackEvents - Raw feedback events
|
|
111
|
+
* @param config - Maturity configuration
|
|
112
|
+
* @param now - Current timestamp for decay calculation
|
|
113
|
+
* @returns Decayed helpful and harmful totals
|
|
114
|
+
*/
|
|
115
|
+
export function calculateDecayedCounts(
|
|
116
|
+
feedbackEvents: MaturityFeedback[],
|
|
117
|
+
config: MaturityConfig = DEFAULT_MATURITY_CONFIG,
|
|
118
|
+
now: Date = new Date(),
|
|
119
|
+
): { decayedHelpful: number; decayedHarmful: number } {
|
|
120
|
+
let decayedHelpful = 0;
|
|
121
|
+
let decayedHarmful = 0;
|
|
122
|
+
|
|
123
|
+
for (const event of feedbackEvents) {
|
|
124
|
+
const decay = calculateDecayedValue(
|
|
125
|
+
event.timestamp,
|
|
126
|
+
now,
|
|
127
|
+
config.halfLifeDays,
|
|
128
|
+
);
|
|
129
|
+
const value = event.weight * decay;
|
|
130
|
+
|
|
131
|
+
if (event.type === "helpful") {
|
|
132
|
+
decayedHelpful += value;
|
|
133
|
+
} else {
|
|
134
|
+
decayedHarmful += value;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { decayedHelpful, decayedHarmful };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Calculate maturity state from feedback events
|
|
143
|
+
*
|
|
144
|
+
* State determination logic:
|
|
145
|
+
* 1. "deprecated" if harmful ratio > 0.3 AND total >= minFeedback
|
|
146
|
+
* 2. "candidate" if total < minFeedback (not enough data)
|
|
147
|
+
* 3. "proven" if decayedHelpful >= minHelpful AND harmfulRatio < maxHarmful
|
|
148
|
+
* 4. "established" otherwise (enough data but not yet proven)
|
|
149
|
+
*
|
|
150
|
+
* @param feedbackEvents - Feedback events for this pattern
|
|
151
|
+
* @param config - Maturity configuration
|
|
152
|
+
* @param now - Current timestamp for decay calculation
|
|
153
|
+
* @returns Calculated maturity state
|
|
154
|
+
*/
|
|
155
|
+
export function calculateMaturityState(
|
|
156
|
+
feedbackEvents: MaturityFeedback[],
|
|
157
|
+
config: MaturityConfig = DEFAULT_MATURITY_CONFIG,
|
|
158
|
+
now: Date = new Date(),
|
|
159
|
+
): MaturityState {
|
|
160
|
+
const { decayedHelpful, decayedHarmful } = calculateDecayedCounts(
|
|
161
|
+
feedbackEvents,
|
|
162
|
+
config,
|
|
163
|
+
now,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const total = decayedHelpful + decayedHarmful;
|
|
167
|
+
const epsilon = 0.01; // Float comparison tolerance
|
|
168
|
+
const safeTotal = total > epsilon ? total : 0;
|
|
169
|
+
const harmfulRatio = safeTotal > 0 ? decayedHarmful / safeTotal : 0;
|
|
170
|
+
|
|
171
|
+
// Deprecated: high harmful ratio with enough feedback
|
|
172
|
+
if (
|
|
173
|
+
harmfulRatio > config.deprecationThreshold &&
|
|
174
|
+
safeTotal >= config.minFeedback - epsilon
|
|
175
|
+
) {
|
|
176
|
+
return "deprecated";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Candidate: not enough feedback yet
|
|
180
|
+
if (safeTotal < config.minFeedback - epsilon) {
|
|
181
|
+
return "candidate";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Proven: strong positive signal
|
|
185
|
+
if (
|
|
186
|
+
decayedHelpful >= config.minHelpful - epsilon &&
|
|
187
|
+
harmfulRatio < config.maxHarmful
|
|
188
|
+
) {
|
|
189
|
+
return "proven";
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Established: enough data but not proven
|
|
193
|
+
return "established";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Create initial pattern maturity record
|
|
198
|
+
*
|
|
199
|
+
* @param patternId - Unique pattern identifier
|
|
200
|
+
* @returns New PatternMaturity in candidate state
|
|
201
|
+
*/
|
|
202
|
+
export function createPatternMaturity(patternId: string): PatternMaturity {
|
|
203
|
+
return {
|
|
204
|
+
pattern_id: patternId,
|
|
205
|
+
state: "candidate",
|
|
206
|
+
helpful_count: 0,
|
|
207
|
+
harmful_count: 0,
|
|
208
|
+
last_validated: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Update pattern maturity with new feedback
|
|
214
|
+
*
|
|
215
|
+
* Records feedback, updates counts, and recalculates state.
|
|
216
|
+
*
|
|
217
|
+
* @param maturity - Current maturity record
|
|
218
|
+
* @param feedbackEvents - All feedback events for this pattern
|
|
219
|
+
* @param config - Maturity configuration
|
|
220
|
+
* @returns Updated maturity record
|
|
221
|
+
*/
|
|
222
|
+
export function updatePatternMaturity(
|
|
223
|
+
maturity: PatternMaturity,
|
|
224
|
+
feedbackEvents: MaturityFeedback[],
|
|
225
|
+
config: MaturityConfig = DEFAULT_MATURITY_CONFIG,
|
|
226
|
+
): PatternMaturity {
|
|
227
|
+
const now = new Date();
|
|
228
|
+
const newState = calculateMaturityState(feedbackEvents, config, now);
|
|
229
|
+
|
|
230
|
+
// Count raw feedback (not decayed)
|
|
231
|
+
const helpfulCount = feedbackEvents.filter(
|
|
232
|
+
(e) => e.type === "helpful",
|
|
233
|
+
).length;
|
|
234
|
+
const harmfulCount = feedbackEvents.filter(
|
|
235
|
+
(e) => e.type === "harmful",
|
|
236
|
+
).length;
|
|
237
|
+
|
|
238
|
+
const updated: PatternMaturity = {
|
|
239
|
+
...maturity,
|
|
240
|
+
state: newState,
|
|
241
|
+
helpful_count: helpfulCount,
|
|
242
|
+
harmful_count: harmfulCount,
|
|
243
|
+
last_validated: now.toISOString(),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Track state transitions
|
|
247
|
+
if (newState === "proven" && maturity.state !== "proven") {
|
|
248
|
+
updated.promoted_at = now.toISOString();
|
|
249
|
+
}
|
|
250
|
+
if (newState === "deprecated" && maturity.state !== "deprecated") {
|
|
251
|
+
updated.deprecated_at = now.toISOString();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return updated;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Promote a pattern to proven state
|
|
259
|
+
*
|
|
260
|
+
* Manually promotes a pattern regardless of feedback counts.
|
|
261
|
+
* Use when external validation confirms pattern effectiveness.
|
|
262
|
+
*
|
|
263
|
+
* @param maturity - Current maturity record
|
|
264
|
+
* @returns Updated maturity record with proven state
|
|
265
|
+
*/
|
|
266
|
+
export function promotePattern(maturity: PatternMaturity): PatternMaturity {
|
|
267
|
+
if (maturity.state === "deprecated") {
|
|
268
|
+
throw new Error("Cannot promote a deprecated pattern");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (maturity.state === "proven") {
|
|
272
|
+
return maturity; // Already proven
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const now = new Date().toISOString();
|
|
276
|
+
return {
|
|
277
|
+
...maturity,
|
|
278
|
+
state: "proven",
|
|
279
|
+
promoted_at: now,
|
|
280
|
+
last_validated: now,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Deprecate a pattern
|
|
286
|
+
*
|
|
287
|
+
* Manually deprecates a pattern regardless of feedback counts.
|
|
288
|
+
* Use when external validation shows pattern is harmful.
|
|
289
|
+
*
|
|
290
|
+
* @param maturity - Current maturity record
|
|
291
|
+
* @param reason - Optional reason for deprecation
|
|
292
|
+
* @returns Updated maturity record with deprecated state
|
|
293
|
+
*/
|
|
294
|
+
export function deprecatePattern(
|
|
295
|
+
maturity: PatternMaturity,
|
|
296
|
+
_reason?: string,
|
|
297
|
+
): PatternMaturity {
|
|
298
|
+
if (maturity.state === "deprecated") {
|
|
299
|
+
return maturity; // Already deprecated
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const now = new Date().toISOString();
|
|
303
|
+
return {
|
|
304
|
+
...maturity,
|
|
305
|
+
state: "deprecated",
|
|
306
|
+
deprecated_at: now,
|
|
307
|
+
last_validated: now,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get maturity score multiplier for pattern ranking
|
|
313
|
+
*
|
|
314
|
+
* Higher maturity patterns should be weighted more heavily.
|
|
315
|
+
*
|
|
316
|
+
* @param state - Maturity state
|
|
317
|
+
* @returns Score multiplier (0-1.5)
|
|
318
|
+
*/
|
|
319
|
+
export function getMaturityMultiplier(state: MaturityState): number {
|
|
320
|
+
const multipliers: Record<MaturityState, number> = {
|
|
321
|
+
candidate: 0.5,
|
|
322
|
+
established: 1.0,
|
|
323
|
+
proven: 1.5,
|
|
324
|
+
deprecated: 0,
|
|
325
|
+
};
|
|
326
|
+
return multipliers[state];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Format maturity state for inclusion in prompts
|
|
331
|
+
*
|
|
332
|
+
* Shows pattern reliability to help agents make informed decisions.
|
|
333
|
+
*
|
|
334
|
+
* @param maturity - Pattern maturity record
|
|
335
|
+
* @returns Formatted string describing pattern reliability
|
|
336
|
+
*/
|
|
337
|
+
export function formatMaturityForPrompt(maturity: PatternMaturity): string {
|
|
338
|
+
const total = maturity.helpful_count + maturity.harmful_count;
|
|
339
|
+
const harmfulRatio =
|
|
340
|
+
total > 0 ? Math.round((maturity.harmful_count / total) * 100) : 0;
|
|
341
|
+
const helpfulRatio =
|
|
342
|
+
total > 0 ? Math.round((maturity.helpful_count / total) * 100) : 0;
|
|
343
|
+
|
|
344
|
+
switch (maturity.state) {
|
|
345
|
+
case "candidate":
|
|
346
|
+
return `[CANDIDATE - ${total} observations, needs more data]`;
|
|
347
|
+
case "established":
|
|
348
|
+
return `[ESTABLISHED - ${helpfulRatio}% helpful, ${harmfulRatio}% harmful from ${total} observations]`;
|
|
349
|
+
case "proven":
|
|
350
|
+
return `[PROVEN - ${helpfulRatio}% helpful from ${total} observations]`;
|
|
351
|
+
case "deprecated":
|
|
352
|
+
return `[DEPRECATED - ${harmfulRatio}% harmful, avoid using]`;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Format multiple patterns with maturity for prompt inclusion
|
|
358
|
+
*
|
|
359
|
+
* Groups patterns by maturity state for clear presentation.
|
|
360
|
+
*
|
|
361
|
+
* @param patterns - Map of pattern content to maturity record
|
|
362
|
+
* @returns Formatted string for prompt inclusion
|
|
363
|
+
*/
|
|
364
|
+
export function formatPatternsWithMaturityForPrompt(
|
|
365
|
+
patterns: Map<string, PatternMaturity>,
|
|
366
|
+
): string {
|
|
367
|
+
const proven: string[] = [];
|
|
368
|
+
const established: string[] = [];
|
|
369
|
+
const candidates: string[] = [];
|
|
370
|
+
const deprecated: string[] = [];
|
|
371
|
+
|
|
372
|
+
for (const [content, maturity] of patterns) {
|
|
373
|
+
const formatted = `- ${content} ${formatMaturityForPrompt(maturity)}`;
|
|
374
|
+
switch (maturity.state) {
|
|
375
|
+
case "proven":
|
|
376
|
+
proven.push(formatted);
|
|
377
|
+
break;
|
|
378
|
+
case "established":
|
|
379
|
+
established.push(formatted);
|
|
380
|
+
break;
|
|
381
|
+
case "candidate":
|
|
382
|
+
candidates.push(formatted);
|
|
383
|
+
break;
|
|
384
|
+
case "deprecated":
|
|
385
|
+
deprecated.push(formatted);
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const sections: string[] = [];
|
|
391
|
+
|
|
392
|
+
if (proven.length > 0) {
|
|
393
|
+
sections.push(
|
|
394
|
+
"## Proven Patterns\n\nThese patterns consistently work well:\n\n" +
|
|
395
|
+
proven.join("\n"),
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (established.length > 0) {
|
|
400
|
+
sections.push(
|
|
401
|
+
"## Established Patterns\n\nThese patterns have track records:\n\n" +
|
|
402
|
+
established.join("\n"),
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (candidates.length > 0) {
|
|
407
|
+
sections.push(
|
|
408
|
+
"## Candidate Patterns\n\nThese patterns need more validation:\n\n" +
|
|
409
|
+
candidates.join("\n"),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (deprecated.length > 0) {
|
|
414
|
+
sections.push(
|
|
415
|
+
"## Deprecated Patterns\n\nAVOID these patterns - they have poor track records:\n\n" +
|
|
416
|
+
deprecated.join("\n"),
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return sections.join("\n\n");
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// Storage
|
|
425
|
+
// ============================================================================
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Storage interface for pattern maturity records
|
|
429
|
+
*/
|
|
430
|
+
export interface MaturityStorage {
|
|
431
|
+
/** Store or update a maturity record */
|
|
432
|
+
store(maturity: PatternMaturity): Promise<void>;
|
|
433
|
+
/** Get maturity record by pattern ID */
|
|
434
|
+
get(patternId: string): Promise<PatternMaturity | null>;
|
|
435
|
+
/** Get all maturity records */
|
|
436
|
+
getAll(): Promise<PatternMaturity[]>;
|
|
437
|
+
/** Get patterns by state */
|
|
438
|
+
getByState(state: MaturityState): Promise<PatternMaturity[]>;
|
|
439
|
+
/** Store a feedback event */
|
|
440
|
+
storeFeedback(feedback: MaturityFeedback): Promise<void>;
|
|
441
|
+
/** Get all feedback for a pattern */
|
|
442
|
+
getFeedback(patternId: string): Promise<MaturityFeedback[]>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* In-memory maturity storage (for testing and short-lived sessions)
|
|
447
|
+
*/
|
|
448
|
+
export class InMemoryMaturityStorage implements MaturityStorage {
|
|
449
|
+
private maturities: Map<string, PatternMaturity> = new Map();
|
|
450
|
+
private feedback: MaturityFeedback[] = [];
|
|
451
|
+
|
|
452
|
+
async store(maturity: PatternMaturity): Promise<void> {
|
|
453
|
+
this.maturities.set(maturity.pattern_id, maturity);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async get(patternId: string): Promise<PatternMaturity | null> {
|
|
457
|
+
return this.maturities.get(patternId) ?? null;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async getAll(): Promise<PatternMaturity[]> {
|
|
461
|
+
return Array.from(this.maturities.values());
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async getByState(state: MaturityState): Promise<PatternMaturity[]> {
|
|
465
|
+
return Array.from(this.maturities.values()).filter(
|
|
466
|
+
(m) => m.state === state,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async storeFeedback(feedback: MaturityFeedback): Promise<void> {
|
|
471
|
+
this.feedback.push(feedback);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async getFeedback(patternId: string): Promise<MaturityFeedback[]> {
|
|
475
|
+
return this.feedback.filter((f) => f.pattern_id === patternId);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ============================================================================
|
|
480
|
+
// Exports
|
|
481
|
+
// ============================================================================
|
|
482
|
+
|
|
483
|
+
export const maturitySchemas = {
|
|
484
|
+
MaturityStateSchema,
|
|
485
|
+
PatternMaturitySchema,
|
|
486
|
+
MaturityFeedbackSchema,
|
|
487
|
+
};
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Plugin Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This file ONLY exports the plugin function.
|
|
5
|
+
* The plugin loader iterates over all exports and calls them as functions,
|
|
6
|
+
* so we cannot export anything else here (classes, constants, types, etc.)
|
|
7
|
+
*/
|
|
8
|
+
import { SwarmPlugin } from "./index";
|
|
9
|
+
|
|
10
|
+
// Only export the plugin function - nothing else!
|
|
11
|
+
export { SwarmPlugin };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bead schemas for type-safe beads operations
|
|
3
|
+
*
|
|
4
|
+
* These schemas validate all data from the `bd` CLI to ensure
|
|
5
|
+
* type safety and catch malformed responses early.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
/** Valid bead statuses */
|
|
10
|
+
export const BeadStatusSchema = z.enum([
|
|
11
|
+
"open",
|
|
12
|
+
"in_progress",
|
|
13
|
+
"blocked",
|
|
14
|
+
"closed",
|
|
15
|
+
]);
|
|
16
|
+
export type BeadStatus = z.infer<typeof BeadStatusSchema>;
|
|
17
|
+
|
|
18
|
+
/** Valid bead types */
|
|
19
|
+
export const BeadTypeSchema = z.enum([
|
|
20
|
+
"bug",
|
|
21
|
+
"feature",
|
|
22
|
+
"task",
|
|
23
|
+
"epic",
|
|
24
|
+
"chore",
|
|
25
|
+
]);
|
|
26
|
+
export type BeadType = z.infer<typeof BeadTypeSchema>;
|
|
27
|
+
|
|
28
|
+
/** Dependency relationship between beads */
|
|
29
|
+
export const BeadDependencySchema = z.object({
|
|
30
|
+
id: z.string(),
|
|
31
|
+
type: z.enum(["blocks", "blocked-by", "related", "discovered-from"]),
|
|
32
|
+
});
|
|
33
|
+
export type BeadDependency = z.infer<typeof BeadDependencySchema>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Core bead schema - validates bd CLI JSON output
|
|
37
|
+
*
|
|
38
|
+
* ID format:
|
|
39
|
+
* - Standard: `{project}-{hash}` (e.g., `opencode-swarm-plugin-1i8`)
|
|
40
|
+
* - Subtask: `{project}-{hash}.{index}` (e.g., `opencode-swarm-plugin-1i8.1`)
|
|
41
|
+
*/
|
|
42
|
+
export const BeadSchema = z.object({
|
|
43
|
+
id: z
|
|
44
|
+
.string()
|
|
45
|
+
.regex(/^[a-z0-9-]+-[a-z0-9]+(\.\d+)?$/, "Invalid bead ID format"),
|
|
46
|
+
title: z.string().min(1, "Title required"),
|
|
47
|
+
description: z.string().optional().default(""),
|
|
48
|
+
status: BeadStatusSchema.default("open"),
|
|
49
|
+
priority: z.number().int().min(0).max(3).default(2),
|
|
50
|
+
issue_type: BeadTypeSchema.default("task"),
|
|
51
|
+
created_at: z.string(), // ISO-8601
|
|
52
|
+
updated_at: z.string().optional(),
|
|
53
|
+
closed_at: z.string().optional(),
|
|
54
|
+
parent_id: z.string().optional(),
|
|
55
|
+
dependencies: z.array(BeadDependencySchema).optional().default([]),
|
|
56
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
57
|
+
});
|
|
58
|
+
export type Bead = z.infer<typeof BeadSchema>;
|
|
59
|
+
|
|
60
|
+
/** Arguments for creating a bead */
|
|
61
|
+
export const BeadCreateArgsSchema = z.object({
|
|
62
|
+
title: z.string().min(1, "Title required"),
|
|
63
|
+
type: BeadTypeSchema.default("task"),
|
|
64
|
+
priority: z.number().int().min(0).max(3).default(2),
|
|
65
|
+
description: z.string().optional(),
|
|
66
|
+
parent_id: z.string().optional(),
|
|
67
|
+
});
|
|
68
|
+
export type BeadCreateArgs = z.infer<typeof BeadCreateArgsSchema>;
|
|
69
|
+
|
|
70
|
+
/** Arguments for updating a bead */
|
|
71
|
+
export const BeadUpdateArgsSchema = z.object({
|
|
72
|
+
id: z.string(),
|
|
73
|
+
status: BeadStatusSchema.optional(),
|
|
74
|
+
description: z.string().optional(),
|
|
75
|
+
priority: z.number().int().min(0).max(3).optional(),
|
|
76
|
+
});
|
|
77
|
+
export type BeadUpdateArgs = z.infer<typeof BeadUpdateArgsSchema>;
|
|
78
|
+
|
|
79
|
+
/** Arguments for closing a bead */
|
|
80
|
+
export const BeadCloseArgsSchema = z.object({
|
|
81
|
+
id: z.string(),
|
|
82
|
+
reason: z.string().min(1, "Reason required"),
|
|
83
|
+
});
|
|
84
|
+
export type BeadCloseArgs = z.infer<typeof BeadCloseArgsSchema>;
|
|
85
|
+
|
|
86
|
+
/** Arguments for querying beads */
|
|
87
|
+
export const BeadQueryArgsSchema = z.object({
|
|
88
|
+
status: BeadStatusSchema.optional(),
|
|
89
|
+
type: BeadTypeSchema.optional(),
|
|
90
|
+
ready: z.boolean().optional(),
|
|
91
|
+
limit: z.number().int().positive().default(20),
|
|
92
|
+
});
|
|
93
|
+
export type BeadQueryArgs = z.infer<typeof BeadQueryArgsSchema>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Subtask specification for epic decomposition
|
|
97
|
+
*
|
|
98
|
+
* Used when creating an epic with subtasks in one operation.
|
|
99
|
+
* The `files` array is used for Agent Mail file reservations.
|
|
100
|
+
*/
|
|
101
|
+
export const SubtaskSpecSchema = z.object({
|
|
102
|
+
title: z.string().min(1),
|
|
103
|
+
description: z.string().optional().default(""),
|
|
104
|
+
files: z.array(z.string()).default([]),
|
|
105
|
+
dependencies: z.array(z.number().int().min(0)).default([]), // Indices of other subtasks
|
|
106
|
+
estimated_complexity: z.number().int().min(1).max(5).default(3),
|
|
107
|
+
});
|
|
108
|
+
export type SubtaskSpec = z.infer<typeof SubtaskSpecSchema>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Bead tree for swarm decomposition
|
|
112
|
+
*
|
|
113
|
+
* Represents an epic with its subtasks, ready for spawning agents.
|
|
114
|
+
*/
|
|
115
|
+
export const BeadTreeSchema = z.object({
|
|
116
|
+
epic: z.object({
|
|
117
|
+
title: z.string().min(1),
|
|
118
|
+
description: z.string().optional().default(""),
|
|
119
|
+
}),
|
|
120
|
+
subtasks: z.array(SubtaskSpecSchema).min(1).max(10),
|
|
121
|
+
});
|
|
122
|
+
export type BeadTree = z.infer<typeof BeadTreeSchema>;
|
|
123
|
+
|
|
124
|
+
/** Arguments for creating an epic with subtasks */
|
|
125
|
+
export const EpicCreateArgsSchema = z.object({
|
|
126
|
+
epic_title: z.string().min(1),
|
|
127
|
+
epic_description: z.string().optional(),
|
|
128
|
+
subtasks: z
|
|
129
|
+
.array(
|
|
130
|
+
z.object({
|
|
131
|
+
title: z.string().min(1),
|
|
132
|
+
priority: z.number().int().min(0).max(3).default(2),
|
|
133
|
+
files: z.array(z.string()).optional().default([]),
|
|
134
|
+
}),
|
|
135
|
+
)
|
|
136
|
+
.min(1)
|
|
137
|
+
.max(10),
|
|
138
|
+
});
|
|
139
|
+
export type EpicCreateArgs = z.infer<typeof EpicCreateArgsSchema>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Result of epic creation
|
|
143
|
+
*
|
|
144
|
+
* Contains the created epic and all subtasks with their IDs.
|
|
145
|
+
*/
|
|
146
|
+
export const EpicCreateResultSchema = z.object({
|
|
147
|
+
success: z.boolean(),
|
|
148
|
+
epic: BeadSchema,
|
|
149
|
+
subtasks: z.array(BeadSchema),
|
|
150
|
+
rollback_hint: z.string().optional(),
|
|
151
|
+
});
|
|
152
|
+
export type EpicCreateResult = z.infer<typeof EpicCreateResultSchema>;
|