opencode-swarm 5.2.0 β 6.0.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/README.md +62 -14
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/reviewer.d.ts +3 -0
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/schema.d.ts +17 -0
- package/dist/hooks/delegation-tracker.d.ts +1 -1
- package/dist/index.js +271 -64
- package/dist/tools/diff.d.ts +18 -0
- package/dist/tools/index.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://img.shields.io/badge/version-
|
|
2
|
+
<img src="https://img.shields.io/badge/version-6.0.0-blue" alt="Version">
|
|
3
3
|
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
|
|
4
4
|
<img src="https://img.shields.io/badge/opencode-plugin-purple" alt="OpenCode Plugin">
|
|
5
5
|
<img src="https://img.shields.io/badge/agents-7-orange" alt="Agents">
|
|
6
|
-
<img src="https://img.shields.io/badge/tests-
|
|
6
|
+
<img src="https://img.shields.io/badge/tests-1188-brightgreen" alt="Tests">
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<h1 align="center">π OpenCode Swarm</h1>
|
|
@@ -138,15 +138,24 @@ OpenCode Swarm:
|
|
|
138
138
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
139
139
|
β PHASE 5: Execute (per task) β
|
|
140
140
|
β β
|
|
141
|
-
β βββββββββββ ββββββββββββββ ββββββββββββββββ
|
|
142
|
-
β β @coder β β β @reviewer β β β @test β
|
|
143
|
-
β β 1 task β β check all β β write + run β
|
|
144
|
-
β βββββββββββ ββββββββββββββ ββββββββββββββββ
|
|
145
|
-
β β
|
|
146
|
-
β β
|
|
147
|
-
β
|
|
141
|
+
β βββββββββββ βββββββββ ββββββββββββββ ββββββββββββββββ β
|
|
142
|
+
β β @coder β β β diff β β β @reviewer β β β @test β β
|
|
143
|
+
β β 1 task β β tool β β check all β β write + run β β
|
|
144
|
+
β βββββββββββ βββββββββ ββββββββββββββ ββββββββββββββββ β
|
|
145
|
+
β β β β β β
|
|
146
|
+
β β Contract β If REJECTED: If FAIL: fix β
|
|
147
|
+
β β changes? β retry from coder + retest β
|
|
148
|
+
β β β β β β
|
|
149
|
+
β β βΌ β βΌ β
|
|
150
|
+
β β βββββββββββ β ββββββββββββββββ ββββββββββββββββ β
|
|
151
|
+
β β β@explorerβ β β @reviewer β β β @test β β
|
|
152
|
+
β β β impact β β β security-onlyβ β adversarial β β
|
|
153
|
+
β β βanalysis β β β (if match) β β (attacks) β β
|
|
154
|
+
β β βββββββββββ β ββββββββββββββββ ββββββββββββββββ β
|
|
155
|
+
β β β β
|
|
156
|
+
β βββββββββββββββββ β
|
|
148
157
|
β β
|
|
149
|
-
β Update plan.md: [x] Task complete (only
|
|
158
|
+
β Update plan.md: [x] Task complete (only after ALL gates pass) β
|
|
150
159
|
β Next task... β
|
|
151
160
|
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
152
161
|
β
|
|
@@ -334,6 +343,13 @@ bunx opencode-swarm uninstall --clean
|
|
|
334
343
|
|
|
335
344
|
## What's New
|
|
336
345
|
|
|
346
|
+
### v6.0.0 β Core QA & Security Gates
|
|
347
|
+
- **Dual-pass security reviewer** β After the general reviewer APPROVES, the architect automatically triggers a second security-only review pass when the changed file matches security-sensitive paths (`auth`, `crypto`, `session`, `token`, `middleware`, `api`, `security`) or the coder's output contains security keywords. Configurable via `review_passes` config.
|
|
348
|
+
- **Adversarial testing** β After verification tests PASS, the test engineer is re-delegated with adversarial-only framing: attack vectors, boundary violations, and injection attempts. Pure prompt engineering, no new infrastructure.
|
|
349
|
+
- **Integration impact analysis** β After the coder completes, the `diff` tool detects contract changes (exported functions, interfaces, types). If found, the explorer runs impact analysis across dependents before review begins.
|
|
350
|
+
- **`diff` tool** β New agent-accessible tool providing structured git diff with numstat parsing, contract change detection, configurable base ref (`HEAD`/staged/unstaged), path filtering, and 500-line truncation.
|
|
351
|
+
- **87 new tests** β 1188 total tests across 53+ files (up from 1101 in v5.2.0).
|
|
352
|
+
|
|
337
353
|
### v5.2.0 β Per-Invocation Guardrails
|
|
338
354
|
- **Per-invocation budget isolation** β Guardrail limits (tool calls, duration, errors) now reset with each agent delegation. Second invocation of the same agent gets a fresh budget, preventing false circuit breaker trips in long-running projects.
|
|
339
355
|
- **Architect protocol enforcement** β New mandatory QA gate rules: every coder task must go through reviewer approval + test_engineer verification before the next coder task. Protocol violations detected at runtime with warning injection.
|
|
@@ -409,7 +425,7 @@ All features are opt-in via configuration. See [Installation Guide](docs/install
|
|
|
409
425
|
### β
Quality Assurance
|
|
410
426
|
| Agent | Role |
|
|
411
427
|
|-------|------|
|
|
412
|
-
| `reviewer` |
|
|
428
|
+
| `reviewer` | Dual-pass review: correctness review first, then automatic security-only pass for security-sensitive files. The architect specifies CHECK dimensions per call. OWASP Top 10 categories built in. |
|
|
413
429
|
| `critic` | Plan review gate. Reviews the architect's plan BEFORE implementation β checks completeness, feasibility, scope, dependencies, and flags AI-slop. |
|
|
414
430
|
|
|
415
431
|
---
|
|
@@ -516,6 +532,38 @@ Override limits for specific agents that need more (or less) room:
|
|
|
516
532
|
|
|
517
533
|
Profiles merge with base config β only specified fields are overridden.
|
|
518
534
|
|
|
535
|
+
### Review Passes
|
|
536
|
+
|
|
537
|
+
Control the dual-pass security review behavior:
|
|
538
|
+
|
|
539
|
+
```jsonc
|
|
540
|
+
{
|
|
541
|
+
"review_passes": {
|
|
542
|
+
"always_security_review": false, // default: false (only on security-sensitive files)
|
|
543
|
+
"security_globs": [ // default patterns:
|
|
544
|
+
"**/*auth*", "**/*crypto*",
|
|
545
|
+
"**/*session*", "**/*token*",
|
|
546
|
+
"**/*middleware*", "**/*api*",
|
|
547
|
+
"**/*security*"
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Set `always_security_review: true` to run the security pass on every task, regardless of file path.
|
|
554
|
+
|
|
555
|
+
### Integration Analysis
|
|
556
|
+
|
|
557
|
+
Control whether contract change detection triggers impact analysis:
|
|
558
|
+
|
|
559
|
+
```jsonc
|
|
560
|
+
{
|
|
561
|
+
"integration_analysis": {
|
|
562
|
+
"enabled": true // default: true
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
519
567
|
> **Architect is exempt/unlimited by default:** The architect agent has no guardrail limits by default. To override, add a `profiles.architect` entry in your guardrails config.
|
|
520
568
|
|
|
521
569
|
### Per-Invocation Budgets
|
|
@@ -549,7 +597,7 @@ This prevents long-running projects from accumulating session-wide counters that
|
|
|
549
597
|
| Execution | Serial (predictable) | Parallel (chaotic) | Parallel | Configurable |
|
|
550
598
|
| Planning | Phased with acceptance criteria | Ad-hoc | Role-based | Graph-based |
|
|
551
599
|
| Memory | Persistent `.swarm/` files | Session only | Session only | Checkpoints |
|
|
552
|
-
| QA |
|
|
600
|
+
| QA | Dual-pass per-task (review + security + adversarial) | Optional | Optional | Manual |
|
|
553
601
|
| Model mixing | Per-agent configuration | Limited | Limited | Manual |
|
|
554
602
|
| Resume projects | β
Native | β | β | Partial |
|
|
555
603
|
| SME domains | Open-domain (any) | Generic | Generic | Generic |
|
|
@@ -561,7 +609,7 @@ This prevents long-running projects from accumulating session-wide counters that
|
|
|
561
609
|
|
|
562
610
|
1. **Plan before code** - Documented phases with acceptance criteria
|
|
563
611
|
2. **One task at a time** - Focused work, quality output
|
|
564
|
-
3. **Review everything immediately** -
|
|
612
|
+
3. **Review everything immediately** - Dual-pass review (correctness + security) with adversarial testing per task
|
|
565
613
|
4. **Cache SME knowledge** - Don't re-ask answered questions
|
|
566
614
|
5. **Persistent memory** - `.swarm/` files survive sessions
|
|
567
615
|
6. **Serial execution** - Predictable, debuggable, no race conditions
|
|
@@ -582,7 +630,7 @@ bun test
|
|
|
582
630
|
bun test tests/unit/config/schema.test.ts
|
|
583
631
|
```
|
|
584
632
|
|
|
585
|
-
|
|
633
|
+
1188 tests across 53+ files covering config, tools, agents, hooks, commands, state, guardrails, evidence, plan schemas, circuit breaker race conditions, invocation windows, multi-invocation isolation, security categories, review/integration schemas, and diff tool. Uses Bun's built-in test runner β zero additional test dependencies.
|
|
586
634
|
|
|
587
635
|
## Troubleshooting
|
|
588
636
|
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export { createArchitectAgent } from './architect';
|
|
|
20
20
|
export { createCoderAgent } from './coder';
|
|
21
21
|
export { createCriticAgent } from './critic';
|
|
22
22
|
export { createExplorerAgent } from './explorer';
|
|
23
|
-
export { createReviewerAgent } from './reviewer';
|
|
23
|
+
export { createReviewerAgent, SECURITY_CATEGORIES, type SecurityCategory, } from './reviewer';
|
|
24
24
|
export { createSMEAgent } from './sme';
|
|
25
25
|
export { createTestEngineerAgent } from './test-engineer';
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { AgentDefinition } from './architect';
|
|
2
|
+
/** OWASP Top 10 2021 categories for security-focused review passes */
|
|
3
|
+
export declare const SECURITY_CATEGORIES: readonly ["broken-access-control", "cryptographic-failures", "injection", "insecure-design", "security-misconfiguration", "vulnerable-components", "auth-failures", "data-integrity-failures", "logging-monitoring-failures", "ssrf"];
|
|
4
|
+
export type SecurityCategory = (typeof SECURITY_CATEGORIES)[number];
|
|
2
5
|
export declare function createReviewerAgent(model: string, customPrompt?: string, customAppendPrompt?: string): AgentDefinition;
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare function deepMerge<T extends Record<string, unknown>>(base?: T, o
|
|
|
13
13
|
* 2. Project config: <directory>/.opencode/opencode-swarm.json
|
|
14
14
|
*
|
|
15
15
|
* Project config takes precedence. Nested objects are deep-merged.
|
|
16
|
+
* IMPORTANT: Raw configs are merged BEFORE Zod parsing so that
|
|
17
|
+
* Zod defaults don't override explicit user values.
|
|
16
18
|
*/
|
|
17
19
|
export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
18
20
|
/**
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -128,6 +128,15 @@ export declare const SummaryConfigSchema: z.ZodObject<{
|
|
|
128
128
|
retention_days: z.ZodDefault<z.ZodNumber>;
|
|
129
129
|
}, z.core.$strip>;
|
|
130
130
|
export type SummaryConfig = z.infer<typeof SummaryConfigSchema>;
|
|
131
|
+
export declare const ReviewPassesConfigSchema: z.ZodObject<{
|
|
132
|
+
always_security_review: z.ZodDefault<z.ZodBoolean>;
|
|
133
|
+
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
134
|
+
}, z.core.$strip>;
|
|
135
|
+
export type ReviewPassesConfig = z.infer<typeof ReviewPassesConfigSchema>;
|
|
136
|
+
export declare const IntegrationAnalysisConfigSchema: z.ZodObject<{
|
|
137
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
138
|
+
}, z.core.$strip>;
|
|
139
|
+
export type IntegrationAnalysisConfig = z.infer<typeof IntegrationAnalysisConfigSchema>;
|
|
131
140
|
export declare const GuardrailsProfileSchema: z.ZodObject<{
|
|
132
141
|
max_tool_calls: z.ZodOptional<z.ZodNumber>;
|
|
133
142
|
max_duration_minutes: z.ZodOptional<z.ZodNumber>;
|
|
@@ -282,6 +291,14 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
282
291
|
max_stored_bytes: z.ZodDefault<z.ZodNumber>;
|
|
283
292
|
retention_days: z.ZodDefault<z.ZodNumber>;
|
|
284
293
|
}, z.core.$strip>>;
|
|
294
|
+
review_passes: z.ZodOptional<z.ZodObject<{
|
|
295
|
+
always_security_review: z.ZodDefault<z.ZodBoolean>;
|
|
296
|
+
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
297
|
+
}, z.core.$strip>>;
|
|
298
|
+
integration_analysis: z.ZodOptional<z.ZodObject<{
|
|
299
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
300
|
+
}, z.core.$strip>>;
|
|
301
|
+
_loadedFromFile: z.ZodDefault<z.ZodBoolean>;
|
|
285
302
|
}, z.core.$strip>;
|
|
286
303
|
export type PluginConfig = z.infer<typeof PluginConfigSchema>;
|
|
287
304
|
export type { AgentName, PipelineAgentName, QAAgentName, } from './constants';
|
|
@@ -8,7 +8,7 @@ import type { PluginConfig } from '../config/schema';
|
|
|
8
8
|
/**
|
|
9
9
|
* Creates the chat.message hook for delegation tracking.
|
|
10
10
|
*/
|
|
11
|
-
export declare function createDelegationTrackerHook(config: PluginConfig): (input: {
|
|
11
|
+
export declare function createDelegationTrackerHook(config: PluginConfig, guardrailsEnabled?: boolean): (input: {
|
|
12
12
|
sessionID: string;
|
|
13
13
|
agent?: string;
|
|
14
14
|
}, output: Record<string, unknown>) => Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -13630,6 +13630,21 @@ var SummaryConfigSchema = exports_external.object({
|
|
|
13630
13630
|
max_stored_bytes: exports_external.number().min(10240).max(104857600).default(10485760),
|
|
13631
13631
|
retention_days: exports_external.number().min(1).max(365).default(7)
|
|
13632
13632
|
});
|
|
13633
|
+
var ReviewPassesConfigSchema = exports_external.object({
|
|
13634
|
+
always_security_review: exports_external.boolean().default(false),
|
|
13635
|
+
security_globs: exports_external.array(exports_external.string()).default([
|
|
13636
|
+
"**/auth/**",
|
|
13637
|
+
"**/api/**",
|
|
13638
|
+
"**/crypto/**",
|
|
13639
|
+
"**/security/**",
|
|
13640
|
+
"**/middleware/**",
|
|
13641
|
+
"**/session/**",
|
|
13642
|
+
"**/token/**"
|
|
13643
|
+
])
|
|
13644
|
+
});
|
|
13645
|
+
var IntegrationAnalysisConfigSchema = exports_external.object({
|
|
13646
|
+
enabled: exports_external.boolean().default(true)
|
|
13647
|
+
});
|
|
13633
13648
|
var GuardrailsProfileSchema = exports_external.object({
|
|
13634
13649
|
max_tool_calls: exports_external.number().min(0).max(1000).optional(),
|
|
13635
13650
|
max_duration_minutes: exports_external.number().min(0).max(480).optional(),
|
|
@@ -13729,7 +13744,10 @@ var PluginConfigSchema = exports_external.object({
|
|
|
13729
13744
|
context_budget: ContextBudgetConfigSchema.optional(),
|
|
13730
13745
|
guardrails: GuardrailsConfigSchema.optional(),
|
|
13731
13746
|
evidence: EvidenceConfigSchema.optional(),
|
|
13732
|
-
summaries: SummaryConfigSchema.optional()
|
|
13747
|
+
summaries: SummaryConfigSchema.optional(),
|
|
13748
|
+
review_passes: ReviewPassesConfigSchema.optional(),
|
|
13749
|
+
integration_analysis: IntegrationAnalysisConfigSchema.optional(),
|
|
13750
|
+
_loadedFromFile: exports_external.boolean().default(false)
|
|
13733
13751
|
});
|
|
13734
13752
|
|
|
13735
13753
|
// src/config/loader.ts
|
|
@@ -13739,25 +13757,26 @@ var MAX_CONFIG_FILE_BYTES = 102400;
|
|
|
13739
13757
|
function getUserConfigDir() {
|
|
13740
13758
|
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
13741
13759
|
}
|
|
13742
|
-
function
|
|
13760
|
+
function loadRawConfigFromPath(configPath) {
|
|
13743
13761
|
try {
|
|
13744
13762
|
const stats = fs.statSync(configPath);
|
|
13745
13763
|
if (stats.size > MAX_CONFIG_FILE_BYTES) {
|
|
13746
13764
|
console.warn(`[opencode-swarm] Config file too large (max 100 KB): ${configPath}`);
|
|
13765
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13747
13766
|
return null;
|
|
13748
13767
|
}
|
|
13749
13768
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
13750
13769
|
const rawConfig = JSON.parse(content);
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
console.warn(
|
|
13754
|
-
console.warn(result.error.format());
|
|
13770
|
+
if (typeof rawConfig !== "object" || rawConfig === null || Array.isArray(rawConfig)) {
|
|
13771
|
+
console.warn(`[opencode-swarm] Invalid config at ${configPath}: expected an object`);
|
|
13772
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13755
13773
|
return null;
|
|
13756
13774
|
}
|
|
13757
|
-
return
|
|
13775
|
+
return rawConfig;
|
|
13758
13776
|
} catch (error48) {
|
|
13759
13777
|
if (error48 instanceof Error && "code" in error48 && error48.code !== "ENOENT") {
|
|
13760
|
-
console.warn(`[opencode-swarm]
|
|
13778
|
+
console.warn(`[opencode-swarm] \u26A0\uFE0F CONFIG LOAD FAILURE \u2014 config exists at ${configPath} but could not be loaded: ${error48.message}`);
|
|
13779
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13761
13780
|
}
|
|
13762
13781
|
return null;
|
|
13763
13782
|
}
|
|
@@ -13779,30 +13798,36 @@ function deepMergeInternal(base, override, depth) {
|
|
|
13779
13798
|
}
|
|
13780
13799
|
return result;
|
|
13781
13800
|
}
|
|
13782
|
-
function deepMerge(base, override) {
|
|
13783
|
-
if (!base)
|
|
13784
|
-
return override;
|
|
13785
|
-
if (!override)
|
|
13786
|
-
return base;
|
|
13787
|
-
return deepMergeInternal(base, override, 0);
|
|
13788
|
-
}
|
|
13789
13801
|
function loadPluginConfig(directory) {
|
|
13790
13802
|
const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
13791
13803
|
const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13804
|
+
const rawUserConfig = loadRawConfigFromPath(userConfigPath);
|
|
13805
|
+
const rawProjectConfig = loadRawConfigFromPath(projectConfigPath);
|
|
13806
|
+
const loadedFromFile = rawUserConfig !== null || rawProjectConfig !== null;
|
|
13807
|
+
let mergedRaw = rawUserConfig ?? {};
|
|
13808
|
+
if (rawProjectConfig) {
|
|
13809
|
+
mergedRaw = deepMergeInternal(mergedRaw, rawProjectConfig, 0);
|
|
13810
|
+
}
|
|
13811
|
+
const result = PluginConfigSchema.safeParse(mergedRaw);
|
|
13812
|
+
if (!result.success) {
|
|
13813
|
+
if (rawUserConfig) {
|
|
13814
|
+
const userResult = PluginConfigSchema.safeParse(rawUserConfig);
|
|
13815
|
+
if (userResult.success) {
|
|
13816
|
+
console.warn("[opencode-swarm] Project config ignored due to validation errors. Using user config.");
|
|
13817
|
+
return { ...userResult.data, _loadedFromFile: true };
|
|
13818
|
+
}
|
|
13819
|
+
}
|
|
13820
|
+
console.warn("[opencode-swarm] Merged config validation failed:");
|
|
13821
|
+
console.warn(result.error.format());
|
|
13822
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13823
|
+
return {
|
|
13824
|
+
max_iterations: 5,
|
|
13825
|
+
qa_retry_limit: 3,
|
|
13826
|
+
inject_phase_reminders: true,
|
|
13827
|
+
_loadedFromFile: false
|
|
13803
13828
|
};
|
|
13804
13829
|
}
|
|
13805
|
-
return
|
|
13830
|
+
return { ...result.data, _loadedFromFile: loadedFromFile };
|
|
13806
13831
|
}
|
|
13807
13832
|
function loadAgentPrompt(agentName) {
|
|
13808
13833
|
const promptsDir = path.join(getUserConfigDir(), "opencode", PROMPTS_DIR_NAME);
|
|
@@ -14021,17 +14046,13 @@ You THINK. Subagents DO. You have the largest context window and strongest reaso
|
|
|
14021
14046
|
- If NEEDS_REVISION: Revise plan and re-submit to critic (max 2 cycles)
|
|
14022
14047
|
- If REJECTED after 2 cycles: Escalate to user with explanation
|
|
14023
14048
|
- ONLY AFTER critic approval: Proceed to implementation (Phase 3+)
|
|
14024
|
-
7. **MANDATORY QA GATE (Execute AFTER every coder task)
|
|
14025
|
-
-
|
|
14026
|
-
-
|
|
14027
|
-
-
|
|
14028
|
-
|
|
14029
|
-
|
|
14030
|
-
-
|
|
14031
|
-
- Step E: Wait for test verdict
|
|
14032
|
-
- If VERDICT: FAIL \u2192 Send failure details back to {{AGENT_PREFIX}}coder (return to Step A)
|
|
14033
|
-
- If VERDICT: PASS \u2192 Mark task complete, proceed to next task
|
|
14034
|
-
8. **NEVER skip the QA gate**: You cannot delegate to {{AGENT_PREFIX}}coder for a new task until the previous task passes BOTH reviewer approval AND test_engineer verification. The sequence is ALWAYS: coder \u2192 reviewer \u2192 test_engineer \u2192 next_coder.
|
|
14049
|
+
7. **MANDATORY QA GATE (Execute AFTER every coder task)** \u2014 sequence: coder \u2192 diff \u2192 review \u2192 security review \u2192 verification tests \u2192 adversarial tests \u2192 next task.
|
|
14050
|
+
- After coder completes: run \`diff\` tool. If \`hasContractChanges\` is true \u2192 delegate {{AGENT_PREFIX}}explorer for integration impact analysis. BREAKING \u2192 return to coder. COMPATIBLE \u2192 proceed.
|
|
14051
|
+
- Delegate {{AGENT_PREFIX}}reviewer with CHECK dimensions. REJECTED \u2192 return to coder (max {{QA_RETRY_LIMIT}} attempts). APPROVED \u2192 continue.
|
|
14052
|
+
- If file matches security globs (auth, api, crypto, security, middleware, session, token) OR coder output contains security keywords \u2192 delegate {{AGENT_PREFIX}}reviewer AGAIN with security-only CHECK. REJECTED \u2192 return to coder.
|
|
14053
|
+
- Delegate {{AGENT_PREFIX}}test_engineer for verification tests. FAIL \u2192 return to coder.
|
|
14054
|
+
- Delegate {{AGENT_PREFIX}}test_engineer for adversarial tests (attack vectors only). FAIL \u2192 return to coder.
|
|
14055
|
+
- All pass \u2192 mark task complete, proceed to next task.
|
|
14035
14056
|
|
|
14036
14057
|
## AGENTS
|
|
14037
14058
|
|
|
@@ -14044,6 +14065,8 @@ You THINK. Subagents DO. You have the largest context window and strongest reaso
|
|
|
14044
14065
|
|
|
14045
14066
|
SMEs advise only. Reviewer and critic review only. None of them write code.
|
|
14046
14067
|
|
|
14068
|
+
Available Tools: diff (structured git diff with contract change detection)
|
|
14069
|
+
|
|
14047
14070
|
## DELEGATION FORMAT
|
|
14048
14071
|
|
|
14049
14072
|
All delegations use this structure:
|
|
@@ -14099,6 +14122,24 @@ PLAN: [paste the plan.md content]
|
|
|
14099
14122
|
CONTEXT: [codebase summary from explorer]
|
|
14100
14123
|
OUTPUT: VERDICT + CONFIDENCE + ISSUES + SUMMARY
|
|
14101
14124
|
|
|
14125
|
+
{{AGENT_PREFIX}}reviewer
|
|
14126
|
+
TASK: Security-only review of login validation
|
|
14127
|
+
FILE: src/auth/login.ts
|
|
14128
|
+
CHECK: [security-only] \u2014 evaluate against OWASP Top 10, scan for hardcoded secrets, injection vectors, insecure crypto, missing input validation
|
|
14129
|
+
OUTPUT: VERDICT + RISK + SECURITY ISSUES ONLY
|
|
14130
|
+
|
|
14131
|
+
{{AGENT_PREFIX}}test_engineer
|
|
14132
|
+
TASK: Adversarial security testing
|
|
14133
|
+
FILE: src/auth/login.ts
|
|
14134
|
+
CONSTRAINT: ONLY attack vectors \u2014 malformed inputs, oversized payloads, injection attempts, auth bypass, boundary violations
|
|
14135
|
+
OUTPUT: Test file + VERDICT: PASS/FAIL
|
|
14136
|
+
|
|
14137
|
+
{{AGENT_PREFIX}}explorer
|
|
14138
|
+
TASK: Integration impact analysis
|
|
14139
|
+
INPUT: Contract changes detected: [list from diff tool]
|
|
14140
|
+
OUTPUT: BREAKING CHANGES + CONSUMERS AFFECTED + VERDICT: BREAKING/COMPATIBLE
|
|
14141
|
+
CONSTRAINT: Read-only. grep for imports/usages of changed exports.
|
|
14142
|
+
|
|
14102
14143
|
## WORKFLOW
|
|
14103
14144
|
|
|
14104
14145
|
### Phase 0: Resume Check
|
|
@@ -14150,15 +14191,13 @@ Delegate plan to {{AGENT_PREFIX}}critic for review BEFORE any implementation beg
|
|
|
14150
14191
|
### Phase 5: Execute
|
|
14151
14192
|
For each task (respecting dependencies):
|
|
14152
14193
|
|
|
14153
|
-
5a. {{AGENT_PREFIX}}coder - Implement
|
|
14154
|
-
5b. {{AGENT_PREFIX}}
|
|
14155
|
-
5c.
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
5e. If test VERDICT is FAIL \u2192 Send failures to {{AGENT_PREFIX}}coder for fixes, then re-run from 5b.
|
|
14161
|
-
5f. Update plan.md [x], proceed to next task (ONLY if tests PASS)
|
|
14194
|
+
5a. {{AGENT_PREFIX}}coder - Implement
|
|
14195
|
+
5b. Run \`diff\` tool. If \`hasContractChanges\` \u2192 {{AGENT_PREFIX}}explorer integration analysis. BREAKING \u2192 coder retry.
|
|
14196
|
+
5c. {{AGENT_PREFIX}}reviewer - General review. REJECTED (< {{QA_RETRY_LIMIT}}) \u2192 coder retry. REJECTED ({{QA_RETRY_LIMIT}}) \u2192 escalate.
|
|
14197
|
+
5d. Security gate: if file matches security globs or content has security keywords \u2192 {{AGENT_PREFIX}}reviewer security-only. REJECTED \u2192 coder retry.
|
|
14198
|
+
5e. {{AGENT_PREFIX}}test_engineer - Verification tests. FAIL \u2192 coder retry from 5c.
|
|
14199
|
+
5f. {{AGENT_PREFIX}}test_engineer - Adversarial tests. FAIL \u2192 coder retry from 5c.
|
|
14200
|
+
5g. Update plan.md [x], proceed to next task.
|
|
14162
14201
|
|
|
14163
14202
|
### Phase 6: Phase Complete
|
|
14164
14203
|
1. {{AGENT_PREFIX}}explorer - Rescan
|
|
@@ -16828,7 +16867,7 @@ ${originalText}`;
|
|
|
16828
16867
|
};
|
|
16829
16868
|
}
|
|
16830
16869
|
// src/hooks/delegation-tracker.ts
|
|
16831
|
-
function createDelegationTrackerHook(config2) {
|
|
16870
|
+
function createDelegationTrackerHook(config2, guardrailsEnabled = true) {
|
|
16832
16871
|
return async (input, _output) => {
|
|
16833
16872
|
const now = Date.now();
|
|
16834
16873
|
if (!input.agent || input.agent === "") {
|
|
@@ -16850,7 +16889,7 @@ function createDelegationTrackerHook(config2) {
|
|
|
16850
16889
|
const isArchitect = strippedAgent === ORCHESTRATOR_NAME;
|
|
16851
16890
|
const session = ensureAgentSession(input.sessionID, agentName);
|
|
16852
16891
|
session.delegationActive = !isArchitect;
|
|
16853
|
-
if (!isArchitect) {
|
|
16892
|
+
if (!isArchitect && guardrailsEnabled) {
|
|
16854
16893
|
beginInvocation(input.sessionID, agentName);
|
|
16855
16894
|
}
|
|
16856
16895
|
if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== agentName) {
|
|
@@ -17025,18 +17064,11 @@ function createGuardrailsHooks(config2) {
|
|
|
17025
17064
|
return;
|
|
17026
17065
|
}
|
|
17027
17066
|
const lastMessage = messages[messages.length - 1];
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
for (const [id] of swarmState.agentSessions) {
|
|
17032
|
-
const window = getActiveWindow(id);
|
|
17033
|
-
if (window && (window.warningIssued || window.hardLimitHit)) {
|
|
17034
|
-
targetWindow = window;
|
|
17035
|
-
sessionId = id;
|
|
17036
|
-
break;
|
|
17037
|
-
}
|
|
17038
|
-
}
|
|
17067
|
+
const sessionId = lastMessage.info?.sessionID;
|
|
17068
|
+
if (!sessionId) {
|
|
17069
|
+
return;
|
|
17039
17070
|
}
|
|
17071
|
+
const targetWindow = getActiveWindow(sessionId);
|
|
17040
17072
|
if (!targetWindow || !targetWindow.warningIssued && !targetWindow.hardLimitHit) {
|
|
17041
17073
|
return;
|
|
17042
17074
|
}
|
|
@@ -17244,6 +17276,12 @@ function createSystemEnhancerHook(config2, directory) {
|
|
|
17244
17276
|
}
|
|
17245
17277
|
}
|
|
17246
17278
|
tryInject("[SWARM HINT] Large tool outputs may be auto-summarized. Use /swarm retrieve <id> to get the full content if needed.");
|
|
17279
|
+
if (config2.review_passes?.always_security_review) {
|
|
17280
|
+
tryInject("[SWARM CONFIG] Security review pass is MANDATORY for ALL tasks. Skip file-pattern check \u2014 always run security-only reviewer pass after general review APPROVED.");
|
|
17281
|
+
}
|
|
17282
|
+
if (config2.integration_analysis?.enabled === false) {
|
|
17283
|
+
tryInject("[SWARM CONFIG] Integration analysis is DISABLED. Skip diff tool and integration impact analysis after coder tasks.");
|
|
17284
|
+
}
|
|
17247
17285
|
return;
|
|
17248
17286
|
}
|
|
17249
17287
|
const userScoringConfig = config2.context_budget?.scoring;
|
|
@@ -17323,6 +17361,28 @@ function createSystemEnhancerHook(config2, directory) {
|
|
|
17323
17361
|
}
|
|
17324
17362
|
}
|
|
17325
17363
|
}
|
|
17364
|
+
if (config2.review_passes?.always_security_review) {
|
|
17365
|
+
const text = "[SWARM CONFIG] Security review pass is MANDATORY for ALL tasks. Skip file-pattern check \u2014 always run security-only reviewer pass after general review APPROVED.";
|
|
17366
|
+
candidates.push({
|
|
17367
|
+
id: `candidate-${idCounter++}`,
|
|
17368
|
+
kind: "phase",
|
|
17369
|
+
text,
|
|
17370
|
+
tokens: estimateTokens(text),
|
|
17371
|
+
priority: 1,
|
|
17372
|
+
metadata: { contentType: "prose" }
|
|
17373
|
+
});
|
|
17374
|
+
}
|
|
17375
|
+
if (config2.integration_analysis?.enabled === false) {
|
|
17376
|
+
const text = "[SWARM CONFIG] Integration analysis is DISABLED. Skip diff tool and integration impact analysis after coder tasks.";
|
|
17377
|
+
candidates.push({
|
|
17378
|
+
id: `candidate-${idCounter++}`,
|
|
17379
|
+
kind: "phase",
|
|
17380
|
+
text,
|
|
17381
|
+
tokens: estimateTokens(text),
|
|
17382
|
+
priority: 1,
|
|
17383
|
+
metadata: { contentType: "prose" }
|
|
17384
|
+
});
|
|
17385
|
+
}
|
|
17326
17386
|
const ranked = rankCandidates(candidates, effectiveConfig);
|
|
17327
17387
|
for (const candidate of ranked) {
|
|
17328
17388
|
if (injectedTokens + candidate.tokens > maxInjectionTokens) {
|
|
@@ -17517,6 +17577,9 @@ function createToolSummarizerHook(config2, directory) {
|
|
|
17517
17577
|
}
|
|
17518
17578
|
};
|
|
17519
17579
|
}
|
|
17580
|
+
// src/tools/diff.ts
|
|
17581
|
+
import { execSync } from "child_process";
|
|
17582
|
+
|
|
17520
17583
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
17521
17584
|
var exports_external2 = {};
|
|
17522
17585
|
__export(exports_external2, {
|
|
@@ -29837,7 +29900,149 @@ function tool(input) {
|
|
|
29837
29900
|
return input;
|
|
29838
29901
|
}
|
|
29839
29902
|
tool.schema = exports_external2;
|
|
29840
|
-
|
|
29903
|
+
// src/tools/diff.ts
|
|
29904
|
+
var MAX_DIFF_LINES = 500;
|
|
29905
|
+
var DIFF_TIMEOUT_MS = 30000;
|
|
29906
|
+
var MAX_BUFFER_BYTES = 5 * 1024 * 1024;
|
|
29907
|
+
var CONTRACT_PATTERNS = [
|
|
29908
|
+
/^[+-]\s*export\s+(function|const|class|interface|type|enum|default)\b/,
|
|
29909
|
+
/^[+-]\s*(interface|type)\s+\w+/,
|
|
29910
|
+
/^[+-]\s*public\s+/,
|
|
29911
|
+
/^[+-]\s*(async\s+)?function\s+\w+\s*\(/
|
|
29912
|
+
];
|
|
29913
|
+
var SAFE_REF_PATTERN = /^[a-zA-Z0-9._\-/~^@{}]+$/;
|
|
29914
|
+
var MAX_REF_LENGTH = 256;
|
|
29915
|
+
var MAX_PATH_LENGTH = 500;
|
|
29916
|
+
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
29917
|
+
function validateBase(base) {
|
|
29918
|
+
if (base.length > MAX_REF_LENGTH) {
|
|
29919
|
+
return `base ref exceeds maximum length of ${MAX_REF_LENGTH}`;
|
|
29920
|
+
}
|
|
29921
|
+
if (!SAFE_REF_PATTERN.test(base)) {
|
|
29922
|
+
return "base contains invalid characters for git ref";
|
|
29923
|
+
}
|
|
29924
|
+
return null;
|
|
29925
|
+
}
|
|
29926
|
+
function validatePaths(paths) {
|
|
29927
|
+
if (!paths)
|
|
29928
|
+
return null;
|
|
29929
|
+
for (const path7 of paths) {
|
|
29930
|
+
if (!path7 || path7.length === 0) {
|
|
29931
|
+
return "empty path not allowed";
|
|
29932
|
+
}
|
|
29933
|
+
if (path7.length > MAX_PATH_LENGTH) {
|
|
29934
|
+
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
29935
|
+
}
|
|
29936
|
+
if (SHELL_METACHARACTERS.test(path7)) {
|
|
29937
|
+
return "path contains shell metacharacters";
|
|
29938
|
+
}
|
|
29939
|
+
}
|
|
29940
|
+
return null;
|
|
29941
|
+
}
|
|
29942
|
+
var diff = tool({
|
|
29943
|
+
description: "Analyze git diff for changed files, exports, interfaces, and function signatures. Returns structured output with contract change detection.",
|
|
29944
|
+
args: {
|
|
29945
|
+
base: tool.schema.string().optional().describe('Base ref to diff against (default: HEAD). Use "staged" for staged changes, "unstaged" for working tree changes.'),
|
|
29946
|
+
paths: tool.schema.array(tool.schema.string()).optional().describe("Optional file paths to restrict diff scope.")
|
|
29947
|
+
},
|
|
29948
|
+
async execute(args, _context) {
|
|
29949
|
+
try {
|
|
29950
|
+
const base = args.base ?? "HEAD";
|
|
29951
|
+
const pathSpec = args.paths?.length ? "-- " + args.paths.join(" ") : "";
|
|
29952
|
+
const baseValidationError = validateBase(base);
|
|
29953
|
+
if (baseValidationError) {
|
|
29954
|
+
const errorResult = {
|
|
29955
|
+
error: `invalid base: ${baseValidationError}`,
|
|
29956
|
+
files: [],
|
|
29957
|
+
contractChanges: [],
|
|
29958
|
+
hasContractChanges: false
|
|
29959
|
+
};
|
|
29960
|
+
return JSON.stringify(errorResult, null, 2);
|
|
29961
|
+
}
|
|
29962
|
+
const pathsValidationError = validatePaths(args.paths);
|
|
29963
|
+
if (pathsValidationError) {
|
|
29964
|
+
const errorResult = {
|
|
29965
|
+
error: `invalid paths: ${pathsValidationError}`,
|
|
29966
|
+
files: [],
|
|
29967
|
+
contractChanges: [],
|
|
29968
|
+
hasContractChanges: false
|
|
29969
|
+
};
|
|
29970
|
+
return JSON.stringify(errorResult, null, 2);
|
|
29971
|
+
}
|
|
29972
|
+
let gitCmd;
|
|
29973
|
+
if (base === "staged") {
|
|
29974
|
+
gitCmd = "git --no-pager diff --cached";
|
|
29975
|
+
} else if (base === "unstaged") {
|
|
29976
|
+
gitCmd = "git --no-pager diff";
|
|
29977
|
+
} else {
|
|
29978
|
+
gitCmd = `git --no-pager diff ${base}`;
|
|
29979
|
+
}
|
|
29980
|
+
const numstatOutput = execSync(gitCmd + " --numstat " + pathSpec, {
|
|
29981
|
+
encoding: "utf-8",
|
|
29982
|
+
timeout: DIFF_TIMEOUT_MS
|
|
29983
|
+
});
|
|
29984
|
+
const fullDiffOutput = execSync(gitCmd + " -U3 " + pathSpec, {
|
|
29985
|
+
encoding: "utf-8",
|
|
29986
|
+
timeout: DIFF_TIMEOUT_MS,
|
|
29987
|
+
maxBuffer: MAX_BUFFER_BYTES
|
|
29988
|
+
});
|
|
29989
|
+
const files = [];
|
|
29990
|
+
const numstatLines = numstatOutput.split(`
|
|
29991
|
+
`);
|
|
29992
|
+
for (const line of numstatLines) {
|
|
29993
|
+
if (!line.trim())
|
|
29994
|
+
continue;
|
|
29995
|
+
const parts = line.split("\t");
|
|
29996
|
+
if (parts.length >= 3) {
|
|
29997
|
+
const additions = parseInt(parts[0]) || 0;
|
|
29998
|
+
const deletions = parseInt(parts[1]) || 0;
|
|
29999
|
+
const path7 = parts[2];
|
|
30000
|
+
files.push({ path: path7, additions, deletions });
|
|
30001
|
+
}
|
|
30002
|
+
}
|
|
30003
|
+
const contractChanges = [];
|
|
30004
|
+
const diffLines = fullDiffOutput.split(`
|
|
30005
|
+
`);
|
|
30006
|
+
let currentFile = "";
|
|
30007
|
+
for (const line of diffLines) {
|
|
30008
|
+
const gitLineMatch = line.match(/^diff --git.* b\/(.+)$/);
|
|
30009
|
+
if (gitLineMatch) {
|
|
30010
|
+
currentFile = gitLineMatch[1];
|
|
30011
|
+
}
|
|
30012
|
+
for (const pattern of CONTRACT_PATTERNS) {
|
|
30013
|
+
if (pattern.test(line)) {
|
|
30014
|
+
const trimmed = line.trim();
|
|
30015
|
+
if (currentFile) {
|
|
30016
|
+
contractChanges.push(`[${currentFile}] ${trimmed}`);
|
|
30017
|
+
} else {
|
|
30018
|
+
contractChanges.push(trimmed);
|
|
30019
|
+
}
|
|
30020
|
+
break;
|
|
30021
|
+
}
|
|
30022
|
+
}
|
|
30023
|
+
}
|
|
30024
|
+
const hasContractChanges = contractChanges.length > 0;
|
|
30025
|
+
const fileCount = files.length;
|
|
30026
|
+
const truncated = diffLines.length > MAX_DIFF_LINES;
|
|
30027
|
+
const summary = truncated ? `${fileCount} files changed. Contract changes: ${hasContractChanges ? "YES" : "NO"}. (truncated to ${MAX_DIFF_LINES} lines)` : `${fileCount} files changed. Contract changes: ${hasContractChanges ? "YES" : "NO"}`;
|
|
30028
|
+
const result = {
|
|
30029
|
+
files,
|
|
30030
|
+
contractChanges,
|
|
30031
|
+
hasContractChanges,
|
|
30032
|
+
summary
|
|
30033
|
+
};
|
|
30034
|
+
return JSON.stringify(result, null, 2);
|
|
30035
|
+
} catch (e) {
|
|
30036
|
+
const errorResult = {
|
|
30037
|
+
error: e instanceof Error ? `git diff failed: ${e.constructor.name}` : "git diff failed: unknown error",
|
|
30038
|
+
files: [],
|
|
30039
|
+
contractChanges: [],
|
|
30040
|
+
hasContractChanges: false
|
|
30041
|
+
};
|
|
30042
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30043
|
+
}
|
|
30044
|
+
}
|
|
30045
|
+
});
|
|
29841
30046
|
// src/tools/domain-detector.ts
|
|
29842
30047
|
var DOMAIN_PATTERNS = {
|
|
29843
30048
|
windows: [
|
|
@@ -30222,9 +30427,10 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30222
30427
|
const contextBudgetHandler = createContextBudgetHandler(config3);
|
|
30223
30428
|
const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])));
|
|
30224
30429
|
const activityHooks = createAgentActivityHooks(config3, ctx.directory);
|
|
30225
|
-
const delegationHandler = createDelegationTrackerHook(config3);
|
|
30226
30430
|
const delegationGateHandler = createDelegationGateHook(config3);
|
|
30227
|
-
const
|
|
30431
|
+
const guardrailsFallback = config3._loadedFromFile ? config3.guardrails ?? {} : { ...config3.guardrails, enabled: false };
|
|
30432
|
+
const guardrailsConfig = GuardrailsConfigSchema.parse(guardrailsFallback);
|
|
30433
|
+
const delegationHandler = createDelegationTrackerHook(config3, guardrailsConfig.enabled);
|
|
30228
30434
|
const guardrailsHooks = createGuardrailsHooks(guardrailsConfig);
|
|
30229
30435
|
const summaryConfig = SummaryConfigSchema.parse(config3.summaries ?? {});
|
|
30230
30436
|
const toolSummarizerHook = createToolSummarizerHook(summaryConfig, ctx.directory);
|
|
@@ -30251,7 +30457,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30251
30457
|
tool: {
|
|
30252
30458
|
detect_domains,
|
|
30253
30459
|
extract_code_blocks,
|
|
30254
|
-
gitingest
|
|
30460
|
+
gitingest,
|
|
30461
|
+
diff
|
|
30255
30462
|
},
|
|
30256
30463
|
config: async (opencodeConfig) => {
|
|
30257
30464
|
if (!opencodeConfig.agent) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin';
|
|
2
|
+
export interface DiffResult {
|
|
3
|
+
files: Array<{
|
|
4
|
+
path: string;
|
|
5
|
+
additions: number;
|
|
6
|
+
deletions: number;
|
|
7
|
+
}>;
|
|
8
|
+
contractChanges: string[];
|
|
9
|
+
hasContractChanges: boolean;
|
|
10
|
+
summary: string;
|
|
11
|
+
}
|
|
12
|
+
export interface DiffErrorResult {
|
|
13
|
+
error: string;
|
|
14
|
+
files: [];
|
|
15
|
+
contractChanges: [];
|
|
16
|
+
hasContractChanges: false;
|
|
17
|
+
}
|
|
18
|
+
export declare const diff: ReturnType<typeof tool>;
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { type DiffErrorResult, type DiffResult, diff } from './diff';
|
|
1
2
|
export { detect_domains } from './domain-detector';
|
|
2
3
|
export { extract_code_blocks } from './file-extractor';
|
|
3
|
-
export {
|
|
4
|
+
export { fetchGitingest, type GitingestArgs, gitingest } from './gitingest';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|