opencode-swarm-plugin 0.43.0 → 0.44.1
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/bin/cass.characterization.test.ts +422 -0
- package/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.test.ts +68 -0
- package/bin/swarm.ts +81 -8
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/contributor-tools.d.ts +42 -0
- package/dist/contributor-tools.d.ts.map +1 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7728 -62590
- package/dist/plugin.js +23833 -78695
- package/dist/sessions/agent-discovery.d.ts +59 -0
- package/dist/sessions/agent-discovery.d.ts.map +1 -0
- package/dist/sessions/index.d.ts +10 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2255
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2426
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -935
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/anti-patterns.ts
DELETED
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anti-Pattern Learning Module
|
|
3
|
-
*
|
|
4
|
-
* Tracks failed decomposition patterns and auto-inverts them to anti-patterns.
|
|
5
|
-
* When a pattern consistently fails, it gets flagged as something to avoid.
|
|
6
|
-
*
|
|
7
|
-
* @see https://github.com/Dicklesworthstone/cass_memory_system/blob/main/src/curate.ts#L95-L117
|
|
8
|
-
*/
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
// ============================================================================
|
|
12
|
-
// Schemas
|
|
13
|
-
// ============================================================================
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Pattern kind - whether this is a positive pattern or an anti-pattern
|
|
17
|
-
*/
|
|
18
|
-
export const PatternKindSchema = z.enum(["pattern", "anti_pattern"]);
|
|
19
|
-
export type PatternKind = z.infer<typeof PatternKindSchema>;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Decomposition pattern with success/failure tracking.
|
|
23
|
-
*
|
|
24
|
-
* Field relationships:
|
|
25
|
-
* - `kind`: Tracks pattern lifecycle ("pattern" → "anti_pattern" when failure rate exceeds threshold)
|
|
26
|
-
* - `is_negative`: Derived boolean flag for quick filtering (true when kind === "anti_pattern")
|
|
27
|
-
*
|
|
28
|
-
* Both fields exist because:
|
|
29
|
-
* - `kind` is the source of truth for pattern status
|
|
30
|
-
* - `is_negative` enables efficient filtering without string comparison
|
|
31
|
-
*/
|
|
32
|
-
export const DecompositionPatternSchema = z.object({
|
|
33
|
-
/** Unique ID for this pattern */
|
|
34
|
-
id: z.string(),
|
|
35
|
-
/** Human-readable description of the pattern */
|
|
36
|
-
content: z.string(),
|
|
37
|
-
/** Whether this is a positive pattern or anti-pattern */
|
|
38
|
-
kind: PatternKindSchema,
|
|
39
|
-
/** Whether this pattern should be avoided (true for anti-patterns) */
|
|
40
|
-
is_negative: z.boolean(),
|
|
41
|
-
/** Number of times this pattern succeeded */
|
|
42
|
-
success_count: z.number().int().min(0).default(0),
|
|
43
|
-
/** Number of times this pattern failed */
|
|
44
|
-
failure_count: z.number().int().min(0).default(0),
|
|
45
|
-
/** When this pattern was first observed */
|
|
46
|
-
created_at: z.string(), // ISO-8601
|
|
47
|
-
/** When this pattern was last updated */
|
|
48
|
-
updated_at: z.string(), // ISO-8601
|
|
49
|
-
/** Context about why this pattern was created/inverted */
|
|
50
|
-
reason: z.string().optional(),
|
|
51
|
-
/** Tags for categorization (e.g., "file-splitting", "dependency-ordering") */
|
|
52
|
-
tags: z.array(z.string()).default([]),
|
|
53
|
-
/** Example bead IDs where this pattern was observed */
|
|
54
|
-
example_beads: z.array(z.string()).default([]),
|
|
55
|
-
});
|
|
56
|
-
export type DecompositionPattern = z.infer<typeof DecompositionPatternSchema>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Result of pattern inversion
|
|
60
|
-
*/
|
|
61
|
-
export const PatternInversionResultSchema = z.object({
|
|
62
|
-
/** The original pattern */
|
|
63
|
-
original: DecompositionPatternSchema,
|
|
64
|
-
/** The inverted anti-pattern */
|
|
65
|
-
inverted: DecompositionPatternSchema,
|
|
66
|
-
/** Why the inversion happened */
|
|
67
|
-
reason: z.string(),
|
|
68
|
-
});
|
|
69
|
-
export type PatternInversionResult = z.infer<
|
|
70
|
-
typeof PatternInversionResultSchema
|
|
71
|
-
>;
|
|
72
|
-
|
|
73
|
-
// ============================================================================
|
|
74
|
-
// Configuration
|
|
75
|
-
// ============================================================================
|
|
76
|
-
|
|
77
|
-
/** Maximum number of example beads to keep per pattern */
|
|
78
|
-
const MAX_EXAMPLE_BEADS = 10;
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Configuration for anti-pattern detection
|
|
82
|
-
*/
|
|
83
|
-
export interface AntiPatternConfig {
|
|
84
|
-
/** Minimum observations before considering inversion */
|
|
85
|
-
minObservations: number;
|
|
86
|
-
/** Failure ratio threshold for inversion (0-1) */
|
|
87
|
-
failureRatioThreshold: number;
|
|
88
|
-
/** Prefix for anti-pattern content */
|
|
89
|
-
antiPatternPrefix: string;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export const DEFAULT_ANTI_PATTERN_CONFIG: AntiPatternConfig = {
|
|
93
|
-
minObservations: 3,
|
|
94
|
-
failureRatioThreshold: 0.6, // 60% failure rate triggers inversion
|
|
95
|
-
antiPatternPrefix: "AVOID: ",
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
// ============================================================================
|
|
99
|
-
// Core Functions
|
|
100
|
-
// ============================================================================
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Check if a pattern should be inverted to an anti-pattern
|
|
104
|
-
*
|
|
105
|
-
* A pattern is inverted when:
|
|
106
|
-
* 1. It has enough observations (minObservations)
|
|
107
|
-
* 2. Its failure ratio exceeds the threshold
|
|
108
|
-
*
|
|
109
|
-
* @param pattern - The pattern to check
|
|
110
|
-
* @param config - Anti-pattern configuration
|
|
111
|
-
* @returns Whether the pattern should be inverted
|
|
112
|
-
*/
|
|
113
|
-
export function shouldInvertPattern(
|
|
114
|
-
pattern: DecompositionPattern,
|
|
115
|
-
config: AntiPatternConfig = DEFAULT_ANTI_PATTERN_CONFIG,
|
|
116
|
-
): boolean {
|
|
117
|
-
// Already an anti-pattern
|
|
118
|
-
if (pattern.kind === "anti_pattern") {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const total = pattern.success_count + pattern.failure_count;
|
|
123
|
-
|
|
124
|
-
// Not enough observations
|
|
125
|
-
if (total < config.minObservations) {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const failureRatio = pattern.failure_count / total;
|
|
130
|
-
return failureRatio >= config.failureRatioThreshold;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Invert a pattern to an anti-pattern
|
|
135
|
-
*
|
|
136
|
-
* Creates a new anti-pattern from a failing pattern.
|
|
137
|
-
* The content is prefixed with "AVOID: " and the kind is changed.
|
|
138
|
-
*
|
|
139
|
-
* @param pattern - The pattern to invert
|
|
140
|
-
* @param reason - Why the inversion is happening
|
|
141
|
-
* @param config - Anti-pattern configuration
|
|
142
|
-
* @returns The inverted anti-pattern
|
|
143
|
-
*/
|
|
144
|
-
export function invertToAntiPattern(
|
|
145
|
-
pattern: DecompositionPattern,
|
|
146
|
-
reason: string,
|
|
147
|
-
config: AntiPatternConfig = DEFAULT_ANTI_PATTERN_CONFIG,
|
|
148
|
-
): PatternInversionResult {
|
|
149
|
-
// Clean the content (remove any existing prefix)
|
|
150
|
-
const cleaned = pattern.content
|
|
151
|
-
.replace(/^AVOID:\s*/i, "")
|
|
152
|
-
.replace(/^DO NOT:\s*/i, "")
|
|
153
|
-
.replace(/^NEVER:\s*/i, "");
|
|
154
|
-
|
|
155
|
-
const inverted: DecompositionPattern = {
|
|
156
|
-
...pattern,
|
|
157
|
-
id: `anti-${pattern.id}`,
|
|
158
|
-
content: `${config.antiPatternPrefix}${cleaned}. ${reason}`,
|
|
159
|
-
kind: "anti_pattern",
|
|
160
|
-
is_negative: true,
|
|
161
|
-
reason,
|
|
162
|
-
updated_at: new Date().toISOString(),
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
original: pattern,
|
|
167
|
-
inverted,
|
|
168
|
-
reason,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Record a pattern observation (success or failure)
|
|
174
|
-
*
|
|
175
|
-
* Updates the pattern's success/failure counts and checks if
|
|
176
|
-
* it should be inverted to an anti-pattern.
|
|
177
|
-
*
|
|
178
|
-
* @param pattern - The pattern to update
|
|
179
|
-
* @param success - Whether this observation was successful
|
|
180
|
-
* @param beadId - Optional bead ID to record as example
|
|
181
|
-
* @param config - Anti-pattern configuration
|
|
182
|
-
* @returns Updated pattern and optional inversion result
|
|
183
|
-
*/
|
|
184
|
-
export function recordPatternObservation(
|
|
185
|
-
pattern: DecompositionPattern,
|
|
186
|
-
success: boolean,
|
|
187
|
-
beadId?: string,
|
|
188
|
-
config: AntiPatternConfig = DEFAULT_ANTI_PATTERN_CONFIG,
|
|
189
|
-
): { pattern: DecompositionPattern; inversion?: PatternInversionResult } {
|
|
190
|
-
// Update counts
|
|
191
|
-
const updated: DecompositionPattern = {
|
|
192
|
-
...pattern,
|
|
193
|
-
success_count: success ? pattern.success_count + 1 : pattern.success_count,
|
|
194
|
-
failure_count: success ? pattern.failure_count : pattern.failure_count + 1,
|
|
195
|
-
updated_at: new Date().toISOString(),
|
|
196
|
-
example_beads: beadId
|
|
197
|
-
? [...pattern.example_beads.slice(-(MAX_EXAMPLE_BEADS - 1)), beadId]
|
|
198
|
-
: pattern.example_beads,
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// Check if should invert
|
|
202
|
-
if (shouldInvertPattern(updated, config)) {
|
|
203
|
-
const total = updated.success_count + updated.failure_count;
|
|
204
|
-
const failureRatio = updated.failure_count / total;
|
|
205
|
-
const reason = `Failed ${updated.failure_count}/${total} times (${Math.round(failureRatio * 100)}% failure rate)`;
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
pattern: updated,
|
|
209
|
-
inversion: invertToAntiPattern(updated, reason, config),
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return { pattern: updated };
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Extract patterns from a decomposition description
|
|
218
|
-
*
|
|
219
|
-
* Looks for common decomposition strategies in the text.
|
|
220
|
-
*
|
|
221
|
-
* @param description - Decomposition description or reasoning
|
|
222
|
-
* @returns Extracted pattern descriptions
|
|
223
|
-
*/
|
|
224
|
-
export function extractPatternsFromDescription(description: string): string[] {
|
|
225
|
-
const patterns: string[] = [];
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Regex patterns for detecting common decomposition strategies.
|
|
229
|
-
*
|
|
230
|
-
* Detection is keyword-based and not exhaustive - patterns can be
|
|
231
|
-
* manually created for novel strategies not covered here.
|
|
232
|
-
*
|
|
233
|
-
* Each pattern maps a regex to a strategy name that will be extracted
|
|
234
|
-
* from task descriptions during pattern observation.
|
|
235
|
-
*/
|
|
236
|
-
const strategyPatterns: Array<{ regex: RegExp; pattern: string }> = [
|
|
237
|
-
{
|
|
238
|
-
regex: /split(?:ting)?\s+by\s+file\s+type/i,
|
|
239
|
-
pattern: "Split by file type",
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
regex: /split(?:ting)?\s+by\s+component/i,
|
|
243
|
-
pattern: "Split by component",
|
|
244
|
-
},
|
|
245
|
-
{
|
|
246
|
-
regex: /split(?:ting)?\s+by\s+layer/i,
|
|
247
|
-
pattern: "Split by layer (UI/logic/data)",
|
|
248
|
-
},
|
|
249
|
-
{ regex: /split(?:ting)?\s+by\s+feature/i, pattern: "Split by feature" },
|
|
250
|
-
{
|
|
251
|
-
regex: /one\s+file\s+per\s+(?:sub)?task/i,
|
|
252
|
-
pattern: "One file per subtask",
|
|
253
|
-
},
|
|
254
|
-
{ regex: /shared\s+types?\s+first/i, pattern: "Handle shared types first" },
|
|
255
|
-
{ regex: /api\s+(?:routes?)?\s+separate/i, pattern: "Separate API routes" },
|
|
256
|
-
{
|
|
257
|
-
regex: /tests?\s+(?:with|alongside)\s+(?:code|implementation)/i,
|
|
258
|
-
pattern: "Tests alongside implementation",
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
regex: /tests?\s+(?:in\s+)?separate\s+(?:sub)?task/i,
|
|
262
|
-
pattern: "Tests in separate subtask",
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
regex: /parallel(?:ize)?\s+(?:all|everything)/i,
|
|
266
|
-
pattern: "Maximize parallelization",
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
regex: /sequential\s+(?:order|execution)/i,
|
|
270
|
-
pattern: "Sequential execution order",
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
regex: /dependency\s+(?:chain|order)/i,
|
|
274
|
-
pattern: "Respect dependency chain",
|
|
275
|
-
},
|
|
276
|
-
];
|
|
277
|
-
|
|
278
|
-
for (const { regex, pattern } of strategyPatterns) {
|
|
279
|
-
if (regex.test(description)) {
|
|
280
|
-
patterns.push(pattern);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return patterns;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Create a new pattern from a description
|
|
289
|
-
*
|
|
290
|
-
* @param content - Pattern description
|
|
291
|
-
* @param tags - Optional tags for categorization
|
|
292
|
-
* @returns New pattern
|
|
293
|
-
*/
|
|
294
|
-
export function createPattern(
|
|
295
|
-
content: string,
|
|
296
|
-
tags: string[] = [],
|
|
297
|
-
): DecompositionPattern {
|
|
298
|
-
const now = new Date().toISOString();
|
|
299
|
-
return {
|
|
300
|
-
id: `pattern-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
301
|
-
content,
|
|
302
|
-
kind: "pattern",
|
|
303
|
-
is_negative: false,
|
|
304
|
-
success_count: 0,
|
|
305
|
-
failure_count: 0,
|
|
306
|
-
created_at: now,
|
|
307
|
-
updated_at: now,
|
|
308
|
-
tags,
|
|
309
|
-
example_beads: [],
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Format anti-patterns for inclusion in decomposition prompts
|
|
315
|
-
*
|
|
316
|
-
* @param patterns - Anti-patterns to format
|
|
317
|
-
* @returns Formatted string for prompt inclusion
|
|
318
|
-
*/
|
|
319
|
-
export function formatAntiPatternsForPrompt(
|
|
320
|
-
patterns: DecompositionPattern[],
|
|
321
|
-
): string {
|
|
322
|
-
const antiPatterns = patterns.filter((p) => p.kind === "anti_pattern");
|
|
323
|
-
|
|
324
|
-
if (antiPatterns.length === 0) {
|
|
325
|
-
return "";
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const lines = [
|
|
329
|
-
"## Anti-Patterns to Avoid",
|
|
330
|
-
"",
|
|
331
|
-
"Based on past failures, avoid these decomposition strategies:",
|
|
332
|
-
"",
|
|
333
|
-
...antiPatterns.map((p) => `- ${p.content}`),
|
|
334
|
-
"",
|
|
335
|
-
];
|
|
336
|
-
|
|
337
|
-
return lines.join("\n");
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Format successful patterns for inclusion in prompts.
|
|
342
|
-
*
|
|
343
|
-
* @param patterns - Array of decomposition patterns to filter and format
|
|
344
|
-
* @param minSuccessRate - Minimum success rate to include (default 0.7 = 70%).
|
|
345
|
-
* Chosen to filter out patterns with marginal track records - only patterns
|
|
346
|
-
* that succeed at least 70% of the time are recommended.
|
|
347
|
-
* @returns Formatted string of successful patterns for prompt injection
|
|
348
|
-
*/
|
|
349
|
-
export function formatSuccessfulPatternsForPrompt(
|
|
350
|
-
patterns: DecompositionPattern[],
|
|
351
|
-
minSuccessRate = 0.7,
|
|
352
|
-
): string {
|
|
353
|
-
const successful = patterns.filter((p) => {
|
|
354
|
-
if (p.kind === "anti_pattern") return false;
|
|
355
|
-
const total = p.success_count + p.failure_count;
|
|
356
|
-
if (total < 2) return false;
|
|
357
|
-
return p.success_count / total >= minSuccessRate;
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
if (successful.length === 0) {
|
|
361
|
-
return "";
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const lines = [
|
|
365
|
-
"## Successful Patterns",
|
|
366
|
-
"",
|
|
367
|
-
"These decomposition strategies have worked well in the past:",
|
|
368
|
-
"",
|
|
369
|
-
...successful.map((p) => {
|
|
370
|
-
const total = p.success_count + p.failure_count;
|
|
371
|
-
const rate = Math.round((p.success_count / total) * 100);
|
|
372
|
-
return `- ${p.content} (${rate}% success rate)`;
|
|
373
|
-
}),
|
|
374
|
-
"",
|
|
375
|
-
];
|
|
376
|
-
|
|
377
|
-
return lines.join("\n");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// ============================================================================
|
|
381
|
-
// Storage Interface
|
|
382
|
-
// ============================================================================
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Storage interface for decomposition patterns
|
|
386
|
-
*/
|
|
387
|
-
export interface PatternStorage {
|
|
388
|
-
/** Store or update a pattern */
|
|
389
|
-
store(pattern: DecompositionPattern): Promise<void>;
|
|
390
|
-
/** Get a pattern by ID */
|
|
391
|
-
get(id: string): Promise<DecompositionPattern | null>;
|
|
392
|
-
/** Get all patterns */
|
|
393
|
-
getAll(): Promise<DecompositionPattern[]>;
|
|
394
|
-
/** Get all anti-patterns */
|
|
395
|
-
getAntiPatterns(): Promise<DecompositionPattern[]>;
|
|
396
|
-
/** Get patterns by tag */
|
|
397
|
-
getByTag(tag: string): Promise<DecompositionPattern[]>;
|
|
398
|
-
/** Find patterns matching content */
|
|
399
|
-
findByContent(content: string): Promise<DecompositionPattern[]>;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* In-memory pattern storage (for testing and short-lived sessions)
|
|
404
|
-
*/
|
|
405
|
-
export class InMemoryPatternStorage implements PatternStorage {
|
|
406
|
-
private patterns: Map<string, DecompositionPattern> = new Map();
|
|
407
|
-
|
|
408
|
-
async store(pattern: DecompositionPattern): Promise<void> {
|
|
409
|
-
this.patterns.set(pattern.id, pattern);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
async get(id: string): Promise<DecompositionPattern | null> {
|
|
413
|
-
return this.patterns.get(id) ?? null;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async getAll(): Promise<DecompositionPattern[]> {
|
|
417
|
-
return Array.from(this.patterns.values());
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
async getAntiPatterns(): Promise<DecompositionPattern[]> {
|
|
421
|
-
return Array.from(this.patterns.values()).filter(
|
|
422
|
-
(p) => p.kind === "anti_pattern",
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
async getByTag(tag: string): Promise<DecompositionPattern[]> {
|
|
427
|
-
return Array.from(this.patterns.values()).filter((p) =>
|
|
428
|
-
p.tags.includes(tag),
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
async findByContent(content: string): Promise<DecompositionPattern[]> {
|
|
433
|
-
const lower = content.toLowerCase();
|
|
434
|
-
return Array.from(this.patterns.values()).filter((p) =>
|
|
435
|
-
p.content.toLowerCase().includes(lower),
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// ============================================================================
|
|
441
|
-
// Exports
|
|
442
|
-
// ============================================================================
|
|
443
|
-
|
|
444
|
-
export const antiPatternSchemas = {
|
|
445
|
-
PatternKindSchema,
|
|
446
|
-
DecompositionPatternSchema,
|
|
447
|
-
PatternInversionResultSchema,
|
|
448
|
-
};
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration test for compaction event capture
|
|
3
|
-
*
|
|
4
|
-
* Verifies that captureCompactionEvent writes events to session JSONL
|
|
5
|
-
* and that all event types are captured with correct data.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, expect, it, afterAll } from "bun:test";
|
|
9
|
-
import { existsSync, unlinkSync } from "node:fs";
|
|
10
|
-
import {
|
|
11
|
-
captureCompactionEvent,
|
|
12
|
-
readSessionEvents,
|
|
13
|
-
getSessionPath,
|
|
14
|
-
} from "./eval-capture";
|
|
15
|
-
|
|
16
|
-
describe("Compaction Event Capture Integration", () => {
|
|
17
|
-
const testSessionId = `test-compaction-${Date.now()}`;
|
|
18
|
-
const sessionPath = getSessionPath(testSessionId);
|
|
19
|
-
|
|
20
|
-
afterAll(() => {
|
|
21
|
-
// Clean up test session file
|
|
22
|
-
if (existsSync(sessionPath)) {
|
|
23
|
-
unlinkSync(sessionPath);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("captures detection_complete event with confidence and reasons", () => {
|
|
28
|
-
captureCompactionEvent({
|
|
29
|
-
session_id: testSessionId,
|
|
30
|
-
epic_id: "bd-test-123",
|
|
31
|
-
compaction_type: "detection_complete",
|
|
32
|
-
payload: {
|
|
33
|
-
confidence: "high",
|
|
34
|
-
detected: true,
|
|
35
|
-
reasons: ["3 cells in_progress", "2 open subtasks"],
|
|
36
|
-
session_scan_contributed: true,
|
|
37
|
-
session_scan_reasons: ["swarm tool calls found in session"],
|
|
38
|
-
epic_id: "bd-test-123",
|
|
39
|
-
epic_title: "Test Epic",
|
|
40
|
-
subtask_count: 5,
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Verify event was written to session file
|
|
45
|
-
expect(existsSync(sessionPath)).toBe(true);
|
|
46
|
-
|
|
47
|
-
// Read events from session
|
|
48
|
-
const events = readSessionEvents(testSessionId);
|
|
49
|
-
expect(events.length).toBe(1);
|
|
50
|
-
|
|
51
|
-
const event = events[0];
|
|
52
|
-
expect(event.session_id).toBe(testSessionId);
|
|
53
|
-
expect(event.epic_id).toBe("bd-test-123");
|
|
54
|
-
expect(event.event_type).toBe("COMPACTION");
|
|
55
|
-
expect(event.compaction_type).toBe("detection_complete");
|
|
56
|
-
|
|
57
|
-
// Verify payload structure
|
|
58
|
-
expect(event.payload.confidence).toBe("high");
|
|
59
|
-
expect(event.payload.detected).toBe(true);
|
|
60
|
-
expect(event.payload.reasons).toEqual(["3 cells in_progress", "2 open subtasks"]);
|
|
61
|
-
expect(event.payload.epic_id).toBe("bd-test-123");
|
|
62
|
-
expect(event.payload.epic_title).toBe("Test Epic");
|
|
63
|
-
expect(event.payload.subtask_count).toBe(5);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("captures prompt_generated event with FULL prompt content", () => {
|
|
67
|
-
const fullPrompt = `
|
|
68
|
-
┌─────────────────────────────────────────┐
|
|
69
|
-
│ 🐝 YOU ARE THE COORDINATOR 🐝 │
|
|
70
|
-
└─────────────────────────────────────────┘
|
|
71
|
-
|
|
72
|
-
# Swarm Continuation
|
|
73
|
-
|
|
74
|
-
**NON-NEGOTIABLE: YOU ARE THE COORDINATOR.**
|
|
75
|
-
|
|
76
|
-
## Epic State
|
|
77
|
-
**ID:** bd-epic-456
|
|
78
|
-
**Title:** Refactor authentication
|
|
79
|
-
**Status:** 2/5 subtasks complete
|
|
80
|
-
|
|
81
|
-
## Next Actions
|
|
82
|
-
1. Check swarm_status(epic_id="bd-epic-456")
|
|
83
|
-
2. Review completed work
|
|
84
|
-
3. Spawn remaining subtasks
|
|
85
|
-
`.trim();
|
|
86
|
-
|
|
87
|
-
captureCompactionEvent({
|
|
88
|
-
session_id: testSessionId,
|
|
89
|
-
epic_id: "bd-epic-456",
|
|
90
|
-
compaction_type: "prompt_generated",
|
|
91
|
-
payload: {
|
|
92
|
-
prompt_length: fullPrompt.length,
|
|
93
|
-
full_prompt: fullPrompt, // FULL content, not truncated
|
|
94
|
-
context_type: "llm_generated",
|
|
95
|
-
duration_ms: 1234,
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const events = readSessionEvents(testSessionId);
|
|
100
|
-
const promptEvent = events.find((e) => e.compaction_type === "prompt_generated");
|
|
101
|
-
|
|
102
|
-
expect(promptEvent).toBeDefined();
|
|
103
|
-
if (promptEvent) {
|
|
104
|
-
expect(promptEvent.payload.full_prompt).toBe(fullPrompt);
|
|
105
|
-
expect(promptEvent.payload.prompt_length).toBe(fullPrompt.length);
|
|
106
|
-
expect(promptEvent.payload.context_type).toBe("llm_generated");
|
|
107
|
-
expect(promptEvent.payload.duration_ms).toBe(1234);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("captures context_injected event with FULL content", () => {
|
|
112
|
-
const fullContent = `[Swarm compaction: LLM-generated, high confidence]
|
|
113
|
-
|
|
114
|
-
# 🐝 Swarm State
|
|
115
|
-
|
|
116
|
-
**Epic:** bd-epic-789 - Add user permissions
|
|
117
|
-
**Project:** /Users/test/project
|
|
118
|
-
|
|
119
|
-
**Subtasks:**
|
|
120
|
-
- 2 closed
|
|
121
|
-
- 1 in_progress
|
|
122
|
-
- 2 open
|
|
123
|
-
|
|
124
|
-
## COORDINATOR MANDATES
|
|
125
|
-
|
|
126
|
-
⛔ NEVER use edit/write directly - SPAWN A WORKER
|
|
127
|
-
✅ ALWAYS use swarm_spawn_subtask for implementation
|
|
128
|
-
✅ ALWAYS review with swarm_review
|
|
129
|
-
`;
|
|
130
|
-
|
|
131
|
-
captureCompactionEvent({
|
|
132
|
-
session_id: testSessionId,
|
|
133
|
-
epic_id: "bd-epic-789",
|
|
134
|
-
compaction_type: "context_injected",
|
|
135
|
-
payload: {
|
|
136
|
-
full_content: fullContent, // FULL content, not truncated
|
|
137
|
-
content_length: fullContent.length,
|
|
138
|
-
injection_method: "output.prompt",
|
|
139
|
-
context_type: "llm_generated",
|
|
140
|
-
},
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
const events = readSessionEvents(testSessionId);
|
|
144
|
-
const injectEvent = events.find((e) => e.compaction_type === "context_injected");
|
|
145
|
-
|
|
146
|
-
expect(injectEvent).toBeDefined();
|
|
147
|
-
if (injectEvent) {
|
|
148
|
-
expect(injectEvent.payload.full_content).toBe(fullContent);
|
|
149
|
-
expect(injectEvent.payload.content_length).toBe(fullContent.length);
|
|
150
|
-
expect(injectEvent.payload.injection_method).toBe("output.prompt");
|
|
151
|
-
expect(injectEvent.payload.context_type).toBe("llm_generated");
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("captures all three event types in sequence", () => {
|
|
156
|
-
const sequenceSessionId = `test-sequence-${Date.now()}`;
|
|
157
|
-
const sequencePath = getSessionPath(sequenceSessionId);
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
// Simulate compaction lifecycle
|
|
161
|
-
|
|
162
|
-
// 1. Detection
|
|
163
|
-
captureCompactionEvent({
|
|
164
|
-
session_id: sequenceSessionId,
|
|
165
|
-
epic_id: "bd-seq-123",
|
|
166
|
-
compaction_type: "detection_complete",
|
|
167
|
-
payload: {
|
|
168
|
-
confidence: "medium",
|
|
169
|
-
detected: true,
|
|
170
|
-
reasons: ["1 unclosed epic"],
|
|
171
|
-
},
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// 2. Prompt generation
|
|
175
|
-
captureCompactionEvent({
|
|
176
|
-
session_id: sequenceSessionId,
|
|
177
|
-
epic_id: "bd-seq-123",
|
|
178
|
-
compaction_type: "prompt_generated",
|
|
179
|
-
payload: {
|
|
180
|
-
full_prompt: "Test prompt content",
|
|
181
|
-
prompt_length: 19,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
// 3. Context injection
|
|
186
|
-
captureCompactionEvent({
|
|
187
|
-
session_id: sequenceSessionId,
|
|
188
|
-
epic_id: "bd-seq-123",
|
|
189
|
-
compaction_type: "context_injected",
|
|
190
|
-
payload: {
|
|
191
|
-
full_content: "Test context content",
|
|
192
|
-
content_length: 20,
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Verify all three events captured
|
|
197
|
-
const events = readSessionEvents(sequenceSessionId);
|
|
198
|
-
expect(events.length).toBe(3);
|
|
199
|
-
|
|
200
|
-
const types = events.map((e) => e.compaction_type);
|
|
201
|
-
expect(types).toContain("detection_complete");
|
|
202
|
-
expect(types).toContain("prompt_generated");
|
|
203
|
-
expect(types).toContain("context_injected");
|
|
204
|
-
|
|
205
|
-
// Verify order (chronological by timestamp)
|
|
206
|
-
const timestamps = events.map((e) => new Date(e.timestamp).getTime());
|
|
207
|
-
expect(timestamps[0]).toBeLessThanOrEqual(timestamps[1]);
|
|
208
|
-
expect(timestamps[1]).toBeLessThanOrEqual(timestamps[2]);
|
|
209
|
-
} finally {
|
|
210
|
-
// Clean up
|
|
211
|
-
if (existsSync(sequencePath)) {
|
|
212
|
-
unlinkSync(sequencePath);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it("validates event schema with Zod", () => {
|
|
218
|
-
// This should not throw - captureCompactionEvent validates internally
|
|
219
|
-
expect(() => {
|
|
220
|
-
captureCompactionEvent({
|
|
221
|
-
session_id: testSessionId,
|
|
222
|
-
epic_id: "bd-validate-123",
|
|
223
|
-
compaction_type: "detection_complete",
|
|
224
|
-
payload: { confidence: "high" },
|
|
225
|
-
});
|
|
226
|
-
}).not.toThrow();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("rejects invalid compaction_type", () => {
|
|
230
|
-
expect(() => {
|
|
231
|
-
captureCompactionEvent({
|
|
232
|
-
session_id: testSessionId,
|
|
233
|
-
epic_id: "bd-invalid-123",
|
|
234
|
-
// @ts-expect-error - intentionally invalid type
|
|
235
|
-
compaction_type: "invalid_type",
|
|
236
|
-
payload: {},
|
|
237
|
-
});
|
|
238
|
-
}).toThrow();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("handles empty epic_id gracefully", () => {
|
|
242
|
-
captureCompactionEvent({
|
|
243
|
-
session_id: testSessionId,
|
|
244
|
-
epic_id: "unknown",
|
|
245
|
-
compaction_type: "detection_complete",
|
|
246
|
-
payload: {
|
|
247
|
-
confidence: "none",
|
|
248
|
-
detected: false,
|
|
249
|
-
reasons: [],
|
|
250
|
-
},
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const events = readSessionEvents(testSessionId);
|
|
254
|
-
const unknownEvent = events.find((e) => e.epic_id === "unknown");
|
|
255
|
-
expect(unknownEvent).toBeDefined();
|
|
256
|
-
});
|
|
257
|
-
});
|