opencode-swarm-plugin 0.30.4 → 0.30.5
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/.turbo/turbo-test.log +300 -300
- package/CHANGELOG.md +52 -0
- package/bin/swarm.ts +34 -0
- package/package.json +1 -1
- package/src/model-selection.test.ts +188 -0
- package/src/model-selection.ts +68 -0
- package/src/schemas/task.ts +5 -0
- package/src/swarm-prompts.ts +28 -1
package/.turbo/turbo-test.log
CHANGED
|
@@ -2,180 +2,180 @@ $ bun test --timeout 10000 src/anti-patterns.test.ts src/mandate-promotion.test.
|
|
|
2
2
|
bun test v1.3.4 (5eb2145b)
|
|
3
3
|
|
|
4
4
|
src/structured.test.ts:
|
|
5
|
-
(pass) extractJsonFromText > Strategy 1: Direct parse > extracts clean JSON directly [
|
|
6
|
-
(pass) extractJsonFromText > Strategy 1: Direct parse > handles nested objects [0.
|
|
7
|
-
(pass) extractJsonFromText > Strategy 1: Direct parse > handles arrays [0.
|
|
8
|
-
(pass) extractJsonFromText > Strategy 1: Direct parse > handles complex nested structures [0.
|
|
9
|
-
(pass) extractJsonFromText > Strategy 2: JSON code block > extracts JSON from ```json code block [1.
|
|
10
|
-
(pass) extractJsonFromText > Strategy 2: JSON code block > handles code block with surrounding text [0.
|
|
5
|
+
(pass) extractJsonFromText > Strategy 1: Direct parse > extracts clean JSON directly [1.22ms]
|
|
6
|
+
(pass) extractJsonFromText > Strategy 1: Direct parse > handles nested objects [0.04ms]
|
|
7
|
+
(pass) extractJsonFromText > Strategy 1: Direct parse > handles arrays [0.06ms]
|
|
8
|
+
(pass) extractJsonFromText > Strategy 1: Direct parse > handles complex nested structures [0.09ms]
|
|
9
|
+
(pass) extractJsonFromText > Strategy 2: JSON code block > extracts JSON from ```json code block [1.03ms]
|
|
10
|
+
(pass) extractJsonFromText > Strategy 2: JSON code block > handles code block with surrounding text [0.01ms]
|
|
11
11
|
(pass) extractJsonFromText > Strategy 2: JSON code block > handles multiline JSON in code block [0.02ms]
|
|
12
12
|
(pass) extractJsonFromText > Strategy 3: Generic code block > extracts JSON from unlabeled code block [0.02ms]
|
|
13
13
|
(pass) extractJsonFromText > Strategy 3: Generic code block > prefers json-labeled block over generic block [0.02ms]
|
|
14
|
-
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > extracts JSON with surrounding text [0.
|
|
15
|
-
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > extracts first balanced object when multiple present [0.
|
|
16
|
-
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > handles deeply nested objects [0.
|
|
14
|
+
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > extracts JSON with surrounding text [0.01ms]
|
|
15
|
+
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > extracts first balanced object when multiple present [0.06ms]
|
|
16
|
+
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > handles deeply nested objects [0.02ms]
|
|
17
17
|
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > handles strings containing braces [0.01ms]
|
|
18
18
|
(pass) extractJsonFromText > Strategy 4: Brace matching for objects > handles escaped quotes in strings [0.01ms]
|
|
19
|
-
(pass) extractJsonFromText > Strategy 5: Bracket matching for arrays > extracts arrays with surrounding text [0.
|
|
19
|
+
(pass) extractJsonFromText > Strategy 5: Bracket matching for arrays > extracts arrays with surrounding text [0.01ms]
|
|
20
20
|
(pass) extractJsonFromText > Strategy 5: Bracket matching for arrays > handles nested arrays [0.02ms]
|
|
21
|
-
(pass) extractJsonFromText > Strategy 5: Bracket matching for arrays > prefers object matching over array when both present
|
|
22
|
-
(pass) extractJsonFromText > Strategy 6: JSON repair > fixes single quotes in keys (limited support) [
|
|
21
|
+
(pass) extractJsonFromText > Strategy 5: Bracket matching for arrays > prefers object matching over array when both present
|
|
22
|
+
(pass) extractJsonFromText > Strategy 6: JSON repair > fixes single quotes in keys (limited support) [1.20ms]
|
|
23
23
|
(pass) extractJsonFromText > Strategy 6: JSON repair > fixes trailing commas in objects [0.03ms]
|
|
24
24
|
(pass) extractJsonFromText > Strategy 6: JSON repair > fixes trailing commas in arrays [0.02ms]
|
|
25
25
|
(pass) extractJsonFromText > Strategy 6: JSON repair > fixes multiple trailing commas [0.02ms]
|
|
26
|
-
(pass) extractJsonFromText > Error cases > throws JsonExtractionError for invalid JSON [0.
|
|
26
|
+
(pass) extractJsonFromText > Error cases > throws JsonExtractionError for invalid JSON [0.02ms]
|
|
27
27
|
(pass) extractJsonFromText > Error cases > throws JsonExtractionError for empty input [0.05ms]
|
|
28
28
|
(pass) extractJsonFromText > Error cases > throws JsonExtractionError for unbalanced braces [0.02ms]
|
|
29
29
|
(pass) extractJsonFromText > Error cases > throws JsonExtractionError for malformed JSON [0.02ms]
|
|
30
|
-
(pass) extractJsonFromText > Error cases > includes attempted strategies in error [
|
|
31
|
-
(pass) extractJsonFromText > Error cases > returns null for deeply nested input exceeding MAX_BRACE_DEPTH [0.
|
|
32
|
-
(pass) formatZodErrors > formats single error with path [0.
|
|
33
|
-
(pass) formatZodErrors > formats multiple errors [0.
|
|
34
|
-
(pass) formatZodErrors > formats nested path [0.
|
|
35
|
-
(pass) formatZodErrors > formats error without path [0.
|
|
36
|
-
(pass) getSchemaByName > returns EvaluationSchema for 'evaluation' [0.
|
|
37
|
-
(pass) getSchemaByName > returns TaskDecompositionSchema for 'task_decomposition' [0.
|
|
30
|
+
(pass) extractJsonFromText > Error cases > includes attempted strategies in error [1.74ms]
|
|
31
|
+
(pass) extractJsonFromText > Error cases > returns null for deeply nested input exceeding MAX_BRACE_DEPTH [0.05ms]
|
|
32
|
+
(pass) formatZodErrors > formats single error with path [0.84ms]
|
|
33
|
+
(pass) formatZodErrors > formats multiple errors [0.56ms]
|
|
34
|
+
(pass) formatZodErrors > formats nested path [0.14ms]
|
|
35
|
+
(pass) formatZodErrors > formats error without path [0.02ms]
|
|
36
|
+
(pass) getSchemaByName > returns EvaluationSchema for 'evaluation' [0.03ms]
|
|
37
|
+
(pass) getSchemaByName > returns TaskDecompositionSchema for 'task_decomposition' [0.02ms]
|
|
38
38
|
(pass) getSchemaByName > returns CellTreeSchema for 'cell_tree'
|
|
39
|
-
(pass) getSchemaByName > throws error for unknown schema name
|
|
40
|
-
(pass) getSchemaByName > error message lists available schemas
|
|
41
|
-
(pass) StructuredValidationError > formats error bullets from ZodError [0.
|
|
42
|
-
(pass) StructuredValidationError > handles null ZodError
|
|
43
|
-
(pass) StructuredValidationError > includes extraction method when provided [0.
|
|
44
|
-
(pass) structured_extract_json > returns success for valid JSON [0.
|
|
45
|
-
(pass) structured_extract_json > returns success for JSON in code block [0.
|
|
46
|
-
(pass) structured_extract_json > returns error for invalid JSON [0.
|
|
47
|
-
(pass) structured_extract_json > includes raw input preview in error [0.
|
|
48
|
-
(pass) structured_validate > evaluation schema > validates correct evaluation [0.
|
|
49
|
-
(pass) structured_validate > evaluation schema > returns error for invalid evaluation [0.
|
|
39
|
+
(pass) getSchemaByName > throws error for unknown schema name
|
|
40
|
+
(pass) getSchemaByName > error message lists available schemas
|
|
41
|
+
(pass) StructuredValidationError > formats error bullets from ZodError [0.06ms]
|
|
42
|
+
(pass) StructuredValidationError > handles null ZodError
|
|
43
|
+
(pass) StructuredValidationError > includes extraction method when provided [0.02ms]
|
|
44
|
+
(pass) structured_extract_json > returns success for valid JSON [0.04ms]
|
|
45
|
+
(pass) structured_extract_json > returns success for JSON in code block [0.09ms]
|
|
46
|
+
(pass) structured_extract_json > returns error for invalid JSON [0.23ms]
|
|
47
|
+
(pass) structured_extract_json > includes raw input preview in error [0.04ms]
|
|
48
|
+
(pass) structured_validate > evaluation schema > validates correct evaluation [0.74ms]
|
|
49
|
+
(pass) structured_validate > evaluation schema > returns error for invalid evaluation [0.11ms]
|
|
50
50
|
(pass) structured_validate > evaluation schema > handles empty response [0.03ms]
|
|
51
|
-
(pass) structured_validate > evaluation schema > handles whitespace-only response [0.
|
|
52
|
-
(pass) structured_validate > task_decomposition schema > validates correct decomposition [0.
|
|
53
|
-
(pass) structured_validate > cell_tree schema > validates correct bead tree [0.
|
|
54
|
-
(pass) structured_validate > extracts JSON from markdown before validation [0.
|
|
51
|
+
(pass) structured_validate > evaluation schema > handles whitespace-only response [0.02ms]
|
|
52
|
+
(pass) structured_validate > task_decomposition schema > validates correct decomposition [0.54ms]
|
|
53
|
+
(pass) structured_validate > cell_tree schema > validates correct bead tree [0.38ms]
|
|
54
|
+
(pass) structured_validate > extracts JSON from markdown before validation [0.15ms]
|
|
55
55
|
(pass) structured_validate > includes retry hint when attempts < max_retries [0.05ms]
|
|
56
|
-
(pass) structured_parse_evaluation > parses valid evaluation [0.
|
|
57
|
-
(pass) structured_parse_evaluation > identifies failed criteria in summary [0.
|
|
58
|
-
(pass) structured_parse_evaluation > returns error for malformed JSON [0.
|
|
59
|
-
(pass) structured_parse_evaluation > returns error for invalid evaluation schema [0.
|
|
60
|
-
(pass) structured_parse_evaluation > includes expected shape in error feedback [0.
|
|
61
|
-
(pass) structured_parse_decomposition > parses valid decomposition [0.
|
|
62
|
-
(pass) structured_parse_decomposition > includes effort breakdown in summary [0.
|
|
63
|
-
(pass) structured_parse_decomposition > handles dependencies in summary [0.
|
|
64
|
-
(pass) structured_parse_decomposition > deduplicates files in summary [0.
|
|
65
|
-
(pass) structured_parse_decomposition > returns error for invalid decomposition [0.
|
|
66
|
-
(pass) structured_parse_cell_tree > parses valid bead tree [0.
|
|
67
|
-
(pass) structured_parse_cell_tree > calculates complexity total [0.
|
|
68
|
-
(pass) structured_parse_cell_tree > lists unique files [0.
|
|
69
|
-
(pass) structured_parse_cell_tree > returns error for invalid bead tree [0.
|
|
70
|
-
(pass) structured_parse_cell_tree > includes expected shape in error [0.
|
|
71
|
-
(pass) Edge cases > handles JSON with unicode characters [0.
|
|
72
|
-
(pass) Edge cases > handles very long strings in JSON [0.
|
|
73
|
-
(pass) Edge cases > handles JSON with null values
|
|
56
|
+
(pass) structured_parse_evaluation > parses valid evaluation [0.22ms]
|
|
57
|
+
(pass) structured_parse_evaluation > identifies failed criteria in summary [0.03ms]
|
|
58
|
+
(pass) structured_parse_evaluation > returns error for malformed JSON [0.09ms]
|
|
59
|
+
(pass) structured_parse_evaluation > returns error for invalid evaluation schema [0.06ms]
|
|
60
|
+
(pass) structured_parse_evaluation > includes expected shape in error feedback [0.06ms]
|
|
61
|
+
(pass) structured_parse_decomposition > parses valid decomposition [0.20ms]
|
|
62
|
+
(pass) structured_parse_decomposition > includes effort breakdown in summary [0.07ms]
|
|
63
|
+
(pass) structured_parse_decomposition > handles dependencies in summary [0.20ms]
|
|
64
|
+
(pass) structured_parse_decomposition > deduplicates files in summary [0.05ms]
|
|
65
|
+
(pass) structured_parse_decomposition > returns error for invalid decomposition [0.10ms]
|
|
66
|
+
(pass) structured_parse_cell_tree > parses valid bead tree [0.15ms]
|
|
67
|
+
(pass) structured_parse_cell_tree > calculates complexity total [0.06ms]
|
|
68
|
+
(pass) structured_parse_cell_tree > lists unique files [0.06ms]
|
|
69
|
+
(pass) structured_parse_cell_tree > returns error for invalid bead tree [0.09ms]
|
|
70
|
+
(pass) structured_parse_cell_tree > includes expected shape in error [0.04ms]
|
|
71
|
+
(pass) Edge cases > handles JSON with unicode characters [0.34ms]
|
|
72
|
+
(pass) Edge cases > handles very long strings in JSON [0.02ms]
|
|
73
|
+
(pass) Edge cases > handles JSON with null values
|
|
74
74
|
(pass) Edge cases > handles JSON with boolean values [0.01ms]
|
|
75
|
-
(pass) Edge cases > handles JSON with number types [0.
|
|
76
|
-
(pass) Edge cases > handles mixed content markdown with multiple code blocks [0.
|
|
77
|
-
(pass) Edge cases > handles JSON with escaped characters
|
|
75
|
+
(pass) Edge cases > handles JSON with number types [0.01ms]
|
|
76
|
+
(pass) Edge cases > handles mixed content markdown with multiple code blocks [0.02ms]
|
|
77
|
+
(pass) Edge cases > handles JSON with escaped characters
|
|
78
78
|
|
|
79
79
|
src/mandate-storage.test.ts:
|
|
80
|
-
(pass) InMemoryMandateStorage > Entry operations > should store and retrieve mandate entry [0.
|
|
81
|
-
(pass) InMemoryMandateStorage > Entry operations > should return null for non-existent mandate [0.
|
|
82
|
-
(pass) InMemoryMandateStorage > Entry operations > should find mandates by query
|
|
83
|
-
(pass) InMemoryMandateStorage > Entry operations > should list all mandates [0.
|
|
80
|
+
(pass) InMemoryMandateStorage > Entry operations > should store and retrieve mandate entry [0.58ms]
|
|
81
|
+
(pass) InMemoryMandateStorage > Entry operations > should return null for non-existent mandate [0.19ms]
|
|
82
|
+
(pass) InMemoryMandateStorage > Entry operations > should find mandates by query
|
|
83
|
+
(pass) InMemoryMandateStorage > Entry operations > should list all mandates [0.27ms]
|
|
84
84
|
(pass) InMemoryMandateStorage > Entry operations > should filter mandates by status [0.06ms]
|
|
85
|
-
(pass) InMemoryMandateStorage > Entry operations > should filter mandates by content_type [0.
|
|
86
|
-
(pass) InMemoryMandateStorage > Entry operations > should update mandate entry [0.
|
|
87
|
-
(pass) InMemoryMandateStorage > Entry operations > should throw when updating non-existent mandate [0.
|
|
88
|
-
(pass) InMemoryMandateStorage > Vote operations > should cast vote and verify storage [0.
|
|
89
|
-
(pass) InMemoryMandateStorage > Vote operations > should prevent duplicate votes from same agent [0.
|
|
90
|
-
(pass) InMemoryMandateStorage > Vote operations > should check if agent has voted [0.
|
|
91
|
-
(pass) InMemoryMandateStorage > Vote operations > should get all votes for a mandate [0.
|
|
92
|
-
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with no votes [0.
|
|
93
|
-
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with recent upvotes [0.
|
|
94
|
-
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with mixed votes [0.
|
|
95
|
-
(pass) InMemoryMandateStorage > Score calculation with decay > should apply decay to old votes [0.
|
|
96
|
-
(pass) InMemoryMandateStorage > Score calculation with decay > should handle vote weights [0.
|
|
97
|
-
(pass) InMemoryMandateStorage > Status updates based on score > should transition to mandate status with high score [0.
|
|
98
|
-
(pass) InMemoryMandateStorage > Status updates based on score > should transition to established status [0.
|
|
99
|
-
(pass) InMemoryMandateStorage > Status updates based on score > should transition to rejected status [0.
|
|
85
|
+
(pass) InMemoryMandateStorage > Entry operations > should filter mandates by content_type [0.03ms]
|
|
86
|
+
(pass) InMemoryMandateStorage > Entry operations > should update mandate entry [0.08ms]
|
|
87
|
+
(pass) InMemoryMandateStorage > Entry operations > should throw when updating non-existent mandate [0.01ms]
|
|
88
|
+
(pass) InMemoryMandateStorage > Vote operations > should cast vote and verify storage [0.08ms]
|
|
89
|
+
(pass) InMemoryMandateStorage > Vote operations > should prevent duplicate votes from same agent [0.16ms]
|
|
90
|
+
(pass) InMemoryMandateStorage > Vote operations > should check if agent has voted [0.01ms]
|
|
91
|
+
(pass) InMemoryMandateStorage > Vote operations > should get all votes for a mandate [0.04ms]
|
|
92
|
+
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with no votes [0.05ms]
|
|
93
|
+
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with recent upvotes [0.20ms]
|
|
94
|
+
(pass) InMemoryMandateStorage > Score calculation with decay > should calculate score with mixed votes [0.02ms]
|
|
95
|
+
(pass) InMemoryMandateStorage > Score calculation with decay > should apply decay to old votes [0.53ms]
|
|
96
|
+
(pass) InMemoryMandateStorage > Score calculation with decay > should handle vote weights [0.07ms]
|
|
97
|
+
(pass) InMemoryMandateStorage > Status updates based on score > should transition to mandate status with high score [0.05ms]
|
|
98
|
+
(pass) InMemoryMandateStorage > Status updates based on score > should transition to established status [0.14ms]
|
|
99
|
+
(pass) InMemoryMandateStorage > Status updates based on score > should transition to rejected status [0.08ms]
|
|
100
100
|
(pass) InMemoryMandateStorage > Status updates based on score > should batch update all mandates [0.08ms]
|
|
101
|
-
(pass) InMemoryMandateStorage > Factory > should create in-memory storage [0.
|
|
102
|
-
(pass) InMemoryMandateStorage > Factory > should throw on unknown backend [0.
|
|
101
|
+
(pass) InMemoryMandateStorage > Factory > should create in-memory storage [0.07ms]
|
|
102
|
+
(pass) InMemoryMandateStorage > Factory > should throw on unknown backend [0.09ms]
|
|
103
103
|
|
|
104
104
|
src/skills.test.ts:
|
|
105
|
-
(pass) parseFrontmatter > parses valid frontmatter with all fields [
|
|
106
|
-
(pass) parseFrontmatter > parses minimal frontmatter with only required fields [0.
|
|
107
|
-
(pass) parseFrontmatter > returns null for missing name value [0.
|
|
105
|
+
(pass) parseFrontmatter > parses valid frontmatter with all fields [1.80ms]
|
|
106
|
+
(pass) parseFrontmatter > parses minimal frontmatter with only required fields [0.11ms]
|
|
107
|
+
(pass) parseFrontmatter > returns null for missing name value [0.05ms]
|
|
108
108
|
(pass) parseFrontmatter > returns empty result for content without frontmatter [0.02ms]
|
|
109
109
|
(pass) parseFrontmatter > returns empty result for empty content
|
|
110
|
-
(pass) parseFrontmatter > handles frontmatter with extra fields [0.
|
|
111
|
-
(pass) parseFrontmatter > handles multiline description [0.
|
|
112
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
110
|
+
(pass) parseFrontmatter > handles frontmatter with extra fields [0.07ms]
|
|
111
|
+
(pass) parseFrontmatter > handles multiline description [0.05ms]
|
|
112
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
113
113
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
114
|
-
(pass) discoverSkills > discovers skills in project directory [
|
|
115
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
114
|
+
(pass) discoverSkills > discovers skills in project directory [13.27ms]
|
|
115
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
116
116
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
117
|
-
(pass) discoverSkills > skips skills with invalid frontmatter [
|
|
118
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
117
|
+
(pass) discoverSkills > skips skills with invalid frontmatter [3.73ms]
|
|
118
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
119
119
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
120
|
-
(pass) discoverSkills > caches discovered skills [
|
|
121
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
120
|
+
(pass) discoverSkills > caches discovered skills [9.01ms]
|
|
121
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
122
122
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
123
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
123
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
124
124
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
125
|
-
(pass) discoverSkills > invalidates cache when requested [
|
|
126
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
125
|
+
(pass) discoverSkills > invalidates cache when requested [21.20ms]
|
|
126
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
127
127
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
128
|
-
(pass) getSkill > returns skill by exact name [
|
|
129
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
128
|
+
(pass) getSkill > returns skill by exact name [5.98ms]
|
|
129
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
130
130
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
131
|
-
(pass) getSkill > returns null for non-existent skill [2.
|
|
132
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
131
|
+
(pass) getSkill > returns null for non-existent skill [2.31ms]
|
|
132
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
133
133
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
134
|
-
(pass) getSkill > returns null for empty name [2.
|
|
135
|
-
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-
|
|
134
|
+
(pass) getSkill > returns null for empty name [2.18ms]
|
|
135
|
+
[skills] Failed to load /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md: Skill at /Users/joel/Code/joelhooks/opencode-swarm-plugin/packages/opencode-swarm-plugin/.test-skills-1766104330148-9im82z/.opencode/skills/invalid-skill/SKILL.md missing required 'name' field
|
|
136
136
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
137
|
-
(pass) listSkills > returns skill refs with name, description, and path [2.
|
|
138
|
-
(pass) path traversal protection > detects basic path traversal attempts [0.
|
|
139
|
-
(pass) path traversal protection > allows valid relative paths [0.
|
|
140
|
-
(pass) path traversal protection > resolve + relative check catches encoded traversal
|
|
141
|
-
(pass) ES module compatibility > import.meta.url is available [0.
|
|
142
|
-
(pass) ES module compatibility > can construct path from import.meta.url [0.
|
|
143
|
-
(pass) validateCSOCompliance > description validation > passes for CSO-compliant description with 'Use when' [0.
|
|
137
|
+
(pass) listSkills > returns skill refs with name, description, and path [2.39ms]
|
|
138
|
+
(pass) path traversal protection > detects basic path traversal attempts [0.03ms]
|
|
139
|
+
(pass) path traversal protection > allows valid relative paths [0.05ms]
|
|
140
|
+
(pass) path traversal protection > resolve + relative check catches encoded traversal [0.02ms]
|
|
141
|
+
(pass) ES module compatibility > import.meta.url is available [0.08ms]
|
|
142
|
+
(pass) ES module compatibility > can construct path from import.meta.url [0.03ms]
|
|
143
|
+
(pass) validateCSOCompliance > description validation > passes for CSO-compliant description with 'Use when' [0.12ms]
|
|
144
144
|
(pass) validateCSOCompliance > description validation > warns when missing 'Use when...' pattern [0.02ms]
|
|
145
|
-
(pass) validateCSOCompliance > description validation > warns for first-person voice [0.
|
|
146
|
-
(pass) validateCSOCompliance > description validation > warns for second-person voice [0.
|
|
147
|
-
(pass) validateCSOCompliance > description validation > rejects description > 1024 chars [0.
|
|
148
|
-
(pass) validateCSOCompliance > description validation > suggests improvement for description > 500 chars [0.
|
|
149
|
-
(pass) validateCSOCompliance > description validation > accepts description < 500 chars with no length warnings [0.
|
|
145
|
+
(pass) validateCSOCompliance > description validation > warns for first-person voice [0.03ms]
|
|
146
|
+
(pass) validateCSOCompliance > description validation > warns for second-person voice [0.01ms]
|
|
147
|
+
(pass) validateCSOCompliance > description validation > rejects description > 1024 chars [0.01ms]
|
|
148
|
+
(pass) validateCSOCompliance > description validation > suggests improvement for description > 500 chars [0.06ms]
|
|
149
|
+
(pass) validateCSOCompliance > description validation > accepts description < 500 chars with no length warnings [0.01ms]
|
|
150
150
|
(pass) validateCSOCompliance > name validation > accepts gerund-based names
|
|
151
151
|
(pass) validateCSOCompliance > name validation > accepts verb-first names
|
|
152
|
-
(pass) validateCSOCompliance > name validation > accepts action verbs [0.
|
|
153
|
-
(pass) validateCSOCompliance > name validation > suggests verb-first for noun-first names [0.
|
|
152
|
+
(pass) validateCSOCompliance > name validation > accepts action verbs [0.02ms]
|
|
153
|
+
(pass) validateCSOCompliance > name validation > suggests verb-first for noun-first names [0.02ms]
|
|
154
154
|
(pass) validateCSOCompliance > name validation > warns for name > 64 chars [0.02ms]
|
|
155
155
|
(pass) validateCSOCompliance > name validation > warns for invalid name format [0.01ms]
|
|
156
156
|
(pass) validateCSOCompliance > comprehensive examples > perfect CSO compliance
|
|
157
|
-
(pass) validateCSOCompliance > comprehensive examples > multiple critical issues [0.
|
|
157
|
+
(pass) validateCSOCompliance > comprehensive examples > multiple critical issues [0.02ms]
|
|
158
158
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
159
|
-
(pass) edge cases > handles non-existent skills directory gracefully [1.
|
|
159
|
+
(pass) edge cases > handles non-existent skills directory gracefully [1.22ms]
|
|
160
160
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
161
|
-
(pass) edge cases > handles empty skills directory [1.
|
|
161
|
+
(pass) edge cases > handles empty skills directory [1.36ms]
|
|
162
162
|
[skills] Failed to load /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md: Skill at /Users/joel/.config/opencode/skills/ai-optimized-content/SKILL.md missing required 'name' field
|
|
163
|
-
(pass) edge cases > handles skill directory without SKILL.md [
|
|
163
|
+
(pass) edge cases > handles skill directory without SKILL.md [1.65ms]
|
|
164
164
|
|
|
165
165
|
src/anti-patterns.test.ts:
|
|
166
|
-
(pass) PatternKindSchema > validates 'pattern' kind
|
|
167
|
-
(pass) PatternKindSchema > validates 'anti_pattern' kind [0.
|
|
168
|
-
(pass) PatternKindSchema > rejects invalid kind [0.
|
|
169
|
-
(pass) DecompositionPatternSchema > validates a complete valid pattern [0.
|
|
170
|
-
(pass) DecompositionPatternSchema > validates a valid anti-pattern [0.
|
|
171
|
-
(pass) DecompositionPatternSchema > applies default values for optional fields [0.
|
|
172
|
-
(pass) DecompositionPatternSchema > rejects negative success_count [0.
|
|
173
|
-
(pass) DecompositionPatternSchema > rejects negative failure_count [0.
|
|
174
|
-
(pass) DecompositionPatternSchema > rejects invalid kind [0.
|
|
166
|
+
(pass) PatternKindSchema > validates 'pattern' kind
|
|
167
|
+
(pass) PatternKindSchema > validates 'anti_pattern' kind [0.38ms]
|
|
168
|
+
(pass) PatternKindSchema > rejects invalid kind [0.08ms]
|
|
169
|
+
(pass) DecompositionPatternSchema > validates a complete valid pattern [0.17ms]
|
|
170
|
+
(pass) DecompositionPatternSchema > validates a valid anti-pattern [0.21ms]
|
|
171
|
+
(pass) DecompositionPatternSchema > applies default values for optional fields [0.03ms]
|
|
172
|
+
(pass) DecompositionPatternSchema > rejects negative success_count [0.05ms]
|
|
173
|
+
(pass) DecompositionPatternSchema > rejects negative failure_count [0.06ms]
|
|
174
|
+
(pass) DecompositionPatternSchema > rejects invalid kind [0.06ms]
|
|
175
175
|
(pass) PatternInversionResultSchema > validates a complete inversion result [0.14ms]
|
|
176
176
|
(pass) shouldInvertPattern > returns true when failure rate exceeds 60% [0.02ms]
|
|
177
|
-
(pass) shouldInvertPattern > returns true when failure rate equals 60% [0.
|
|
178
|
-
(pass) shouldInvertPattern > returns false when failure rate is below 60%
|
|
177
|
+
(pass) shouldInvertPattern > returns true when failure rate equals 60% [0.02ms]
|
|
178
|
+
(pass) shouldInvertPattern > returns false when failure rate is below 60% [0.01ms]
|
|
179
179
|
(pass) shouldInvertPattern > returns false when failure rate is just below threshold
|
|
180
180
|
(pass) shouldInvertPattern > returns false with insufficient observations (< minObservations)
|
|
181
181
|
(pass) shouldInvertPattern > returns false when exactly at minObservations but low failure rate
|
|
@@ -184,290 +184,290 @@ src/anti-patterns.test.ts:
|
|
|
184
184
|
(pass) shouldInvertPattern > returns false with zero observations
|
|
185
185
|
(pass) shouldInvertPattern > respects custom config minObservations
|
|
186
186
|
(pass) shouldInvertPattern > respects custom config failureRatioThreshold [0.01ms]
|
|
187
|
-
(pass) invertToAntiPattern > converts pattern to anti-pattern with correct kind [0.
|
|
188
|
-
(pass) invertToAntiPattern > prefixes content with AVOID: [0.
|
|
189
|
-
(pass) invertToAntiPattern > appends reason to content
|
|
187
|
+
(pass) invertToAntiPattern > converts pattern to anti-pattern with correct kind [0.02ms]
|
|
188
|
+
(pass) invertToAntiPattern > prefixes content with AVOID: [0.05ms]
|
|
189
|
+
(pass) invertToAntiPattern > appends reason to content
|
|
190
190
|
(pass) invertToAntiPattern > preserves success and failure counts
|
|
191
191
|
(pass) invertToAntiPattern > preserves example_beads [0.01ms]
|
|
192
192
|
(pass) invertToAntiPattern > preserves tags
|
|
193
193
|
(pass) invertToAntiPattern > generates new ID with 'anti-' prefix
|
|
194
194
|
(pass) invertToAntiPattern > stores reason in inverted pattern
|
|
195
|
-
(pass) invertToAntiPattern > updates updated_at timestamp
|
|
196
|
-
(pass) invertToAntiPattern > returns original pattern in result
|
|
195
|
+
(pass) invertToAntiPattern > updates updated_at timestamp
|
|
196
|
+
(pass) invertToAntiPattern > returns original pattern in result [0.02ms]
|
|
197
197
|
(pass) invertToAntiPattern > returns reason in result
|
|
198
|
-
(pass) invertToAntiPattern > cleans existing AVOID: prefix
|
|
199
|
-
(pass) invertToAntiPattern > cleans existing DO NOT: prefix [0.
|
|
198
|
+
(pass) invertToAntiPattern > cleans existing AVOID: prefix
|
|
199
|
+
(pass) invertToAntiPattern > cleans existing DO NOT: prefix [0.03ms]
|
|
200
200
|
(pass) invertToAntiPattern > cleans existing NEVER: prefix [0.01ms]
|
|
201
|
-
(pass) invertToAntiPattern > respects custom antiPatternPrefix
|
|
202
|
-
(pass) recordPatternObservation > increments success count on success [0.
|
|
203
|
-
(pass) recordPatternObservation > increments failure count on failure [0.
|
|
201
|
+
(pass) invertToAntiPattern > respects custom antiPatternPrefix [0.06ms]
|
|
202
|
+
(pass) recordPatternObservation > increments success count on success [0.01ms]
|
|
203
|
+
(pass) recordPatternObservation > increments failure count on failure [0.05ms]
|
|
204
204
|
(pass) recordPatternObservation > adds bead to example_beads when provided
|
|
205
205
|
(pass) recordPatternObservation > does not modify example_beads when beadId not provided
|
|
206
|
-
(pass) recordPatternObservation > limits example_beads to MAX_EXAMPLE_BEADS (10) [0.
|
|
207
|
-
(pass) recordPatternObservation > keeps newest beads when trimming example_beads [0.
|
|
208
|
-
(pass) recordPatternObservation > updates updated_at timestamp [0.
|
|
209
|
-
(pass) recordPatternObservation > does not invert when below threshold
|
|
210
|
-
(pass) recordPatternObservation > inverts when crossing threshold [0.
|
|
211
|
-
(pass) recordPatternObservation > includes failure statistics in inversion reason
|
|
206
|
+
(pass) recordPatternObservation > limits example_beads to MAX_EXAMPLE_BEADS (10) [0.01ms]
|
|
207
|
+
(pass) recordPatternObservation > keeps newest beads when trimming example_beads [0.03ms]
|
|
208
|
+
(pass) recordPatternObservation > updates updated_at timestamp [0.01ms]
|
|
209
|
+
(pass) recordPatternObservation > does not invert when below threshold [0.01ms]
|
|
210
|
+
(pass) recordPatternObservation > inverts when crossing threshold [0.02ms]
|
|
211
|
+
(pass) recordPatternObservation > includes failure statistics in inversion reason
|
|
212
212
|
(pass) recordPatternObservation > does not invert already-inverted anti-patterns [0.01ms]
|
|
213
|
-
(pass) recordPatternObservation > respects custom config for inversion [0.
|
|
213
|
+
(pass) recordPatternObservation > respects custom config for inversion [0.01ms]
|
|
214
214
|
(pass) recordPatternObservation > preserves original pattern fields [0.01ms]
|
|
215
|
-
(pass) extractPatternsFromDescription > detects 'split by file type' pattern [0.
|
|
216
|
-
(pass) extractPatternsFromDescription > detects 'splitting by file type' variant [0.
|
|
215
|
+
(pass) extractPatternsFromDescription > detects 'split by file type' pattern [0.10ms]
|
|
216
|
+
(pass) extractPatternsFromDescription > detects 'splitting by file type' variant [0.05ms]
|
|
217
217
|
(pass) extractPatternsFromDescription > detects 'split by component' pattern
|
|
218
218
|
(pass) extractPatternsFromDescription > detects 'split by layer' pattern
|
|
219
219
|
(pass) extractPatternsFromDescription > detects 'split by feature' pattern
|
|
220
220
|
(pass) extractPatternsFromDescription > detects 'one file per task' pattern
|
|
221
221
|
(pass) extractPatternsFromDescription > detects 'shared types first' pattern
|
|
222
|
-
(pass) extractPatternsFromDescription > detects 'API routes separate' pattern
|
|
223
|
-
(pass) extractPatternsFromDescription > detects 'tests with code' pattern
|
|
224
|
-
(pass) extractPatternsFromDescription > detects 'tests in separate subtask' pattern
|
|
225
|
-
(pass) extractPatternsFromDescription > detects 'parallelize all' pattern
|
|
222
|
+
(pass) extractPatternsFromDescription > detects 'API routes separate' pattern
|
|
223
|
+
(pass) extractPatternsFromDescription > detects 'tests with code' pattern
|
|
224
|
+
(pass) extractPatternsFromDescription > detects 'tests in separate subtask' pattern
|
|
225
|
+
(pass) extractPatternsFromDescription > detects 'parallelize all' pattern
|
|
226
226
|
(pass) extractPatternsFromDescription > detects 'sequential order' pattern
|
|
227
227
|
(pass) extractPatternsFromDescription > detects 'dependency chain' pattern
|
|
228
228
|
(pass) extractPatternsFromDescription > returns empty array for unrecognized descriptions
|
|
229
229
|
(pass) extractPatternsFromDescription > detects multiple patterns in one description [0.01ms]
|
|
230
230
|
(pass) extractPatternsFromDescription > is case-insensitive
|
|
231
231
|
(pass) extractPatternsFromDescription > handles partial matches in longer sentences
|
|
232
|
-
(pass) createPattern > creates pattern with provided content [0.
|
|
232
|
+
(pass) createPattern > creates pattern with provided content [0.01ms]
|
|
233
233
|
(pass) createPattern > creates pattern with kind='pattern' [0.02ms]
|
|
234
234
|
(pass) createPattern > creates pattern with is_negative=false
|
|
235
235
|
(pass) createPattern > initializes counts to zero
|
|
236
|
-
(pass) createPattern > includes provided tags
|
|
236
|
+
(pass) createPattern > includes provided tags [0.01ms]
|
|
237
237
|
(pass) createPattern > defaults to empty tags array
|
|
238
238
|
(pass) createPattern > generates unique ID
|
|
239
239
|
(pass) createPattern > sets created_at timestamp [0.01ms]
|
|
240
240
|
(pass) createPattern > sets updated_at equal to created_at
|
|
241
241
|
(pass) createPattern > initializes example_beads to empty array
|
|
242
|
-
(pass) formatAntiPatternsForPrompt > formats anti-patterns with header [0.
|
|
243
|
-
(pass) formatAntiPatternsForPrompt > filters out non-anti-patterns [0.
|
|
244
|
-
(pass) formatAntiPatternsForPrompt > returns empty string when no anti-patterns [0.
|
|
242
|
+
(pass) formatAntiPatternsForPrompt > formats anti-patterns with header [0.01ms]
|
|
243
|
+
(pass) formatAntiPatternsForPrompt > filters out non-anti-patterns [0.03ms]
|
|
244
|
+
(pass) formatAntiPatternsForPrompt > returns empty string when no anti-patterns [0.02ms]
|
|
245
245
|
(pass) formatAntiPatternsForPrompt > returns empty string for empty array [0.02ms]
|
|
246
|
-
(pass) formatAntiPatternsForPrompt > formats multiple anti-patterns
|
|
247
|
-
(pass) formatSuccessfulPatternsForPrompt > filters patterns below minSuccessRate [0.
|
|
248
|
-
(pass) formatSuccessfulPatternsForPrompt > includes success rate percentage in output [0.
|
|
249
|
-
(pass) formatSuccessfulPatternsForPrompt > filters out anti-patterns
|
|
246
|
+
(pass) formatAntiPatternsForPrompt > formats multiple anti-patterns
|
|
247
|
+
(pass) formatSuccessfulPatternsForPrompt > filters patterns below minSuccessRate [0.06ms]
|
|
248
|
+
(pass) formatSuccessfulPatternsForPrompt > includes success rate percentage in output [0.02ms]
|
|
249
|
+
(pass) formatSuccessfulPatternsForPrompt > filters out anti-patterns [0.01ms]
|
|
250
250
|
(pass) formatSuccessfulPatternsForPrompt > filters out patterns with < 2 total observations [0.01ms]
|
|
251
|
-
(pass) formatSuccessfulPatternsForPrompt > returns empty string when no qualifying patterns [0.
|
|
251
|
+
(pass) formatSuccessfulPatternsForPrompt > returns empty string when no qualifying patterns [0.02ms]
|
|
252
252
|
(pass) formatSuccessfulPatternsForPrompt > returns empty string for empty array
|
|
253
|
-
(pass) formatSuccessfulPatternsForPrompt > uses default minSuccessRate of 0.7
|
|
253
|
+
(pass) formatSuccessfulPatternsForPrompt > uses default minSuccessRate of 0.7 [0.01ms]
|
|
254
254
|
(pass) formatSuccessfulPatternsForPrompt > respects custom minSuccessRate
|
|
255
255
|
(pass) formatSuccessfulPatternsForPrompt > formats multiple successful patterns [0.01ms]
|
|
256
256
|
(pass) formatSuccessfulPatternsForPrompt > includes header when patterns exist [0.02ms]
|
|
257
|
-
(pass) InMemoryPatternStorage > stores and retrieves a pattern [0.
|
|
258
|
-
(pass) InMemoryPatternStorage > returns null for non-existent pattern [0.
|
|
259
|
-
(pass) InMemoryPatternStorage > updates existing pattern on store [0.
|
|
260
|
-
(pass) InMemoryPatternStorage > getAll returns all stored patterns [0.
|
|
261
|
-
(pass) InMemoryPatternStorage > getAll returns empty array when no patterns [0.
|
|
262
|
-
(pass) InMemoryPatternStorage > getAntiPatterns filters by kind [0.
|
|
263
|
-
(pass) InMemoryPatternStorage > getByTag filters by tag [0.
|
|
257
|
+
(pass) InMemoryPatternStorage > stores and retrieves a pattern [0.04ms]
|
|
258
|
+
(pass) InMemoryPatternStorage > returns null for non-existent pattern [0.06ms]
|
|
259
|
+
(pass) InMemoryPatternStorage > updates existing pattern on store [0.02ms]
|
|
260
|
+
(pass) InMemoryPatternStorage > getAll returns all stored patterns [0.03ms]
|
|
261
|
+
(pass) InMemoryPatternStorage > getAll returns empty array when no patterns [0.09ms]
|
|
262
|
+
(pass) InMemoryPatternStorage > getAntiPatterns filters by kind [0.01ms]
|
|
263
|
+
(pass) InMemoryPatternStorage > getByTag filters by tag [0.10ms]
|
|
264
264
|
(pass) InMemoryPatternStorage > getByTag returns empty for non-existent tag [0.02ms]
|
|
265
|
-
(pass) InMemoryPatternStorage > findByContent finds patterns by substring [0.
|
|
266
|
-
(pass) InMemoryPatternStorage > findByContent is case-insensitive [0.
|
|
265
|
+
(pass) InMemoryPatternStorage > findByContent finds patterns by substring [0.05ms]
|
|
266
|
+
(pass) InMemoryPatternStorage > findByContent is case-insensitive [0.04ms]
|
|
267
267
|
(pass) InMemoryPatternStorage > findByContent returns empty for no matches [0.02ms]
|
|
268
268
|
|
|
269
269
|
src/output-guardrails.test.ts:
|
|
270
|
-
(pass) truncateWithBoundaries > returns unchanged text when under limit [0.
|
|
271
|
-
(pass) truncateWithBoundaries > preserves complete JSON objects [7.
|
|
272
|
-
(pass) truncateWithBoundaries > preserves nested JSON structure [0.
|
|
273
|
-
(pass) truncateWithBoundaries > preserves code block boundaries [0.
|
|
274
|
-
(pass) truncateWithBoundaries > preserves markdown header boundaries [0.
|
|
275
|
-
(pass) truncateWithBoundaries > handles text without structure boundaries [0.
|
|
276
|
-
(pass) truncateWithBoundaries > adds truncation suffix with character count [0.
|
|
270
|
+
(pass) truncateWithBoundaries > returns unchanged text when under limit [0.08ms]
|
|
271
|
+
(pass) truncateWithBoundaries > preserves complete JSON objects [7.20ms]
|
|
272
|
+
(pass) truncateWithBoundaries > preserves nested JSON structure [0.21ms]
|
|
273
|
+
(pass) truncateWithBoundaries > preserves code block boundaries [0.11ms]
|
|
274
|
+
(pass) truncateWithBoundaries > preserves markdown header boundaries [0.02ms]
|
|
275
|
+
(pass) truncateWithBoundaries > handles text without structure boundaries [0.04ms]
|
|
276
|
+
(pass) truncateWithBoundaries > adds truncation suffix with character count [0.01ms]
|
|
277
277
|
(pass) truncateWithBoundaries > avoids truncating mid-word [0.04ms]
|
|
278
|
-
(pass) truncateWithBoundaries > handles empty string [0.
|
|
279
|
-
(pass) truncateWithBoundaries > handles exact limit length
|
|
280
|
-
(pass) truncateWithBoundaries > handles just over limit
|
|
278
|
+
(pass) truncateWithBoundaries > handles empty string [0.05ms]
|
|
279
|
+
(pass) truncateWithBoundaries > handles exact limit length
|
|
280
|
+
(pass) truncateWithBoundaries > handles just over limit
|
|
281
281
|
(pass) truncateWithBoundaries > extends limit by 20% to include matching braces [0.02ms]
|
|
282
|
-
(pass) truncateWithBoundaries > extends limit by 20% to include closing code block [0.
|
|
283
|
-
(pass) guardrailOutput > skips configured tools [0.
|
|
284
|
-
(pass) guardrailOutput > truncates oversized output for non-skip tools [0.
|
|
285
|
-
(pass) guardrailOutput > respects per-tool limits [0.
|
|
286
|
-
(pass) guardrailOutput > uses custom config when provided [0.
|
|
287
|
-
(pass) guardrailOutput > returns complete metadata [0.
|
|
282
|
+
(pass) truncateWithBoundaries > extends limit by 20% to include closing code block [0.01ms]
|
|
283
|
+
(pass) guardrailOutput > skips configured tools [0.01ms]
|
|
284
|
+
(pass) guardrailOutput > truncates oversized output for non-skip tools [0.61ms]
|
|
285
|
+
(pass) guardrailOutput > respects per-tool limits [0.15ms]
|
|
286
|
+
(pass) guardrailOutput > uses custom config when provided [0.02ms]
|
|
287
|
+
(pass) guardrailOutput > returns complete metadata [0.45ms]
|
|
288
288
|
(pass) guardrailOutput > handles all skip tools from DEFAULT_GUARDRAIL_CONFIG [0.05ms]
|
|
289
|
-
(pass) createMetrics > creates metrics entry from guardrail result [0.
|
|
290
|
-
(pass) createMetrics > timestamp is reasonable [0.
|
|
291
|
-
(pass) DEFAULT_GUARDRAIL_CONFIG > has sensible defaults [0.
|
|
292
|
-
(pass) DEFAULT_GUARDRAIL_CONFIG > includes higher limits for code/doc tools
|
|
289
|
+
(pass) createMetrics > creates metrics entry from guardrail result [0.29ms]
|
|
290
|
+
(pass) createMetrics > timestamp is reasonable [0.01ms]
|
|
291
|
+
(pass) DEFAULT_GUARDRAIL_CONFIG > has sensible defaults [0.01ms]
|
|
292
|
+
(pass) DEFAULT_GUARDRAIL_CONFIG > includes higher limits for code/doc tools
|
|
293
293
|
(pass) DEFAULT_GUARDRAIL_CONFIG > includes lower limits for stats tools
|
|
294
|
-
(pass) DEFAULT_GUARDRAIL_CONFIG > skips all internal coordination tools [0.
|
|
295
|
-
(pass) edge cases > handles JSON array at truncation boundary [0.
|
|
296
|
-
(pass) edge cases > handles mixed code blocks and JSON [0.
|
|
294
|
+
(pass) DEFAULT_GUARDRAIL_CONFIG > skips all internal coordination tools [0.01ms]
|
|
295
|
+
(pass) edge cases > handles JSON array at truncation boundary [0.20ms]
|
|
296
|
+
(pass) edge cases > handles mixed code blocks and JSON [0.02ms]
|
|
297
297
|
(pass) edge cases > handles unicode characters correctly [0.03ms]
|
|
298
|
-
(pass) edge cases > handles CRLF line endings [0.
|
|
298
|
+
(pass) edge cases > handles CRLF line endings [0.03ms]
|
|
299
299
|
|
|
300
300
|
src/pattern-maturity.test.ts:
|
|
301
|
-
(pass) calculateDecayedCounts > returns zero counts for empty feedback [0.
|
|
302
|
-
(pass) calculateDecayedCounts > counts recent helpful feedback at full weight [0.
|
|
303
|
-
(pass) calculateDecayedCounts > counts recent harmful feedback at full weight [0.
|
|
301
|
+
(pass) calculateDecayedCounts > returns zero counts for empty feedback [0.08ms]
|
|
302
|
+
(pass) calculateDecayedCounts > counts recent helpful feedback at full weight [0.04ms]
|
|
303
|
+
(pass) calculateDecayedCounts > counts recent harmful feedback at full weight [0.03ms]
|
|
304
304
|
(pass) calculateDecayedCounts > applies decay to old feedback [0.04ms]
|
|
305
305
|
(pass) calculateDecayedCounts > handles mixed feedback types [0.02ms]
|
|
306
|
-
(pass) calculateDecayedCounts > respects weight parameter [0.
|
|
306
|
+
(pass) calculateDecayedCounts > respects weight parameter [0.03ms]
|
|
307
307
|
(pass) calculateDecayedCounts > uses custom config half-life [0.02ms]
|
|
308
308
|
(pass) calculateDecayedCounts > uses custom now parameter for decay calculation [0.02ms]
|
|
309
|
-
(pass) calculateMaturityState > returns candidate with no feedback [0.
|
|
310
|
-
(pass) calculateMaturityState > returns candidate with insufficient feedback [0.
|
|
311
|
-
(pass) calculateMaturityState > returns established with enough neutral feedback [0.
|
|
312
|
-
(pass) calculateMaturityState > returns proven with strong positive feedback [0.
|
|
313
|
-
(pass) calculateMaturityState > returns deprecated with high harmful ratio
|
|
314
|
-
(pass) calculateMaturityState > proven requires minimum helpful count
|
|
315
|
-
(pass) calculateMaturityState > proven requires low harmful ratio [0.
|
|
316
|
-
(pass) calculateMaturityState > deprecation takes priority over proven [0.
|
|
309
|
+
(pass) calculateMaturityState > returns candidate with no feedback [0.01ms]
|
|
310
|
+
(pass) calculateMaturityState > returns candidate with insufficient feedback [0.03ms]
|
|
311
|
+
(pass) calculateMaturityState > returns established with enough neutral feedback [0.02ms]
|
|
312
|
+
(pass) calculateMaturityState > returns proven with strong positive feedback [0.01ms]
|
|
313
|
+
(pass) calculateMaturityState > returns deprecated with high harmful ratio [0.03ms]
|
|
314
|
+
(pass) calculateMaturityState > proven requires minimum helpful count
|
|
315
|
+
(pass) calculateMaturityState > proven requires low harmful ratio [0.03ms]
|
|
316
|
+
(pass) calculateMaturityState > deprecation takes priority over proven [0.02ms]
|
|
317
317
|
(pass) calculateMaturityState > uses custom config thresholds [0.03ms]
|
|
318
318
|
(pass) calculateMaturityState > accounts for decay in state calculation [0.01ms]
|
|
319
319
|
(pass) createPatternMaturity > creates initial maturity in candidate state [0.02ms]
|
|
320
|
-
(pass) createPatternMaturity > sets last_validated timestamp
|
|
320
|
+
(pass) createPatternMaturity > sets last_validated timestamp
|
|
321
321
|
(pass) createPatternMaturity > does not set promoted_at or deprecated_at initially [0.01ms]
|
|
322
|
-
(pass) updatePatternMaturity > updates state based on feedback [0.
|
|
323
|
-
(pass) updatePatternMaturity > updates helpful and harmful counts [0.
|
|
322
|
+
(pass) updatePatternMaturity > updates state based on feedback [0.05ms]
|
|
323
|
+
(pass) updatePatternMaturity > updates helpful and harmful counts [0.02ms]
|
|
324
324
|
(pass) updatePatternMaturity > sets promoted_at on first transition to proven [0.03ms]
|
|
325
325
|
(pass) updatePatternMaturity > does not update promoted_at if already proven [0.03ms]
|
|
326
326
|
(pass) updatePatternMaturity > sets deprecated_at on first transition to deprecated [0.02ms]
|
|
327
|
-
(pass) updatePatternMaturity > does not update deprecated_at if already deprecated [0.
|
|
327
|
+
(pass) updatePatternMaturity > does not update deprecated_at if already deprecated [0.02ms]
|
|
328
328
|
(pass) updatePatternMaturity > updates last_validated timestamp [0.02ms]
|
|
329
|
-
(pass) updatePatternMaturity > handles state transitions: candidate -> established [0.
|
|
329
|
+
(pass) updatePatternMaturity > handles state transitions: candidate -> established [0.02ms]
|
|
330
330
|
(pass) updatePatternMaturity > handles state transitions: established -> proven [0.04ms]
|
|
331
|
-
(pass) updatePatternMaturity > handles state transitions: proven -> deprecated [0.
|
|
332
|
-
(pass) updatePatternMaturity > handles empty feedback array
|
|
331
|
+
(pass) updatePatternMaturity > handles state transitions: proven -> deprecated [0.04ms]
|
|
332
|
+
(pass) updatePatternMaturity > handles empty feedback array
|
|
333
333
|
[PatternMaturity] Promoting candidate with insufficient data: test-pattern (0 helpful observations)
|
|
334
334
|
(pass) promotePattern > promotes candidate to proven [0.03ms]
|
|
335
|
-
(pass) promotePattern > promotes established to proven
|
|
335
|
+
(pass) promotePattern > promotes established to proven
|
|
336
336
|
[PatternMaturity] Promoting candidate with insufficient data: test-pattern (0 helpful observations)
|
|
337
|
-
(pass) promotePattern > sets promoted_at timestamp [0.
|
|
337
|
+
(pass) promotePattern > sets promoted_at timestamp [0.02ms]
|
|
338
338
|
[PatternMaturity] Promoting candidate with insufficient data: test-pattern (0 helpful observations)
|
|
339
|
-
(pass) promotePattern > updates last_validated timestamp [0.
|
|
339
|
+
(pass) promotePattern > updates last_validated timestamp [0.01ms]
|
|
340
340
|
(pass) promotePattern > throws error when promoting deprecated pattern [0.03ms]
|
|
341
341
|
[PatternMaturity] Pattern already proven: test-pattern
|
|
342
|
-
(pass) promotePattern > returns unchanged maturity when already proven [0.
|
|
343
|
-
(pass) deprecatePattern > deprecates candidate pattern [0.
|
|
344
|
-
(pass) deprecatePattern > deprecates established pattern
|
|
345
|
-
(pass) deprecatePattern > deprecates proven pattern
|
|
342
|
+
(pass) promotePattern > returns unchanged maturity when already proven [0.05ms]
|
|
343
|
+
(pass) deprecatePattern > deprecates candidate pattern [0.03ms]
|
|
344
|
+
(pass) deprecatePattern > deprecates established pattern [0.02ms]
|
|
345
|
+
(pass) deprecatePattern > deprecates proven pattern [0.02ms]
|
|
346
346
|
(pass) deprecatePattern > sets deprecated_at timestamp [0.02ms]
|
|
347
|
-
(pass) deprecatePattern > updates last_validated timestamp
|
|
348
|
-
(pass) deprecatePattern > returns unchanged maturity when already deprecated [0.
|
|
349
|
-
(pass) deprecatePattern > accepts optional reason parameter
|
|
347
|
+
(pass) deprecatePattern > updates last_validated timestamp [0.02ms]
|
|
348
|
+
(pass) deprecatePattern > returns unchanged maturity when already deprecated [0.02ms]
|
|
349
|
+
(pass) deprecatePattern > accepts optional reason parameter [0.01ms]
|
|
350
350
|
(pass) getMaturityMultiplier > returns 0.5 for candidate [0.01ms]
|
|
351
351
|
(pass) getMaturityMultiplier > returns 1.0 for established
|
|
352
352
|
(pass) getMaturityMultiplier > returns 1.5 for proven
|
|
353
353
|
(pass) getMaturityMultiplier > returns 0 for deprecated
|
|
354
|
-
(pass) formatMaturityForPrompt > shows limited data for insufficient observations [0.
|
|
355
|
-
(pass) formatMaturityForPrompt > shows singular observation for count of 1 [0.
|
|
354
|
+
(pass) formatMaturityForPrompt > shows limited data for insufficient observations [0.01ms]
|
|
355
|
+
(pass) formatMaturityForPrompt > shows singular observation for count of 1 [0.04ms]
|
|
356
356
|
(pass) formatMaturityForPrompt > shows candidate with observation count when >= 3
|
|
357
|
-
(pass) formatMaturityForPrompt > shows established with percentages
|
|
358
|
-
(pass) formatMaturityForPrompt > shows proven with helpful percentage
|
|
357
|
+
(pass) formatMaturityForPrompt > shows established with percentages [0.01ms]
|
|
358
|
+
(pass) formatMaturityForPrompt > shows proven with helpful percentage [0.01ms]
|
|
359
359
|
(pass) formatMaturityForPrompt > shows deprecated with harmful percentage
|
|
360
|
-
(pass) formatMaturityForPrompt > rounds percentages correctly
|
|
361
|
-
(pass) formatMaturityForPrompt > handles zero counts edge case
|
|
360
|
+
(pass) formatMaturityForPrompt > rounds percentages correctly
|
|
361
|
+
(pass) formatMaturityForPrompt > handles zero counts edge case
|
|
362
362
|
(pass) formatPatternsWithMaturityForPrompt > formats empty map [0.04ms]
|
|
363
|
-
(pass) formatPatternsWithMaturityForPrompt > groups patterns by maturity state [0.
|
|
363
|
+
(pass) formatPatternsWithMaturityForPrompt > groups patterns by maturity state [0.05ms]
|
|
364
364
|
(pass) formatPatternsWithMaturityForPrompt > omits sections with no patterns [0.02ms]
|
|
365
365
|
(pass) formatPatternsWithMaturityForPrompt > includes pattern maturity labels [0.02ms]
|
|
366
|
-
(pass) formatPatternsWithMaturityForPrompt > maintains multiple patterns in same section [0.
|
|
366
|
+
(pass) formatPatternsWithMaturityForPrompt > maintains multiple patterns in same section [0.01ms]
|
|
367
367
|
(pass) formatPatternsWithMaturityForPrompt > formats section headers correctly [0.02ms]
|
|
368
|
-
(pass) InMemoryMaturityStorage > store and get > stores and retrieves maturity by pattern ID [0.
|
|
369
|
-
(pass) InMemoryMaturityStorage > store and get > returns null for non-existent pattern [0.
|
|
370
|
-
(pass) InMemoryMaturityStorage > store and get > overwrites existing maturity on store [0.
|
|
371
|
-
(pass) InMemoryMaturityStorage > getAll > returns empty array when no maturities stored [0.
|
|
368
|
+
(pass) InMemoryMaturityStorage > store and get > stores and retrieves maturity by pattern ID [0.02ms]
|
|
369
|
+
(pass) InMemoryMaturityStorage > store and get > returns null for non-existent pattern [0.08ms]
|
|
370
|
+
(pass) InMemoryMaturityStorage > store and get > overwrites existing maturity on store [0.02ms]
|
|
371
|
+
(pass) InMemoryMaturityStorage > getAll > returns empty array when no maturities stored [0.04ms]
|
|
372
372
|
(pass) InMemoryMaturityStorage > getAll > returns all stored maturities [0.04ms]
|
|
373
373
|
(pass) InMemoryMaturityStorage > getByState > returns empty array when no patterns match state [0.04ms]
|
|
374
374
|
(pass) InMemoryMaturityStorage > getByState > returns only patterns matching state [0.06ms]
|
|
375
|
-
(pass) InMemoryMaturityStorage > getByState > handles multiple patterns with same state [0.
|
|
376
|
-
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > stores and retrieves feedback for pattern [0.
|
|
377
|
-
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > returns empty array for pattern with no feedback [0.
|
|
375
|
+
(pass) InMemoryMaturityStorage > getByState > handles multiple patterns with same state [0.01ms]
|
|
376
|
+
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > stores and retrieves feedback for pattern [0.08ms]
|
|
377
|
+
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > returns empty array for pattern with no feedback [0.01ms]
|
|
378
378
|
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > stores multiple feedback events for same pattern [0.03ms]
|
|
379
|
-
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > filters feedback by pattern ID [0.
|
|
379
|
+
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > filters feedback by pattern ID [0.03ms]
|
|
380
380
|
(pass) InMemoryMaturityStorage > storeFeedback and getFeedback > preserves feedback event data [0.02ms]
|
|
381
|
-
(pass) InMemoryMaturityStorage > integration: full workflow > supports complete maturity tracking workflow [0.
|
|
381
|
+
(pass) InMemoryMaturityStorage > integration: full workflow > supports complete maturity tracking workflow [0.02ms]
|
|
382
382
|
|
|
383
383
|
src/mandate-promotion.test.ts:
|
|
384
|
-
(pass) shouldPromote > candidate stays candidate with insufficient votes
|
|
385
|
-
(pass) shouldPromote > candidate → established at threshold (net_votes >= 2) [0.
|
|
386
|
-
(pass) shouldPromote > candidate → established above threshold
|
|
384
|
+
(pass) shouldPromote > candidate stays candidate with insufficient votes
|
|
385
|
+
(pass) shouldPromote > candidate → established at threshold (net_votes >= 2) [0.13ms]
|
|
386
|
+
(pass) shouldPromote > candidate → established above threshold [0.01ms]
|
|
387
387
|
(pass) shouldPromote > established stays established with insufficient mandate votes
|
|
388
388
|
(pass) shouldPromote > established stays established with low vote ratio
|
|
389
389
|
(pass) shouldPromote > established → mandate at threshold (net >= 5, ratio >= 0.7)
|
|
390
390
|
(pass) shouldPromote > established → mandate above threshold
|
|
391
391
|
(pass) shouldPromote > mandate stays mandate (no demotion)
|
|
392
|
-
(pass) shouldPromote > candidate → rejected with negative votes
|
|
392
|
+
(pass) shouldPromote > candidate → rejected with negative votes
|
|
393
393
|
(pass) shouldPromote > established → rejected with negative votes
|
|
394
394
|
(pass) shouldPromote > rejected stays rejected (permanent)
|
|
395
395
|
(pass) shouldPromote > uses custom config thresholds [0.01ms]
|
|
396
|
-
(pass) evaluatePromotion > returns correct promotion result for candidate → established [0.
|
|
397
|
-
(pass) evaluatePromotion > returns correct promotion result for established → mandate [0.
|
|
398
|
-
(pass) evaluatePromotion > returns correct promotion result for candidate → rejected [0.
|
|
399
|
-
(pass) evaluatePromotion > returns correct result for no status change
|
|
400
|
-
(pass) evaluatePromotion > returns correct result for mandate staying mandate [0.
|
|
401
|
-
(pass) evaluatePromotion > returns correct result for rejected staying rejected [0.
|
|
402
|
-
(pass) decay affects promotion timing > net_votes can decay below promotion threshold
|
|
396
|
+
(pass) evaluatePromotion > returns correct promotion result for candidate → established [0.05ms]
|
|
397
|
+
(pass) evaluatePromotion > returns correct promotion result for established → mandate [0.04ms]
|
|
398
|
+
(pass) evaluatePromotion > returns correct promotion result for candidate → rejected [0.02ms]
|
|
399
|
+
(pass) evaluatePromotion > returns correct result for no status change [0.01ms]
|
|
400
|
+
(pass) evaluatePromotion > returns correct result for mandate staying mandate [0.01ms]
|
|
401
|
+
(pass) evaluatePromotion > returns correct result for rejected staying rejected [0.02ms]
|
|
402
|
+
(pass) decay affects promotion timing > net_votes can decay below promotion threshold [0.01ms]
|
|
403
403
|
(pass) decay affects promotion timing > vote_ratio decay prevents mandate promotion [0.01ms]
|
|
404
|
-
(pass) decay affects promotion timing > fresh votes can push over mandate threshold
|
|
404
|
+
(pass) decay affects promotion timing > fresh votes can push over mandate threshold [0.01ms]
|
|
405
405
|
(pass) formatPromotionResult > formats promoted result with arrow [0.02ms]
|
|
406
406
|
(pass) formatPromotionResult > formats no-change result without arrow [0.02ms]
|
|
407
|
-
(pass) evaluateBatchPromotions > evaluates multiple entries [0.
|
|
408
|
-
(pass) evaluateBatchPromotions > skips entries without scores [0.
|
|
407
|
+
(pass) evaluateBatchPromotions > evaluates multiple entries [0.02ms]
|
|
408
|
+
(pass) evaluateBatchPromotions > skips entries without scores [0.04ms]
|
|
409
409
|
(pass) getStatusChanges > filters to only promoted entries [0.02ms]
|
|
410
|
-
(pass) groupByTransition > groups results by transition type [0.
|
|
411
|
-
(pass) groupByTransition > uses status name for no-change transitions [0.
|
|
412
|
-
(pass) edge cases > handles exact threshold values [0.
|
|
413
|
-
(pass) edge cases > handles zero votes
|
|
414
|
-
(pass) edge cases > handles negative vote ratio edge case [0.
|
|
410
|
+
(pass) groupByTransition > groups results by transition type [0.04ms]
|
|
411
|
+
(pass) groupByTransition > uses status name for no-change transitions [0.04ms]
|
|
412
|
+
(pass) edge cases > handles exact threshold values [0.02ms]
|
|
413
|
+
(pass) edge cases > handles zero votes [0.02ms]
|
|
414
|
+
(pass) edge cases > handles negative vote ratio edge case [0.03ms]
|
|
415
415
|
(pass) edge cases > rejects at exact rejection threshold
|
|
416
416
|
|
|
417
417
|
src/schemas/index.test.ts:
|
|
418
418
|
(pass) BeadSchema > validates a complete bead [0.52ms]
|
|
419
|
-
(pass) BeadSchema > rejects invalid priority [0.
|
|
420
|
-
(pass) BeadSchema > accepts all valid types [0.
|
|
421
|
-
(pass) BeadCreateArgsSchema > validates minimal create args [0.
|
|
422
|
-
(pass) BeadCreateArgsSchema > rejects empty title [0.
|
|
423
|
-
(pass) EpicCreateArgsSchema > validates epic with subtasks [0.
|
|
424
|
-
(pass) EpicCreateArgsSchema > requires at least one subtask [0.
|
|
425
|
-
(pass) EvaluationSchema > validates a passing evaluation [0.
|
|
419
|
+
(pass) BeadSchema > rejects invalid priority [0.10ms]
|
|
420
|
+
(pass) BeadSchema > accepts all valid types [0.01ms]
|
|
421
|
+
(pass) BeadCreateArgsSchema > validates minimal create args [0.17ms]
|
|
422
|
+
(pass) BeadCreateArgsSchema > rejects empty title [0.03ms]
|
|
423
|
+
(pass) EpicCreateArgsSchema > validates epic with subtasks [0.05ms]
|
|
424
|
+
(pass) EpicCreateArgsSchema > requires at least one subtask [0.21ms]
|
|
425
|
+
(pass) EvaluationSchema > validates a passing evaluation [0.04ms]
|
|
426
426
|
(pass) EvaluationSchema > validates a failing evaluation with retry suggestion [0.02ms]
|
|
427
|
-
(pass) TaskDecompositionSchema > validates a decomposition [0.
|
|
428
|
-
(pass) TaskDecompositionSchema > validates subtask effort levels [0.
|
|
427
|
+
(pass) TaskDecompositionSchema > validates a decomposition [0.03ms]
|
|
428
|
+
(pass) TaskDecompositionSchema > validates subtask effort levels [0.02ms]
|
|
429
429
|
(pass) SwarmStatusSchema > validates swarm status [0.30ms]
|
|
430
|
-
(pass) ValidationResultSchema > validates success result [0.
|
|
431
|
-
(pass) ValidationResultSchema > validates failure result with errors [0.
|
|
430
|
+
(pass) ValidationResultSchema > validates success result [0.09ms]
|
|
431
|
+
(pass) ValidationResultSchema > validates failure result with errors [0.07ms]
|
|
432
432
|
|
|
433
433
|
src/schemas/cell-events.test.ts:
|
|
434
|
-
(pass) CellEventSchema > createCellEvent > creates valid cell_created event [0.
|
|
435
|
-
(pass) CellEventSchema > createCellEvent > creates valid cell_closed event [0.
|
|
436
|
-
(pass) CellEventSchema > createCellEvent > creates valid cell_dependency_added event [0.
|
|
437
|
-
(pass) CellEventSchema > createCellEvent > creates valid cell_epic_child_added event [0.
|
|
438
|
-
(pass) CellEventSchema > createCellEvent > throws on invalid event data [0.
|
|
439
|
-
(pass) CellEventSchema > type guards > isCellEventType narrows type correctly [0.
|
|
440
|
-
(pass) CellEventSchema > type guards > isStateTransitionEvent identifies status changes [0.
|
|
441
|
-
(pass) CellEventSchema > type guards > isEpicEvent identifies epic operations [0.
|
|
442
|
-
(pass) CellEventSchema > type guards > isAgentEvent detects agent-triggered events [0.
|
|
434
|
+
(pass) CellEventSchema > createCellEvent > creates valid cell_created event [0.85ms]
|
|
435
|
+
(pass) CellEventSchema > createCellEvent > creates valid cell_closed event [0.53ms]
|
|
436
|
+
(pass) CellEventSchema > createCellEvent > creates valid cell_dependency_added event [0.28ms]
|
|
437
|
+
(pass) CellEventSchema > createCellEvent > creates valid cell_epic_child_added event [0.13ms]
|
|
438
|
+
(pass) CellEventSchema > createCellEvent > throws on invalid event data [0.10ms]
|
|
439
|
+
(pass) CellEventSchema > type guards > isCellEventType narrows type correctly [0.06ms]
|
|
440
|
+
(pass) CellEventSchema > type guards > isStateTransitionEvent identifies status changes [0.03ms]
|
|
441
|
+
(pass) CellEventSchema > type guards > isEpicEvent identifies epic operations [0.04ms]
|
|
442
|
+
(pass) CellEventSchema > type guards > isAgentEvent detects agent-triggered events [0.20ms]
|
|
443
443
|
(pass) CellEventSchema > getCellIdFromEvent > extracts cell_id from any event [0.03ms]
|
|
444
|
-
(pass) CellEventSchema > discriminated union validation > validates against full CellEventSchema [0.
|
|
445
|
-
(pass) CellEventSchema > discriminated union validation > rejects invalid event type [0.
|
|
446
|
-
(pass) CellEventSchema > discriminated union validation > validates dependency types [0.
|
|
447
|
-
(pass) CellEventSchema > event metadata > supports metadata field [0.
|
|
448
|
-
(pass) CellEventSchema > epic closure eligible event > creates valid closure eligible event [0.
|
|
444
|
+
(pass) CellEventSchema > discriminated union validation > validates against full CellEventSchema [0.05ms]
|
|
445
|
+
(pass) CellEventSchema > discriminated union validation > rejects invalid event type [0.04ms]
|
|
446
|
+
(pass) CellEventSchema > discriminated union validation > validates dependency types [0.01ms]
|
|
447
|
+
(pass) CellEventSchema > event metadata > supports metadata field [0.04ms]
|
|
448
|
+
(pass) CellEventSchema > epic closure eligible event > creates valid closure eligible event [0.20ms]
|
|
449
449
|
(pass) CellEventSchema > status changed event > tracks status transitions [0.16ms]
|
|
450
|
-
(pass) CellEventSchema > status changed event > includes optional reason for blocked/closed [0.
|
|
450
|
+
(pass) CellEventSchema > status changed event > includes optional reason for blocked/closed [0.01ms]
|
|
451
451
|
(pass) CellEventSchema > comment events > creates comment with optional parent [0.17ms]
|
|
452
|
-
(pass) CellEventSchema > work tracking events > tracks work start with file reservations [0.
|
|
452
|
+
(pass) CellEventSchema > work tracking events > tracks work start with file reservations [0.14ms]
|
|
453
453
|
|
|
454
454
|
src/schemas/worker-handoff.test.ts:
|
|
455
|
-
(pass) WorkerHandoffContractSchema > valid contract parses correctly [0.
|
|
456
|
-
(pass) WorkerHandoffContractSchema > missing task_id fails [0.
|
|
457
|
-
(pass) WorkerHandoffContractSchema > empty files_owned is valid (read-only tasks) [0.
|
|
458
|
-
(pass) WorkerHandoffContractSchema > empty success_criteria fails [0.
|
|
459
|
-
(pass) WorkerHandoffContractSchema > invalid task_id format fails [0.
|
|
460
|
-
(pass) WorkerHandoffContextSchema > valid context parses correctly [0.
|
|
461
|
-
(pass) WorkerHandoffContextSchema > missing required fields fails [0.
|
|
455
|
+
(pass) WorkerHandoffContractSchema > valid contract parses correctly [0.81ms]
|
|
456
|
+
(pass) WorkerHandoffContractSchema > missing task_id fails [0.04ms]
|
|
457
|
+
(pass) WorkerHandoffContractSchema > empty files_owned is valid (read-only tasks) [0.04ms]
|
|
458
|
+
(pass) WorkerHandoffContractSchema > empty success_criteria fails [0.02ms]
|
|
459
|
+
(pass) WorkerHandoffContractSchema > invalid task_id format fails [0.04ms]
|
|
460
|
+
(pass) WorkerHandoffContextSchema > valid context parses correctly [0.12ms]
|
|
461
|
+
(pass) WorkerHandoffContextSchema > missing required fields fails [0.09ms]
|
|
462
462
|
(pass) WorkerHandoffContextSchema > empty strings are valid [0.02ms]
|
|
463
|
-
(pass) WorkerHandoffEscalationSchema > valid escalation parses correctly [0.
|
|
464
|
-
(pass) WorkerHandoffEscalationSchema > missing required fields fails [0.
|
|
465
|
-
(pass) WorkerHandoffSchema > complete valid handoff parses correctly [0.
|
|
463
|
+
(pass) WorkerHandoffEscalationSchema > valid escalation parses correctly [0.05ms]
|
|
464
|
+
(pass) WorkerHandoffEscalationSchema > missing required fields fails [0.05ms]
|
|
465
|
+
(pass) WorkerHandoffSchema > complete valid handoff parses correctly [0.08ms]
|
|
466
466
|
(pass) WorkerHandoffSchema > missing contract section fails [0.04ms]
|
|
467
|
-
(pass) WorkerHandoffSchema > nested validation catches contract errors [0.
|
|
468
|
-
(pass) WorkerHandoffSchema > type inference works correctly
|
|
467
|
+
(pass) WorkerHandoffSchema > nested validation catches contract errors [0.02ms]
|
|
468
|
+
(pass) WorkerHandoffSchema > type inference works correctly [0.04ms]
|
|
469
469
|
|
|
470
470
|
421 pass
|
|
471
471
|
0 fail
|
|
472
472
|
772 expect() calls
|
|
473
|
-
Ran 421 tests across 10 files. [
|
|
473
|
+
Ran 421 tests across 10 files. [193.00ms]
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# opencode-swarm-plugin
|
|
2
2
|
|
|
3
|
+
## 0.30.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`08e61ab`](https://github.com/joelhooks/swarm-tools/commit/08e61abd96ced0443a5ac5dca0e8f362ed869075) Thanks [@joelhooks](https://github.com/joelhooks)! - ## 🐝 Workers Now Choose Their Own Model
|
|
8
|
+
|
|
9
|
+
Added intelligent model selection for swarm workers based on task characteristics.
|
|
10
|
+
|
|
11
|
+
**What changed:**
|
|
12
|
+
|
|
13
|
+
- `swarm setup` now asks for a "lite model" preference (docs/tests/simple edits)
|
|
14
|
+
- New `selectWorkerModel()` function auto-selects based on file types
|
|
15
|
+
- `swarm_spawn_subtask` includes `recommended_model` in metadata
|
|
16
|
+
- `DecomposedSubtask` schema supports optional explicit `model` field
|
|
17
|
+
|
|
18
|
+
**Model selection priority:**
|
|
19
|
+
|
|
20
|
+
1. Explicit `model` field in subtask (if specified)
|
|
21
|
+
2. File-type inference:
|
|
22
|
+
- All `.md`/`.mdx` files → lite model
|
|
23
|
+
- All `.test.`/`.spec.` files → lite model
|
|
24
|
+
3. Mixed or implementation files → primary model
|
|
25
|
+
|
|
26
|
+
**Why it matters:**
|
|
27
|
+
|
|
28
|
+
- Cost savings: docs and tests don't need expensive models
|
|
29
|
+
- Faster execution: lite models are snappier for simple tasks
|
|
30
|
+
- Better defaults: right-sized models for each subtask type
|
|
31
|
+
- Still flexible: coordinators can override per-subtask
|
|
32
|
+
|
|
33
|
+
**Backward compatible:**
|
|
34
|
+
|
|
35
|
+
- Existing workflows continue to work
|
|
36
|
+
- Model selection is transparent to agents
|
|
37
|
+
- Defaults to primary model if lite model not configured
|
|
38
|
+
|
|
39
|
+
**Example:**
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Subtask with all markdown files
|
|
43
|
+
{ files: ["README.md", "docs/guide.mdx"] }
|
|
44
|
+
// → selects lite model (haiku)
|
|
45
|
+
|
|
46
|
+
// Subtask with mixed files
|
|
47
|
+
{ files: ["src/auth.ts", "README.md"] }
|
|
48
|
+
// → selects primary model (sonnet)
|
|
49
|
+
|
|
50
|
+
// Explicit override
|
|
51
|
+
{ files: ["complex-refactor.ts"], model: "anthropic/claude-opus-4-5" }
|
|
52
|
+
// → uses opus as specified
|
|
53
|
+
```
|
|
54
|
+
|
|
3
55
|
## 0.30.4
|
|
4
56
|
|
|
5
57
|
### Patch Changes
|
package/bin/swarm.ts
CHANGED
|
@@ -1889,9 +1889,43 @@ async function setup() {
|
|
|
1889
1889
|
process.exit(0);
|
|
1890
1890
|
}
|
|
1891
1891
|
|
|
1892
|
+
// Lite model selection for simple tasks (docs, tests)
|
|
1893
|
+
const liteModel = await p.select({
|
|
1894
|
+
message: "Select lite model (for docs, tests, simple edits):",
|
|
1895
|
+
options: [
|
|
1896
|
+
{
|
|
1897
|
+
value: "anthropic/claude-haiku-4-5",
|
|
1898
|
+
label: "Claude Haiku 4.5",
|
|
1899
|
+
hint: "Fast and cost-effective (recommended)",
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
value: "anthropic/claude-sonnet-4-5",
|
|
1903
|
+
label: "Claude Sonnet 4.5",
|
|
1904
|
+
hint: "More capable, slower",
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
value: "openai/gpt-4o-mini",
|
|
1908
|
+
label: "GPT-4o Mini",
|
|
1909
|
+
hint: "Fast and cheap",
|
|
1910
|
+
},
|
|
1911
|
+
{
|
|
1912
|
+
value: "google/gemini-2.0-flash",
|
|
1913
|
+
label: "Gemini 2.0 Flash",
|
|
1914
|
+
hint: "Fast and capable",
|
|
1915
|
+
},
|
|
1916
|
+
],
|
|
1917
|
+
initialValue: "anthropic/claude-haiku-4-5",
|
|
1918
|
+
});
|
|
1919
|
+
|
|
1920
|
+
if (p.isCancel(liteModel)) {
|
|
1921
|
+
p.cancel("Setup cancelled");
|
|
1922
|
+
process.exit(0);
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1892
1925
|
p.log.success("Selected models:");
|
|
1893
1926
|
p.log.message(dim(` Coordinator: ${coordinatorModel}`));
|
|
1894
1927
|
p.log.message(dim(` Worker: ${workerModel}`));
|
|
1928
|
+
p.log.message(dim(` Lite: ${liteModel}`));
|
|
1895
1929
|
|
|
1896
1930
|
p.log.step("Setting up OpenCode integration...");
|
|
1897
1931
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Selection Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for selectWorkerModel function that determines which model
|
|
5
|
+
* a worker should use based on subtask characteristics.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, test, expect } from "bun:test";
|
|
8
|
+
import { selectWorkerModel } from "./model-selection";
|
|
9
|
+
import type { DecomposedSubtask } from "./schemas/task";
|
|
10
|
+
|
|
11
|
+
// Mock config type matching expected SwarmConfig structure
|
|
12
|
+
interface TestConfig {
|
|
13
|
+
primaryModel: string;
|
|
14
|
+
liteModel?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("selectWorkerModel", () => {
|
|
18
|
+
const mockConfig: TestConfig = {
|
|
19
|
+
primaryModel: "anthropic/claude-sonnet-4-5",
|
|
20
|
+
liteModel: "anthropic/claude-haiku-4-5",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
test("uses explicit model field from subtask when provided", () => {
|
|
24
|
+
const subtask: DecomposedSubtask & { model?: string } = {
|
|
25
|
+
title: "Update docs",
|
|
26
|
+
description: "Update README",
|
|
27
|
+
files: ["README.md"],
|
|
28
|
+
estimated_effort: "trivial",
|
|
29
|
+
model: "anthropic/claude-opus-4-5", // Explicit override
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
33
|
+
expect(result).toBe("anthropic/claude-opus-4-5");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("uses liteModel for all markdown files", () => {
|
|
37
|
+
const subtask: DecomposedSubtask = {
|
|
38
|
+
title: "Update docs",
|
|
39
|
+
description: "Update all docs",
|
|
40
|
+
files: ["README.md", "CONTRIBUTING.md"],
|
|
41
|
+
estimated_effort: "small",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
45
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("uses liteModel for all MDX files", () => {
|
|
49
|
+
const subtask: DecomposedSubtask = {
|
|
50
|
+
title: "Update docs",
|
|
51
|
+
description: "Update content",
|
|
52
|
+
files: ["docs/intro.mdx", "docs/guide.mdx"],
|
|
53
|
+
estimated_effort: "small",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
57
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("uses liteModel for test files with .test. pattern", () => {
|
|
61
|
+
const subtask: DecomposedSubtask = {
|
|
62
|
+
title: "Write tests",
|
|
63
|
+
description: "Add unit tests",
|
|
64
|
+
files: ["src/auth.test.ts", "src/user.test.ts"],
|
|
65
|
+
estimated_effort: "small",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
69
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("uses liteModel for test files with .spec. pattern", () => {
|
|
73
|
+
const subtask: DecomposedSubtask = {
|
|
74
|
+
title: "Write specs",
|
|
75
|
+
description: "Add spec tests",
|
|
76
|
+
files: ["src/auth.spec.ts", "src/user.spec.ts"],
|
|
77
|
+
estimated_effort: "small",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
81
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("uses primaryModel when files are mixed (code + docs)", () => {
|
|
85
|
+
const subtask: DecomposedSubtask = {
|
|
86
|
+
title: "Implement feature with docs",
|
|
87
|
+
description: "Add feature and document it",
|
|
88
|
+
files: ["src/feature.ts", "README.md"],
|
|
89
|
+
estimated_effort: "medium",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
93
|
+
expect(result).toBe("anthropic/claude-sonnet-4-5");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("uses primaryModel when files are mixed (code + tests)", () => {
|
|
97
|
+
const subtask: DecomposedSubtask = {
|
|
98
|
+
title: "Implement feature with tests",
|
|
99
|
+
description: "Add feature and tests",
|
|
100
|
+
files: ["src/feature.ts", "src/feature.test.ts"],
|
|
101
|
+
estimated_effort: "medium",
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
105
|
+
expect(result).toBe("anthropic/claude-sonnet-4-5");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("uses primaryModel for implementation files", () => {
|
|
109
|
+
const subtask: DecomposedSubtask = {
|
|
110
|
+
title: "Implement auth",
|
|
111
|
+
description: "Add authentication",
|
|
112
|
+
files: ["src/auth.ts", "src/middleware.ts"],
|
|
113
|
+
estimated_effort: "large",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
117
|
+
expect(result).toBe("anthropic/claude-sonnet-4-5");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("defaults to primaryModel when liteModel not configured", () => {
|
|
121
|
+
const configWithoutLite: TestConfig = {
|
|
122
|
+
primaryModel: "anthropic/claude-sonnet-4-5",
|
|
123
|
+
// liteModel is undefined
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const subtask: DecomposedSubtask = {
|
|
127
|
+
title: "Update docs",
|
|
128
|
+
description: "Update README",
|
|
129
|
+
files: ["README.md"],
|
|
130
|
+
estimated_effort: "trivial",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const result = selectWorkerModel(subtask, configWithoutLite);
|
|
134
|
+
expect(result).toBe("anthropic/claude-sonnet-4-5");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("falls back to claude-haiku when liteModel not configured but primaryModel missing", () => {
|
|
138
|
+
const emptyConfig: TestConfig = {
|
|
139
|
+
primaryModel: "",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const subtask: DecomposedSubtask = {
|
|
143
|
+
title: "Update docs",
|
|
144
|
+
description: "Update README",
|
|
145
|
+
files: ["README.md"],
|
|
146
|
+
estimated_effort: "trivial",
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const result = selectWorkerModel(subtask, emptyConfig);
|
|
150
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("handles empty files array by defaulting to primaryModel", () => {
|
|
154
|
+
const subtask: DecomposedSubtask = {
|
|
155
|
+
title: "Research task",
|
|
156
|
+
description: "Investigate options",
|
|
157
|
+
files: [],
|
|
158
|
+
estimated_effort: "small",
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
162
|
+
expect(result).toBe("anthropic/claude-sonnet-4-5");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("handles mixed markdown and mdx files", () => {
|
|
166
|
+
const subtask: DecomposedSubtask = {
|
|
167
|
+
title: "Update all docs",
|
|
168
|
+
description: "Update docs",
|
|
169
|
+
files: ["README.md", "docs/guide.mdx", "CHANGELOG.md"],
|
|
170
|
+
estimated_effort: "small",
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
174
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("case insensitive file extension matching", () => {
|
|
178
|
+
const subtask: DecomposedSubtask = {
|
|
179
|
+
title: "Update docs",
|
|
180
|
+
description: "Update README",
|
|
181
|
+
files: ["README.MD", "CONTRIBUTING.MD"],
|
|
182
|
+
estimated_effort: "trivial",
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const result = selectWorkerModel(subtask, mockConfig);
|
|
186
|
+
expect(result).toBe("anthropic/claude-haiku-4-5");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Selection Module
|
|
3
|
+
*
|
|
4
|
+
* Determines which model a worker agent should use based on subtask
|
|
5
|
+
* characteristics like file types and complexity.
|
|
6
|
+
*
|
|
7
|
+
* Priority:
|
|
8
|
+
* 1. Explicit model field in subtask
|
|
9
|
+
* 2. File-type inference (docs/tests → lite model)
|
|
10
|
+
* 3. Default to primary model
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { DecomposedSubtask } from "./schemas/task";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Configuration interface for swarm models
|
|
17
|
+
*/
|
|
18
|
+
export interface SwarmConfig {
|
|
19
|
+
primaryModel: string;
|
|
20
|
+
liteModel?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Select the appropriate model for a worker agent based on subtask characteristics
|
|
25
|
+
*
|
|
26
|
+
* Priority order:
|
|
27
|
+
* 1. Explicit `model` field in subtask (if present)
|
|
28
|
+
* 2. File-type inference:
|
|
29
|
+
* - All .md/.mdx files → liteModel
|
|
30
|
+
* - All .test./.spec. files → liteModel
|
|
31
|
+
* 3. Mixed files or implementation → primaryModel
|
|
32
|
+
*
|
|
33
|
+
* @param subtask - The subtask to evaluate
|
|
34
|
+
* @param config - Swarm configuration with model preferences
|
|
35
|
+
* @returns Model identifier string
|
|
36
|
+
*/
|
|
37
|
+
export function selectWorkerModel(
|
|
38
|
+
subtask: DecomposedSubtask & { model?: string },
|
|
39
|
+
config: SwarmConfig,
|
|
40
|
+
): string {
|
|
41
|
+
// Priority 1: Explicit model in subtask
|
|
42
|
+
if (subtask.model) {
|
|
43
|
+
return subtask.model;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const files = subtask.files || [];
|
|
47
|
+
|
|
48
|
+
// Priority 2: File-type inference
|
|
49
|
+
if (files.length > 0) {
|
|
50
|
+
const allDocs = files.every((f) => {
|
|
51
|
+
const lower = f.toLowerCase();
|
|
52
|
+
return lower.endsWith(".md") || lower.endsWith(".mdx");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const allTests = files.every((f) => {
|
|
56
|
+
const lower = f.toLowerCase();
|
|
57
|
+
return lower.includes(".test.") || lower.includes(".spec.");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (allDocs || allTests) {
|
|
61
|
+
// Use lite model if configured, otherwise fall back to primary
|
|
62
|
+
return config.liteModel || config.primaryModel || "anthropic/claude-haiku-4-5";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Priority 3: Default to primary model
|
|
67
|
+
return config.primaryModel || "anthropic/claude-haiku-4-5";
|
|
68
|
+
}
|
package/src/schemas/task.ts
CHANGED
|
@@ -43,6 +43,11 @@ export const DecomposedSubtaskSchema = z.object({
|
|
|
43
43
|
estimated_effort: EffortLevelSchema,
|
|
44
44
|
/** Potential risks or complications (e.g., 'tight coupling', 'data migration required', 'breaking change') */
|
|
45
45
|
risks: z.array(z.string()).optional().default([]),
|
|
46
|
+
/**
|
|
47
|
+
* Optional explicit model override for this subtask.
|
|
48
|
+
* If not specified, model will be selected based on file types.
|
|
49
|
+
*/
|
|
50
|
+
model: z.string().optional(),
|
|
46
51
|
});
|
|
47
52
|
export type DecomposedSubtask = z.infer<typeof DecomposedSubtaskSchema>;
|
|
48
53
|
|
package/src/swarm-prompts.ts
CHANGED
|
@@ -739,7 +739,7 @@ export const swarm_subtask_prompt = tool({
|
|
|
739
739
|
*/
|
|
740
740
|
export const swarm_spawn_subtask = tool({
|
|
741
741
|
description:
|
|
742
|
-
"Prepare a subtask for spawning. Returns prompt with Agent Mail/hive tracking instructions. IMPORTANT: Pass project_path for swarmmail_init.",
|
|
742
|
+
"Prepare a subtask for spawning. Returns prompt with Agent Mail/hive tracking instructions. IMPORTANT: Pass project_path for swarmmail_init. Automatically selects appropriate model based on file types.",
|
|
743
743
|
args: {
|
|
744
744
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
745
745
|
epic_id: tool.schema.string().describe("Parent epic bead ID"),
|
|
@@ -769,6 +769,10 @@ export const swarm_spawn_subtask = tool({
|
|
|
769
769
|
})
|
|
770
770
|
.optional()
|
|
771
771
|
.describe("Recovery context from checkpoint compaction"),
|
|
772
|
+
model: tool.schema
|
|
773
|
+
.string()
|
|
774
|
+
.optional()
|
|
775
|
+
.describe("Optional explicit model override (auto-selected if not provided)"),
|
|
772
776
|
},
|
|
773
777
|
async execute(args) {
|
|
774
778
|
const prompt = formatSubtaskPromptV2({
|
|
@@ -782,6 +786,28 @@ export const swarm_spawn_subtask = tool({
|
|
|
782
786
|
recovery_context: args.recovery_context,
|
|
783
787
|
});
|
|
784
788
|
|
|
789
|
+
// Import selectWorkerModel at function scope to avoid circular dependencies
|
|
790
|
+
const { selectWorkerModel } = await import("./model-selection.js");
|
|
791
|
+
|
|
792
|
+
// Create a mock subtask for model selection
|
|
793
|
+
const subtask = {
|
|
794
|
+
title: args.subtask_title,
|
|
795
|
+
description: args.subtask_description || "",
|
|
796
|
+
files: args.files,
|
|
797
|
+
estimated_effort: "medium" as const,
|
|
798
|
+
risks: [],
|
|
799
|
+
model: args.model,
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
// Use placeholder config - actual config should be passed from coordinator
|
|
803
|
+
// For now, we use reasonable defaults
|
|
804
|
+
const config = {
|
|
805
|
+
primaryModel: "anthropic/claude-sonnet-4-5",
|
|
806
|
+
liteModel: "anthropic/claude-haiku-4-5",
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
const selectedModel = selectWorkerModel(subtask, config);
|
|
810
|
+
|
|
785
811
|
return JSON.stringify(
|
|
786
812
|
{
|
|
787
813
|
prompt,
|
|
@@ -790,6 +816,7 @@ export const swarm_spawn_subtask = tool({
|
|
|
790
816
|
files: args.files,
|
|
791
817
|
project_path: args.project_path,
|
|
792
818
|
recovery_context: args.recovery_context,
|
|
819
|
+
recommended_model: selectedModel,
|
|
793
820
|
},
|
|
794
821
|
null,
|
|
795
822
|
2,
|