opencode-swarm 5.2.0 β 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -14
- package/dist/agents/designer.d.ts +2 -0
- package/dist/agents/docs.d.ts +2 -0
- package/dist/agents/index.d.ts +3 -1
- package/dist/agents/reviewer.d.ts +3 -0
- package/dist/config/constants.d.ts +2 -2
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/schema.d.ts +37 -0
- package/dist/hooks/delegation-tracker.d.ts +1 -1
- package/dist/index.js +625 -68
- 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
|
@@ -19,7 +19,9 @@ export declare function getAgentConfigs(config?: PluginConfig): Record<string, S
|
|
|
19
19
|
export { createArchitectAgent } from './architect';
|
|
20
20
|
export { createCoderAgent } from './coder';
|
|
21
21
|
export { createCriticAgent } from './critic';
|
|
22
|
+
export { createDesignerAgent } from './designer';
|
|
23
|
+
export { createDocsAgent } from './docs';
|
|
22
24
|
export { createExplorerAgent } from './explorer';
|
|
23
|
-
export { createReviewerAgent } from './reviewer';
|
|
25
|
+
export { createReviewerAgent, SECURITY_CATEGORIES, type SecurityCategory, } from './reviewer';
|
|
24
26
|
export { createSMEAgent } from './sme';
|
|
25
27
|
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;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const QA_AGENTS: readonly ["reviewer", "critic"];
|
|
2
2
|
export declare const PIPELINE_AGENTS: readonly ["explorer", "coder", "test_engineer"];
|
|
3
3
|
export declare const ORCHESTRATOR_NAME: "architect";
|
|
4
|
-
export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
5
|
-
export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
4
|
+
export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "designer", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
5
|
+
export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "designer", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
6
6
|
export type QAAgentName = (typeof QA_AGENTS)[number];
|
|
7
7
|
export type PipelineAgentName = (typeof PIPELINE_AGENTS)[number];
|
|
8
8
|
export type AgentName = (typeof ALL_AGENT_NAMES)[number];
|
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,26 @@ 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>;
|
|
140
|
+
export declare const DocsConfigSchema: z.ZodObject<{
|
|
141
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
142
|
+
doc_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
143
|
+
}, z.core.$strip>;
|
|
144
|
+
export type DocsConfig = z.infer<typeof DocsConfigSchema>;
|
|
145
|
+
export declare const UIReviewConfigSchema: z.ZodObject<{
|
|
146
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
147
|
+
trigger_paths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
148
|
+
trigger_keywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
149
|
+
}, z.core.$strip>;
|
|
150
|
+
export type UIReviewConfig = z.infer<typeof UIReviewConfigSchema>;
|
|
131
151
|
export declare const GuardrailsProfileSchema: z.ZodObject<{
|
|
132
152
|
max_tool_calls: z.ZodOptional<z.ZodNumber>;
|
|
133
153
|
max_duration_minutes: z.ZodOptional<z.ZodNumber>;
|
|
@@ -282,6 +302,23 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
282
302
|
max_stored_bytes: z.ZodDefault<z.ZodNumber>;
|
|
283
303
|
retention_days: z.ZodDefault<z.ZodNumber>;
|
|
284
304
|
}, z.core.$strip>>;
|
|
305
|
+
review_passes: z.ZodOptional<z.ZodObject<{
|
|
306
|
+
always_security_review: z.ZodDefault<z.ZodBoolean>;
|
|
307
|
+
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
308
|
+
}, z.core.$strip>>;
|
|
309
|
+
integration_analysis: z.ZodOptional<z.ZodObject<{
|
|
310
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
311
|
+
}, z.core.$strip>>;
|
|
312
|
+
docs: z.ZodOptional<z.ZodObject<{
|
|
313
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
314
|
+
doc_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
315
|
+
}, z.core.$strip>>;
|
|
316
|
+
ui_review: z.ZodOptional<z.ZodObject<{
|
|
317
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
318
|
+
trigger_paths: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
319
|
+
trigger_keywords: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
320
|
+
}, z.core.$strip>>;
|
|
321
|
+
_loadedFromFile: z.ZodDefault<z.ZodBoolean>;
|
|
285
322
|
}, z.core.$strip>;
|
|
286
323
|
export type PluginConfig = z.infer<typeof PluginConfigSchema>;
|
|
287
324
|
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,61 @@ 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
|
+
});
|
|
13648
|
+
var DocsConfigSchema = exports_external.object({
|
|
13649
|
+
enabled: exports_external.boolean().default(true),
|
|
13650
|
+
doc_patterns: exports_external.array(exports_external.string()).default([
|
|
13651
|
+
"README.md",
|
|
13652
|
+
"CONTRIBUTING.md",
|
|
13653
|
+
"docs/**/*.md",
|
|
13654
|
+
"docs/**/*.rst",
|
|
13655
|
+
"**/CHANGELOG.md"
|
|
13656
|
+
])
|
|
13657
|
+
});
|
|
13658
|
+
var UIReviewConfigSchema = exports_external.object({
|
|
13659
|
+
enabled: exports_external.boolean().default(false),
|
|
13660
|
+
trigger_paths: exports_external.array(exports_external.string()).default([
|
|
13661
|
+
"**/pages/**",
|
|
13662
|
+
"**/components/**",
|
|
13663
|
+
"**/views/**",
|
|
13664
|
+
"**/screens/**",
|
|
13665
|
+
"**/ui/**",
|
|
13666
|
+
"**/layouts/**"
|
|
13667
|
+
]),
|
|
13668
|
+
trigger_keywords: exports_external.array(exports_external.string()).default([
|
|
13669
|
+
"new page",
|
|
13670
|
+
"new screen",
|
|
13671
|
+
"new component",
|
|
13672
|
+
"redesign",
|
|
13673
|
+
"layout change",
|
|
13674
|
+
"form",
|
|
13675
|
+
"modal",
|
|
13676
|
+
"dialog",
|
|
13677
|
+
"dropdown",
|
|
13678
|
+
"sidebar",
|
|
13679
|
+
"navbar",
|
|
13680
|
+
"dashboard",
|
|
13681
|
+
"landing page",
|
|
13682
|
+
"signup",
|
|
13683
|
+
"login form",
|
|
13684
|
+
"settings page",
|
|
13685
|
+
"profile page"
|
|
13686
|
+
])
|
|
13687
|
+
});
|
|
13633
13688
|
var GuardrailsProfileSchema = exports_external.object({
|
|
13634
13689
|
max_tool_calls: exports_external.number().min(0).max(1000).optional(),
|
|
13635
13690
|
max_duration_minutes: exports_external.number().min(0).max(480).optional(),
|
|
@@ -13674,6 +13729,16 @@ var DEFAULT_AGENT_PROFILES = {
|
|
|
13674
13729
|
max_tool_calls: 200,
|
|
13675
13730
|
max_duration_minutes: 30,
|
|
13676
13731
|
warning_threshold: 0.65
|
|
13732
|
+
},
|
|
13733
|
+
docs: {
|
|
13734
|
+
max_tool_calls: 200,
|
|
13735
|
+
max_duration_minutes: 30,
|
|
13736
|
+
warning_threshold: 0.75
|
|
13737
|
+
},
|
|
13738
|
+
designer: {
|
|
13739
|
+
max_tool_calls: 150,
|
|
13740
|
+
max_duration_minutes: 20,
|
|
13741
|
+
warning_threshold: 0.75
|
|
13677
13742
|
}
|
|
13678
13743
|
};
|
|
13679
13744
|
var DEFAULT_ARCHITECT_PROFILE = DEFAULT_AGENT_PROFILES.architect;
|
|
@@ -13729,7 +13794,12 @@ var PluginConfigSchema = exports_external.object({
|
|
|
13729
13794
|
context_budget: ContextBudgetConfigSchema.optional(),
|
|
13730
13795
|
guardrails: GuardrailsConfigSchema.optional(),
|
|
13731
13796
|
evidence: EvidenceConfigSchema.optional(),
|
|
13732
|
-
summaries: SummaryConfigSchema.optional()
|
|
13797
|
+
summaries: SummaryConfigSchema.optional(),
|
|
13798
|
+
review_passes: ReviewPassesConfigSchema.optional(),
|
|
13799
|
+
integration_analysis: IntegrationAnalysisConfigSchema.optional(),
|
|
13800
|
+
docs: DocsConfigSchema.optional(),
|
|
13801
|
+
ui_review: UIReviewConfigSchema.optional(),
|
|
13802
|
+
_loadedFromFile: exports_external.boolean().default(false)
|
|
13733
13803
|
});
|
|
13734
13804
|
|
|
13735
13805
|
// src/config/loader.ts
|
|
@@ -13739,25 +13809,26 @@ var MAX_CONFIG_FILE_BYTES = 102400;
|
|
|
13739
13809
|
function getUserConfigDir() {
|
|
13740
13810
|
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
13741
13811
|
}
|
|
13742
|
-
function
|
|
13812
|
+
function loadRawConfigFromPath(configPath) {
|
|
13743
13813
|
try {
|
|
13744
13814
|
const stats = fs.statSync(configPath);
|
|
13745
13815
|
if (stats.size > MAX_CONFIG_FILE_BYTES) {
|
|
13746
13816
|
console.warn(`[opencode-swarm] Config file too large (max 100 KB): ${configPath}`);
|
|
13817
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13747
13818
|
return null;
|
|
13748
13819
|
}
|
|
13749
13820
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
13750
13821
|
const rawConfig = JSON.parse(content);
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
console.warn(
|
|
13754
|
-
console.warn(result.error.format());
|
|
13822
|
+
if (typeof rawConfig !== "object" || rawConfig === null || Array.isArray(rawConfig)) {
|
|
13823
|
+
console.warn(`[opencode-swarm] Invalid config at ${configPath}: expected an object`);
|
|
13824
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13755
13825
|
return null;
|
|
13756
13826
|
}
|
|
13757
|
-
return
|
|
13827
|
+
return rawConfig;
|
|
13758
13828
|
} catch (error48) {
|
|
13759
13829
|
if (error48 instanceof Error && "code" in error48 && error48.code !== "ENOENT") {
|
|
13760
|
-
console.warn(`[opencode-swarm]
|
|
13830
|
+
console.warn(`[opencode-swarm] \u26A0\uFE0F CONFIG LOAD FAILURE \u2014 config exists at ${configPath} but could not be loaded: ${error48.message}`);
|
|
13831
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13761
13832
|
}
|
|
13762
13833
|
return null;
|
|
13763
13834
|
}
|
|
@@ -13779,30 +13850,36 @@ function deepMergeInternal(base, override, depth) {
|
|
|
13779
13850
|
}
|
|
13780
13851
|
return result;
|
|
13781
13852
|
}
|
|
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
13853
|
function loadPluginConfig(directory) {
|
|
13790
13854
|
const userConfigPath = path.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
13791
13855
|
const projectConfigPath = path.join(directory, ".opencode", CONFIG_FILENAME);
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13856
|
+
const rawUserConfig = loadRawConfigFromPath(userConfigPath);
|
|
13857
|
+
const rawProjectConfig = loadRawConfigFromPath(projectConfigPath);
|
|
13858
|
+
const loadedFromFile = rawUserConfig !== null || rawProjectConfig !== null;
|
|
13859
|
+
let mergedRaw = rawUserConfig ?? {};
|
|
13860
|
+
if (rawProjectConfig) {
|
|
13861
|
+
mergedRaw = deepMergeInternal(mergedRaw, rawProjectConfig, 0);
|
|
13862
|
+
}
|
|
13863
|
+
const result = PluginConfigSchema.safeParse(mergedRaw);
|
|
13864
|
+
if (!result.success) {
|
|
13865
|
+
if (rawUserConfig) {
|
|
13866
|
+
const userResult = PluginConfigSchema.safeParse(rawUserConfig);
|
|
13867
|
+
if (userResult.success) {
|
|
13868
|
+
console.warn("[opencode-swarm] Project config ignored due to validation errors. Using user config.");
|
|
13869
|
+
return { ...userResult.data, _loadedFromFile: true };
|
|
13870
|
+
}
|
|
13871
|
+
}
|
|
13872
|
+
console.warn("[opencode-swarm] Merged config validation failed:");
|
|
13873
|
+
console.warn(result.error.format());
|
|
13874
|
+
console.warn("[opencode-swarm] \u26A0\uFE0F Guardrails will be DISABLED as a safety precaution. Fix the config file to restore normal operation.");
|
|
13875
|
+
return {
|
|
13876
|
+
max_iterations: 5,
|
|
13877
|
+
qa_retry_limit: 3,
|
|
13878
|
+
inject_phase_reminders: true,
|
|
13879
|
+
_loadedFromFile: false
|
|
13803
13880
|
};
|
|
13804
13881
|
}
|
|
13805
|
-
return
|
|
13882
|
+
return { ...result.data, _loadedFromFile: loadedFromFile };
|
|
13806
13883
|
}
|
|
13807
13884
|
function loadAgentPrompt(agentName) {
|
|
13808
13885
|
const promptsDir = path.join(getUserConfigDir(), "opencode", PROMPTS_DIR_NAME);
|
|
@@ -13832,6 +13909,8 @@ var PIPELINE_AGENTS = ["explorer", "coder", "test_engineer"];
|
|
|
13832
13909
|
var ORCHESTRATOR_NAME = "architect";
|
|
13833
13910
|
var ALL_SUBAGENT_NAMES = [
|
|
13834
13911
|
"sme",
|
|
13912
|
+
"docs",
|
|
13913
|
+
"designer",
|
|
13835
13914
|
...QA_AGENTS,
|
|
13836
13915
|
...PIPELINE_AGENTS
|
|
13837
13916
|
];
|
|
@@ -13847,6 +13926,8 @@ var DEFAULT_MODELS = {
|
|
|
13847
13926
|
sme: "google/gemini-2.0-flash",
|
|
13848
13927
|
reviewer: "google/gemini-2.0-flash",
|
|
13849
13928
|
critic: "google/gemini-2.0-flash",
|
|
13929
|
+
docs: "google/gemini-2.0-flash",
|
|
13930
|
+
designer: "google/gemini-2.0-flash",
|
|
13850
13931
|
default: "google/gemini-2.0-flash"
|
|
13851
13932
|
};
|
|
13852
13933
|
var DEFAULT_SCORING_CONFIG = {
|
|
@@ -13998,7 +14079,7 @@ var ARCHITECT_PROMPT = `You are Architect - orchestrator of a multi-agent swarm.
|
|
|
13998
14079
|
## IDENTITY
|
|
13999
14080
|
|
|
14000
14081
|
Swarm: {{SWARM_ID}}
|
|
14001
|
-
Your agents: {{AGENT_PREFIX}}explorer, {{AGENT_PREFIX}}sme, {{AGENT_PREFIX}}coder, {{AGENT_PREFIX}}reviewer, {{AGENT_PREFIX}}critic, {{AGENT_PREFIX}}test_engineer
|
|
14082
|
+
Your agents: {{AGENT_PREFIX}}explorer, {{AGENT_PREFIX}}sme, {{AGENT_PREFIX}}coder, {{AGENT_PREFIX}}reviewer, {{AGENT_PREFIX}}critic, {{AGENT_PREFIX}}test_engineer, {{AGENT_PREFIX}}docs, {{AGENT_PREFIX}}designer
|
|
14002
14083
|
|
|
14003
14084
|
## ROLE
|
|
14004
14085
|
|
|
@@ -14021,17 +14102,18 @@ You THINK. Subagents DO. You have the largest context window and strongest reaso
|
|
|
14021
14102
|
- If NEEDS_REVISION: Revise plan and re-submit to critic (max 2 cycles)
|
|
14022
14103
|
- If REJECTED after 2 cycles: Escalate to user with explanation
|
|
14023
14104
|
- 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
|
-
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14105
|
+
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.
|
|
14106
|
+
- 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.
|
|
14107
|
+
- Delegate {{AGENT_PREFIX}}reviewer with CHECK dimensions. REJECTED \u2192 return to coder (max {{QA_RETRY_LIMIT}} attempts). APPROVED \u2192 continue.
|
|
14108
|
+
- 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.
|
|
14109
|
+
- Delegate {{AGENT_PREFIX}}test_engineer for verification tests. FAIL \u2192 return to coder.
|
|
14110
|
+
- Delegate {{AGENT_PREFIX}}test_engineer for adversarial tests (attack vectors only). FAIL \u2192 return to coder.
|
|
14111
|
+
- All pass \u2192 mark task complete, proceed to next task.
|
|
14112
|
+
9. **UI/UX DESIGN GATE**: Before delegating UI tasks to {{AGENT_PREFIX}}coder, check if the task involves UI components. Trigger conditions (ANY match):
|
|
14113
|
+
- Task description contains UI keywords: new page, new screen, new component, redesign, layout change, form, modal, dialog, dropdown, sidebar, navbar, dashboard, landing page, signup, login form, settings page, profile page
|
|
14114
|
+
- Target file is in: pages/, components/, views/, screens/, ui/, layouts/
|
|
14115
|
+
If triggered: delegate to {{AGENT_PREFIX}}designer FIRST to produce a code scaffold. Then pass the scaffold to {{AGENT_PREFIX}}coder as INPUT alongside the task. The coder implements the TODOs in the scaffold without changing component structure or accessibility attributes.
|
|
14116
|
+
If not triggered: delegate directly to {{AGENT_PREFIX}}coder as normal.
|
|
14035
14117
|
|
|
14036
14118
|
## AGENTS
|
|
14037
14119
|
|
|
@@ -14041,9 +14123,13 @@ You THINK. Subagents DO. You have the largest context window and strongest reaso
|
|
|
14041
14123
|
{{AGENT_PREFIX}}reviewer - Code review (correctness, security, and any other dimensions you specify)
|
|
14042
14124
|
{{AGENT_PREFIX}}test_engineer - Test generation AND execution (writes tests, runs them, reports PASS/FAIL)
|
|
14043
14125
|
{{AGENT_PREFIX}}critic - Plan review gate (reviews plan BEFORE implementation)
|
|
14126
|
+
{{AGENT_PREFIX}}docs - Documentation updates (README, API docs, guides \u2014 NOT .swarm/ files)
|
|
14127
|
+
{{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components \u2014 runs BEFORE coder on UI tasks)
|
|
14044
14128
|
|
|
14045
14129
|
SMEs advise only. Reviewer and critic review only. None of them write code.
|
|
14046
14130
|
|
|
14131
|
+
Available Tools: diff (structured git diff with contract change detection)
|
|
14132
|
+
|
|
14047
14133
|
## DELEGATION FORMAT
|
|
14048
14134
|
|
|
14049
14135
|
All delegations use this structure:
|
|
@@ -14099,6 +14185,41 @@ PLAN: [paste the plan.md content]
|
|
|
14099
14185
|
CONTEXT: [codebase summary from explorer]
|
|
14100
14186
|
OUTPUT: VERDICT + CONFIDENCE + ISSUES + SUMMARY
|
|
14101
14187
|
|
|
14188
|
+
{{AGENT_PREFIX}}reviewer
|
|
14189
|
+
TASK: Security-only review of login validation
|
|
14190
|
+
FILE: src/auth/login.ts
|
|
14191
|
+
CHECK: [security-only] \u2014 evaluate against OWASP Top 10, scan for hardcoded secrets, injection vectors, insecure crypto, missing input validation
|
|
14192
|
+
OUTPUT: VERDICT + RISK + SECURITY ISSUES ONLY
|
|
14193
|
+
|
|
14194
|
+
{{AGENT_PREFIX}}test_engineer
|
|
14195
|
+
TASK: Adversarial security testing
|
|
14196
|
+
FILE: src/auth/login.ts
|
|
14197
|
+
CONSTRAINT: ONLY attack vectors \u2014 malformed inputs, oversized payloads, injection attempts, auth bypass, boundary violations
|
|
14198
|
+
OUTPUT: Test file + VERDICT: PASS/FAIL
|
|
14199
|
+
|
|
14200
|
+
{{AGENT_PREFIX}}explorer
|
|
14201
|
+
TASK: Integration impact analysis
|
|
14202
|
+
INPUT: Contract changes detected: [list from diff tool]
|
|
14203
|
+
OUTPUT: BREAKING CHANGES + CONSUMERS AFFECTED + VERDICT: BREAKING/COMPATIBLE
|
|
14204
|
+
CONSTRAINT: Read-only. grep for imports/usages of changed exports.
|
|
14205
|
+
|
|
14206
|
+
{{AGENT_PREFIX}}docs
|
|
14207
|
+
TASK: Update documentation for Phase 2 changes
|
|
14208
|
+
FILES CHANGED: src/auth/login.ts, src/auth/session.ts, src/types/user.ts
|
|
14209
|
+
CHANGES SUMMARY:
|
|
14210
|
+
- Added login() function with email/password authentication
|
|
14211
|
+
- Added SessionManager class with create/revoke/refresh methods
|
|
14212
|
+
- Added UserSession interface with refreshToken field
|
|
14213
|
+
DOC FILES: README.md, docs/api.md, docs/installation.md
|
|
14214
|
+
OUTPUT: Updated doc files + SUMMARY
|
|
14215
|
+
|
|
14216
|
+
{{AGENT_PREFIX}}designer
|
|
14217
|
+
TASK: Design specification for user settings page
|
|
14218
|
+
CONTEXT: Users need to update profile info, change password, manage notification preferences. App uses React + Tailwind + shadcn/ui.
|
|
14219
|
+
FRAMEWORK: React (TSX)
|
|
14220
|
+
EXISTING PATTERNS: All forms use react-hook-form, validation with zod, toast notifications for success/error
|
|
14221
|
+
OUTPUT: Code scaffold for src/pages/Settings.tsx with component tree, typed props, layout, and accessibility
|
|
14222
|
+
|
|
14102
14223
|
## WORKFLOW
|
|
14103
14224
|
|
|
14104
14225
|
### Phase 0: Resume Check
|
|
@@ -14150,21 +14271,24 @@ Delegate plan to {{AGENT_PREFIX}}critic for review BEFORE any implementation beg
|
|
|
14150
14271
|
### Phase 5: Execute
|
|
14151
14272
|
For each task (respecting dependencies):
|
|
14152
14273
|
|
|
14153
|
-
5a. {{AGENT_PREFIX}}coder
|
|
14154
|
-
5b. {{AGENT_PREFIX}}
|
|
14155
|
-
5c.
|
|
14156
|
-
|
|
14157
|
-
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
5f. Update plan.md [x], proceed to next task (ONLY if tests PASS)
|
|
14274
|
+
5a. **UI DESIGN GATE** (conditional \u2014 Rule 9): If task matches UI trigger \u2192 {{AGENT_PREFIX}}designer produces scaffold \u2192 pass scaffold to coder as INPUT. If no match \u2192 skip.
|
|
14275
|
+
5b. {{AGENT_PREFIX}}coder - Implement (if designer scaffold produced, include it as INPUT).
|
|
14276
|
+
5c. Run \`diff\` tool. If \`hasContractChanges\` \u2192 {{AGENT_PREFIX}}explorer integration analysis. BREAKING \u2192 coder retry.
|
|
14277
|
+
5d. {{AGENT_PREFIX}}reviewer - General review. REJECTED (< {{QA_RETRY_LIMIT}}) \u2192 coder retry. REJECTED ({{QA_RETRY_LIMIT}}) \u2192 escalate.
|
|
14278
|
+
5e. Security gate: if file matches security globs or content has security keywords \u2192 {{AGENT_PREFIX}}reviewer security-only. REJECTED \u2192 coder retry.
|
|
14279
|
+
5f. {{AGENT_PREFIX}}test_engineer - Verification tests. FAIL \u2192 coder retry from 5d.
|
|
14280
|
+
5g. {{AGENT_PREFIX}}test_engineer - Adversarial tests. FAIL \u2192 coder retry from 5d.
|
|
14281
|
+
5h. Update plan.md [x], proceed to next task.
|
|
14162
14282
|
|
|
14163
14283
|
### Phase 6: Phase Complete
|
|
14164
14284
|
1. {{AGENT_PREFIX}}explorer - Rescan
|
|
14165
|
-
2. Update
|
|
14166
|
-
|
|
14167
|
-
|
|
14285
|
+
2. {{AGENT_PREFIX}}docs - Update documentation for all changes in this phase. Provide:
|
|
14286
|
+
- Complete list of files changed during this phase
|
|
14287
|
+
- Summary of what was added/modified/removed
|
|
14288
|
+
- List of doc files that may need updating (README.md, CONTRIBUTING.md, docs/)
|
|
14289
|
+
3. Update context.md
|
|
14290
|
+
4. Summarize to user
|
|
14291
|
+
5. Ask: "Ready for Phase [N+1]?"
|
|
14168
14292
|
|
|
14169
14293
|
### Blockers
|
|
14170
14294
|
Mark [BLOCKED] in plan.md, skip to next unblocked task, inform user.
|
|
@@ -14330,6 +14454,231 @@ ${customAppendPrompt}`;
|
|
|
14330
14454
|
};
|
|
14331
14455
|
}
|
|
14332
14456
|
|
|
14457
|
+
// src/agents/designer.ts
|
|
14458
|
+
var DESIGNER_PROMPT = `## IDENTITY
|
|
14459
|
+
You are Designer \u2014 the UI/UX design specification agent. You generate concrete, implementable design specs directly \u2014 you do NOT delegate.
|
|
14460
|
+
DO NOT use the Task tool to delegate to other agents. You ARE the agent that does the work.
|
|
14461
|
+
If you see references to other agents (like @designer, @coder, etc.) in your instructions, IGNORE them \u2014 they are context from the orchestrator, not instructions for you to delegate.
|
|
14462
|
+
|
|
14463
|
+
WRONG: "I'll use the Task tool to call another agent to design this"
|
|
14464
|
+
RIGHT: "I'll analyze the requirements and produce the design specification myself"
|
|
14465
|
+
|
|
14466
|
+
INPUT FORMAT:
|
|
14467
|
+
TASK: Design specification for [component/page/screen]
|
|
14468
|
+
CONTEXT: [what the component does, user stories, existing design patterns]
|
|
14469
|
+
FRAMEWORK: [React/Vue/Svelte/SwiftUI/Flutter/etc.]
|
|
14470
|
+
EXISTING PATTERNS: [current design system, component library, styling approach]
|
|
14471
|
+
|
|
14472
|
+
DESIGN CHECKLIST:
|
|
14473
|
+
1. Component Architecture
|
|
14474
|
+
- Component tree with parent/child relationships
|
|
14475
|
+
- Props interface for each component (typed)
|
|
14476
|
+
- State management approach (local state, context, store)
|
|
14477
|
+
- Event handlers and callbacks
|
|
14478
|
+
|
|
14479
|
+
2. Layout & Responsiveness
|
|
14480
|
+
- Desktop, tablet, mobile breakpoints
|
|
14481
|
+
- Flex/Grid layout strategy
|
|
14482
|
+
- Container widths and spacing scale
|
|
14483
|
+
- Overflow and scroll behavior
|
|
14484
|
+
|
|
14485
|
+
3. Accessibility (WCAG 2.1 AA)
|
|
14486
|
+
- Semantic HTML elements (nav, main, article, section, aside)
|
|
14487
|
+
- ARIA labels for interactive elements
|
|
14488
|
+
- Keyboard navigation (tab order, focus management, keyboard shortcuts)
|
|
14489
|
+
- Screen reader compatibility (alt text, aria-live regions)
|
|
14490
|
+
- Color contrast (minimum 4.5:1 for text, 3:1 for large text)
|
|
14491
|
+
- Focus indicators (visible focus rings, not just outline: none)
|
|
14492
|
+
|
|
14493
|
+
4. Visual Design
|
|
14494
|
+
- Color palette (from existing design system or proposed)
|
|
14495
|
+
- Typography scale (font family, sizes, weights, line heights)
|
|
14496
|
+
- Spacing scale (consistent spacing values)
|
|
14497
|
+
- Border radius, shadows, elevation
|
|
14498
|
+
|
|
14499
|
+
5. Interaction Design
|
|
14500
|
+
- Loading states (skeleton screens, spinners, progress bars)
|
|
14501
|
+
- Error states (inline validation, error boundaries, empty states)
|
|
14502
|
+
- Hover/focus/active states for interactive elements
|
|
14503
|
+
- Transitions and animations (duration, easing)
|
|
14504
|
+
- Optimistic updates where applicable
|
|
14505
|
+
|
|
14506
|
+
OUTPUT FORMAT:
|
|
14507
|
+
Produce a CODE SCAFFOLD in the target framework. This is a skeleton file with:
|
|
14508
|
+
- Component structure with typed props and proper imports
|
|
14509
|
+
- Layout structure using the project's CSS framework (Tailwind classes, CSS modules, styled-components, etc.)
|
|
14510
|
+
- Placeholder TODO comments for business logic
|
|
14511
|
+
- Accessibility attributes (aria-*, role, tabIndex)
|
|
14512
|
+
- Responsive breakpoint classes/media queries
|
|
14513
|
+
- Named event handler stubs
|
|
14514
|
+
|
|
14515
|
+
Example output structure:
|
|
14516
|
+
\`\`\`tsx
|
|
14517
|
+
// src/components/LoginForm.tsx
|
|
14518
|
+
// DESIGN SPEC \u2014 generated by Designer agent
|
|
14519
|
+
// Coder: implement TODO items, do not change component structure or accessibility attributes
|
|
14520
|
+
|
|
14521
|
+
import { useState } from 'react';
|
|
14522
|
+
|
|
14523
|
+
interface LoginFormProps {
|
|
14524
|
+
onSubmit: (email: string, password: string) => Promise<void>;
|
|
14525
|
+
onForgotPassword?: () => void;
|
|
14526
|
+
isLoading?: boolean;
|
|
14527
|
+
error?: string;
|
|
14528
|
+
}
|
|
14529
|
+
|
|
14530
|
+
export function LoginForm({ onSubmit, onForgotPassword, isLoading, error }: LoginFormProps) {
|
|
14531
|
+
const [email, setEmail] = useState('');
|
|
14532
|
+
const [password, setPassword] = useState('');
|
|
14533
|
+
|
|
14534
|
+
return (
|
|
14535
|
+
<div className="flex min-h-screen items-center justify-center bg-gray-50 px-4 sm:px-6 lg:px-8"
|
|
14536
|
+
role="main">
|
|
14537
|
+
<div className="w-full max-w-md space-y-8">
|
|
14538
|
+
<div>
|
|
14539
|
+
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-gray-900">
|
|
14540
|
+
{/* TODO: Use app name from config */}
|
|
14541
|
+
Sign in to your account
|
|
14542
|
+
</h2>
|
|
14543
|
+
</div>
|
|
14544
|
+
{error && (
|
|
14545
|
+
<div role="alert" aria-live="polite"
|
|
14546
|
+
className="rounded-md bg-red-50 p-4 text-sm text-red-800">
|
|
14547
|
+
{error}
|
|
14548
|
+
</div>
|
|
14549
|
+
)}
|
|
14550
|
+
<div className="mt-8 space-y-6">
|
|
14551
|
+
<div className="space-y-4 rounded-md">
|
|
14552
|
+
<div>
|
|
14553
|
+
<label htmlFor="email" className="sr-only">Email address</label>
|
|
14554
|
+
<input id="email" name="email" type="email" autoComplete="email" required
|
|
14555
|
+
aria-label="Email address"
|
|
14556
|
+
className="relative block w-full rounded-t-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:z-10 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
|
14557
|
+
placeholder="Email address"
|
|
14558
|
+
value={email}
|
|
14559
|
+
onChange={(e) => setEmail(e.target.value)} />
|
|
14560
|
+
</div>
|
|
14561
|
+
{/* TODO: Password field with show/hide toggle */}
|
|
14562
|
+
{/* TODO: Remember me checkbox */}
|
|
14563
|
+
</div>
|
|
14564
|
+
<div className="flex items-center justify-between">
|
|
14565
|
+
{/* TODO: Forgot password link */}
|
|
14566
|
+
</div>
|
|
14567
|
+
<button type="submit" disabled={isLoading}
|
|
14568
|
+
aria-busy={isLoading}
|
|
14569
|
+
className="group relative flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:opacity-50">
|
|
14570
|
+
{isLoading ? 'Signing in...' : 'Sign in'}
|
|
14571
|
+
</button>
|
|
14572
|
+
</div>
|
|
14573
|
+
</div>
|
|
14574
|
+
</div>
|
|
14575
|
+
);
|
|
14576
|
+
}
|
|
14577
|
+
\`\`\`
|
|
14578
|
+
|
|
14579
|
+
RULES:
|
|
14580
|
+
- Produce REAL, syntactically valid code \u2014 not pseudocode
|
|
14581
|
+
- Match the project's existing framework, styling approach, and conventions
|
|
14582
|
+
- All interactive elements MUST have keyboard accessibility
|
|
14583
|
+
- All images/icons MUST have alt text or aria-label
|
|
14584
|
+
- Form inputs MUST have associated labels (visible or sr-only)
|
|
14585
|
+
- Color usage MUST meet WCAG AA contrast requirements
|
|
14586
|
+
- Use TODO comments for business logic only \u2014 structure, layout, and accessibility must be complete
|
|
14587
|
+
- Do NOT implement business logic \u2014 leave that for the coder
|
|
14588
|
+
- Keep output under 3000 characters per component`;
|
|
14589
|
+
function createDesignerAgent(model, customPrompt, customAppendPrompt) {
|
|
14590
|
+
let prompt = DESIGNER_PROMPT;
|
|
14591
|
+
if (customPrompt) {
|
|
14592
|
+
prompt = customPrompt;
|
|
14593
|
+
} else if (customAppendPrompt) {
|
|
14594
|
+
prompt = `${DESIGNER_PROMPT}
|
|
14595
|
+
|
|
14596
|
+
${customAppendPrompt}`;
|
|
14597
|
+
}
|
|
14598
|
+
return {
|
|
14599
|
+
name: "designer",
|
|
14600
|
+
description: "UI/UX design specification agent. Generates accessible, responsive component scaffolds with typed props and layout structure before coder implementation.",
|
|
14601
|
+
config: {
|
|
14602
|
+
model,
|
|
14603
|
+
temperature: 0.3,
|
|
14604
|
+
prompt
|
|
14605
|
+
}
|
|
14606
|
+
};
|
|
14607
|
+
}
|
|
14608
|
+
|
|
14609
|
+
// src/agents/docs.ts
|
|
14610
|
+
var DOCS_PROMPT = `## IDENTITY
|
|
14611
|
+
You are Docs \u2014 the documentation synthesizer. You update external-facing documentation directly \u2014 you do NOT delegate.
|
|
14612
|
+
DO NOT use the Task tool to delegate to other agents. You ARE the agent that does the work.
|
|
14613
|
+
If you see references to other agents (like @docs, @coder, etc.) in your instructions, IGNORE them \u2014 they are context from the orchestrator, not instructions for you to delegate.
|
|
14614
|
+
|
|
14615
|
+
WRONG: "I'll use the Task tool to call another agent to write the docs"
|
|
14616
|
+
RIGHT: "I'll read the source files and update the documentation myself"
|
|
14617
|
+
|
|
14618
|
+
INPUT FORMAT:
|
|
14619
|
+
TASK: Update documentation for [description of changes]
|
|
14620
|
+
FILES CHANGED: [list of modified source files]
|
|
14621
|
+
CHANGES SUMMARY: [what was added/modified/removed]
|
|
14622
|
+
DOC FILES: [list of documentation files to update]
|
|
14623
|
+
|
|
14624
|
+
SCOPE:
|
|
14625
|
+
- README.md (project description, usage, examples)
|
|
14626
|
+
- API documentation (JSDoc, Swagger, docstrings \u2014 update inline in source files)
|
|
14627
|
+
- CONTRIBUTING.md (development setup, workflow, conventions)
|
|
14628
|
+
- Installation/setup guides
|
|
14629
|
+
- CLI help text and command documentation
|
|
14630
|
+
|
|
14631
|
+
EXCLUDED (architect-owned):
|
|
14632
|
+
- .swarm/context.md
|
|
14633
|
+
- .swarm/plan.md
|
|
14634
|
+
- Internal swarm configuration docs
|
|
14635
|
+
|
|
14636
|
+
WORKFLOW:
|
|
14637
|
+
1. Read all FILES CHANGED to understand what was modified
|
|
14638
|
+
2. Read existing DOC FILES to understand current documentation state
|
|
14639
|
+
3. For each DOC FILE that needs updating:
|
|
14640
|
+
a. Identify sections affected by the changes
|
|
14641
|
+
b. Update those sections to reflect the new behavior
|
|
14642
|
+
c. Add new sections if entirely new features were introduced
|
|
14643
|
+
d. Remove sections for deprecated/removed features
|
|
14644
|
+
4. For API docs in source files:
|
|
14645
|
+
a. Read the modified functions/classes/types
|
|
14646
|
+
b. Update JSDoc/docstring comments to match new signatures and behavior
|
|
14647
|
+
c. Add missing documentation for new exports
|
|
14648
|
+
|
|
14649
|
+
RULES:
|
|
14650
|
+
- Be accurate: documentation MUST match the actual code behavior
|
|
14651
|
+
- Be concise: update only what changed, do not rewrite entire files
|
|
14652
|
+
- Preserve existing style: match the tone, formatting, and conventions of the existing docs
|
|
14653
|
+
- Include examples: every new public API should have at least one usage example
|
|
14654
|
+
- No fabrication: if you cannot determine behavior from the code, say so explicitly
|
|
14655
|
+
- Update version references if package.json version changed
|
|
14656
|
+
|
|
14657
|
+
OUTPUT FORMAT:
|
|
14658
|
+
UPDATED: [list of files modified]
|
|
14659
|
+
ADDED: [list of new sections/files created]
|
|
14660
|
+
REMOVED: [list of deprecated sections removed]
|
|
14661
|
+
SUMMARY: [one-line description of doc changes]`;
|
|
14662
|
+
function createDocsAgent(model, customPrompt, customAppendPrompt) {
|
|
14663
|
+
let prompt = DOCS_PROMPT;
|
|
14664
|
+
if (customPrompt) {
|
|
14665
|
+
prompt = customPrompt;
|
|
14666
|
+
} else if (customAppendPrompt) {
|
|
14667
|
+
prompt = `${DOCS_PROMPT}
|
|
14668
|
+
|
|
14669
|
+
${customAppendPrompt}`;
|
|
14670
|
+
}
|
|
14671
|
+
return {
|
|
14672
|
+
name: "docs",
|
|
14673
|
+
description: "Documentation synthesizer. Updates README, API docs, and guides to reflect code changes after each phase.",
|
|
14674
|
+
config: {
|
|
14675
|
+
model,
|
|
14676
|
+
temperature: 0.2,
|
|
14677
|
+
prompt
|
|
14678
|
+
}
|
|
14679
|
+
};
|
|
14680
|
+
}
|
|
14681
|
+
|
|
14333
14682
|
// src/agents/explorer.ts
|
|
14334
14683
|
var EXPLORER_PROMPT = `## IDENTITY
|
|
14335
14684
|
You are Explorer. You analyze codebases directly \u2014 you do NOT delegate.
|
|
@@ -14664,6 +15013,18 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
|
|
|
14664
15013
|
testEngineer.name = prefixName("test_engineer");
|
|
14665
15014
|
agents.push(applyOverrides(testEngineer, swarmAgents, swarmPrefix));
|
|
14666
15015
|
}
|
|
15016
|
+
if (!isAgentDisabled("docs", swarmAgents, swarmPrefix)) {
|
|
15017
|
+
const docsPrompts = getPrompts("docs");
|
|
15018
|
+
const docs = createDocsAgent(getModel("docs"), docsPrompts.prompt, docsPrompts.appendPrompt);
|
|
15019
|
+
docs.name = prefixName("docs");
|
|
15020
|
+
agents.push(applyOverrides(docs, swarmAgents, swarmPrefix));
|
|
15021
|
+
}
|
|
15022
|
+
if (pluginConfig?.ui_review?.enabled === true && !isAgentDisabled("designer", swarmAgents, swarmPrefix)) {
|
|
15023
|
+
const designerPrompts = getPrompts("designer");
|
|
15024
|
+
const designer = createDesignerAgent(getModel("designer"), designerPrompts.prompt, designerPrompts.appendPrompt);
|
|
15025
|
+
designer.name = prefixName("designer");
|
|
15026
|
+
agents.push(applyOverrides(designer, swarmAgents, swarmPrefix));
|
|
15027
|
+
}
|
|
14667
15028
|
return agents;
|
|
14668
15029
|
}
|
|
14669
15030
|
function createAgents(config2) {
|
|
@@ -16828,7 +17189,7 @@ ${originalText}`;
|
|
|
16828
17189
|
};
|
|
16829
17190
|
}
|
|
16830
17191
|
// src/hooks/delegation-tracker.ts
|
|
16831
|
-
function createDelegationTrackerHook(config2) {
|
|
17192
|
+
function createDelegationTrackerHook(config2, guardrailsEnabled = true) {
|
|
16832
17193
|
return async (input, _output) => {
|
|
16833
17194
|
const now = Date.now();
|
|
16834
17195
|
if (!input.agent || input.agent === "") {
|
|
@@ -16850,7 +17211,7 @@ function createDelegationTrackerHook(config2) {
|
|
|
16850
17211
|
const isArchitect = strippedAgent === ORCHESTRATOR_NAME;
|
|
16851
17212
|
const session = ensureAgentSession(input.sessionID, agentName);
|
|
16852
17213
|
session.delegationActive = !isArchitect;
|
|
16853
|
-
if (!isArchitect) {
|
|
17214
|
+
if (!isArchitect && guardrailsEnabled) {
|
|
16854
17215
|
beginInvocation(input.sessionID, agentName);
|
|
16855
17216
|
}
|
|
16856
17217
|
if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== agentName) {
|
|
@@ -17025,18 +17386,11 @@ function createGuardrailsHooks(config2) {
|
|
|
17025
17386
|
return;
|
|
17026
17387
|
}
|
|
17027
17388
|
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
|
-
}
|
|
17389
|
+
const sessionId = lastMessage.info?.sessionID;
|
|
17390
|
+
if (!sessionId) {
|
|
17391
|
+
return;
|
|
17039
17392
|
}
|
|
17393
|
+
const targetWindow = getActiveWindow(sessionId);
|
|
17040
17394
|
if (!targetWindow || !targetWindow.warningIssued && !targetWindow.hardLimitHit) {
|
|
17041
17395
|
return;
|
|
17042
17396
|
}
|
|
@@ -17244,6 +17598,18 @@ function createSystemEnhancerHook(config2, directory) {
|
|
|
17244
17598
|
}
|
|
17245
17599
|
}
|
|
17246
17600
|
tryInject("[SWARM HINT] Large tool outputs may be auto-summarized. Use /swarm retrieve <id> to get the full content if needed.");
|
|
17601
|
+
if (config2.review_passes?.always_security_review) {
|
|
17602
|
+
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.");
|
|
17603
|
+
}
|
|
17604
|
+
if (config2.integration_analysis?.enabled === false) {
|
|
17605
|
+
tryInject("[SWARM CONFIG] Integration analysis is DISABLED. Skip diff tool and integration impact analysis after coder tasks.");
|
|
17606
|
+
}
|
|
17607
|
+
if (config2.ui_review?.enabled) {
|
|
17608
|
+
tryInject("[SWARM CONFIG] UI/UX Designer agent is ENABLED. For tasks matching UI trigger keywords or file paths, delegate to designer BEFORE coder (Rule 9).");
|
|
17609
|
+
}
|
|
17610
|
+
if (config2.docs?.enabled === false) {
|
|
17611
|
+
tryInject("[SWARM CONFIG] Docs agent is DISABLED. Skip docs delegation in Phase 6.");
|
|
17612
|
+
}
|
|
17247
17613
|
return;
|
|
17248
17614
|
}
|
|
17249
17615
|
const userScoringConfig = config2.context_budget?.scoring;
|
|
@@ -17323,6 +17689,50 @@ function createSystemEnhancerHook(config2, directory) {
|
|
|
17323
17689
|
}
|
|
17324
17690
|
}
|
|
17325
17691
|
}
|
|
17692
|
+
if (config2.review_passes?.always_security_review) {
|
|
17693
|
+
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.";
|
|
17694
|
+
candidates.push({
|
|
17695
|
+
id: `candidate-${idCounter++}`,
|
|
17696
|
+
kind: "phase",
|
|
17697
|
+
text,
|
|
17698
|
+
tokens: estimateTokens(text),
|
|
17699
|
+
priority: 1,
|
|
17700
|
+
metadata: { contentType: "prose" }
|
|
17701
|
+
});
|
|
17702
|
+
}
|
|
17703
|
+
if (config2.integration_analysis?.enabled === false) {
|
|
17704
|
+
const text = "[SWARM CONFIG] Integration analysis is DISABLED. Skip diff tool and integration impact analysis after coder tasks.";
|
|
17705
|
+
candidates.push({
|
|
17706
|
+
id: `candidate-${idCounter++}`,
|
|
17707
|
+
kind: "phase",
|
|
17708
|
+
text,
|
|
17709
|
+
tokens: estimateTokens(text),
|
|
17710
|
+
priority: 1,
|
|
17711
|
+
metadata: { contentType: "prose" }
|
|
17712
|
+
});
|
|
17713
|
+
}
|
|
17714
|
+
if (config2.ui_review?.enabled) {
|
|
17715
|
+
const text = "[SWARM CONFIG] UI/UX Designer agent is ENABLED. For tasks matching UI trigger keywords or file paths, delegate to designer BEFORE coder (Rule 9).";
|
|
17716
|
+
candidates.push({
|
|
17717
|
+
id: `candidate-${idCounter++}`,
|
|
17718
|
+
kind: "phase",
|
|
17719
|
+
text,
|
|
17720
|
+
tokens: estimateTokens(text),
|
|
17721
|
+
priority: 1,
|
|
17722
|
+
metadata: { contentType: "prose" }
|
|
17723
|
+
});
|
|
17724
|
+
}
|
|
17725
|
+
if (config2.docs?.enabled === false) {
|
|
17726
|
+
const text = "[SWARM CONFIG] Docs agent is DISABLED. Skip docs delegation in Phase 6.";
|
|
17727
|
+
candidates.push({
|
|
17728
|
+
id: `candidate-${idCounter++}`,
|
|
17729
|
+
kind: "phase",
|
|
17730
|
+
text,
|
|
17731
|
+
tokens: estimateTokens(text),
|
|
17732
|
+
priority: 1,
|
|
17733
|
+
metadata: { contentType: "prose" }
|
|
17734
|
+
});
|
|
17735
|
+
}
|
|
17326
17736
|
const ranked = rankCandidates(candidates, effectiveConfig);
|
|
17327
17737
|
for (const candidate of ranked) {
|
|
17328
17738
|
if (injectedTokens + candidate.tokens > maxInjectionTokens) {
|
|
@@ -17517,6 +17927,9 @@ function createToolSummarizerHook(config2, directory) {
|
|
|
17517
17927
|
}
|
|
17518
17928
|
};
|
|
17519
17929
|
}
|
|
17930
|
+
// src/tools/diff.ts
|
|
17931
|
+
import { execSync } from "child_process";
|
|
17932
|
+
|
|
17520
17933
|
// node_modules/@opencode-ai/plugin/node_modules/zod/v4/classic/external.js
|
|
17521
17934
|
var exports_external2 = {};
|
|
17522
17935
|
__export(exports_external2, {
|
|
@@ -29837,7 +30250,149 @@ function tool(input) {
|
|
|
29837
30250
|
return input;
|
|
29838
30251
|
}
|
|
29839
30252
|
tool.schema = exports_external2;
|
|
29840
|
-
|
|
30253
|
+
// src/tools/diff.ts
|
|
30254
|
+
var MAX_DIFF_LINES = 500;
|
|
30255
|
+
var DIFF_TIMEOUT_MS = 30000;
|
|
30256
|
+
var MAX_BUFFER_BYTES = 5 * 1024 * 1024;
|
|
30257
|
+
var CONTRACT_PATTERNS = [
|
|
30258
|
+
/^[+-]\s*export\s+(function|const|class|interface|type|enum|default)\b/,
|
|
30259
|
+
/^[+-]\s*(interface|type)\s+\w+/,
|
|
30260
|
+
/^[+-]\s*public\s+/,
|
|
30261
|
+
/^[+-]\s*(async\s+)?function\s+\w+\s*\(/
|
|
30262
|
+
];
|
|
30263
|
+
var SAFE_REF_PATTERN = /^[a-zA-Z0-9._\-/~^@{}]+$/;
|
|
30264
|
+
var MAX_REF_LENGTH = 256;
|
|
30265
|
+
var MAX_PATH_LENGTH = 500;
|
|
30266
|
+
var SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
30267
|
+
function validateBase(base) {
|
|
30268
|
+
if (base.length > MAX_REF_LENGTH) {
|
|
30269
|
+
return `base ref exceeds maximum length of ${MAX_REF_LENGTH}`;
|
|
30270
|
+
}
|
|
30271
|
+
if (!SAFE_REF_PATTERN.test(base)) {
|
|
30272
|
+
return "base contains invalid characters for git ref";
|
|
30273
|
+
}
|
|
30274
|
+
return null;
|
|
30275
|
+
}
|
|
30276
|
+
function validatePaths(paths) {
|
|
30277
|
+
if (!paths)
|
|
30278
|
+
return null;
|
|
30279
|
+
for (const path7 of paths) {
|
|
30280
|
+
if (!path7 || path7.length === 0) {
|
|
30281
|
+
return "empty path not allowed";
|
|
30282
|
+
}
|
|
30283
|
+
if (path7.length > MAX_PATH_LENGTH) {
|
|
30284
|
+
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
30285
|
+
}
|
|
30286
|
+
if (SHELL_METACHARACTERS.test(path7)) {
|
|
30287
|
+
return "path contains shell metacharacters";
|
|
30288
|
+
}
|
|
30289
|
+
}
|
|
30290
|
+
return null;
|
|
30291
|
+
}
|
|
30292
|
+
var diff = tool({
|
|
30293
|
+
description: "Analyze git diff for changed files, exports, interfaces, and function signatures. Returns structured output with contract change detection.",
|
|
30294
|
+
args: {
|
|
30295
|
+
base: tool.schema.string().optional().describe('Base ref to diff against (default: HEAD). Use "staged" for staged changes, "unstaged" for working tree changes.'),
|
|
30296
|
+
paths: tool.schema.array(tool.schema.string()).optional().describe("Optional file paths to restrict diff scope.")
|
|
30297
|
+
},
|
|
30298
|
+
async execute(args, _context) {
|
|
30299
|
+
try {
|
|
30300
|
+
const base = args.base ?? "HEAD";
|
|
30301
|
+
const pathSpec = args.paths?.length ? "-- " + args.paths.join(" ") : "";
|
|
30302
|
+
const baseValidationError = validateBase(base);
|
|
30303
|
+
if (baseValidationError) {
|
|
30304
|
+
const errorResult = {
|
|
30305
|
+
error: `invalid base: ${baseValidationError}`,
|
|
30306
|
+
files: [],
|
|
30307
|
+
contractChanges: [],
|
|
30308
|
+
hasContractChanges: false
|
|
30309
|
+
};
|
|
30310
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30311
|
+
}
|
|
30312
|
+
const pathsValidationError = validatePaths(args.paths);
|
|
30313
|
+
if (pathsValidationError) {
|
|
30314
|
+
const errorResult = {
|
|
30315
|
+
error: `invalid paths: ${pathsValidationError}`,
|
|
30316
|
+
files: [],
|
|
30317
|
+
contractChanges: [],
|
|
30318
|
+
hasContractChanges: false
|
|
30319
|
+
};
|
|
30320
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30321
|
+
}
|
|
30322
|
+
let gitCmd;
|
|
30323
|
+
if (base === "staged") {
|
|
30324
|
+
gitCmd = "git --no-pager diff --cached";
|
|
30325
|
+
} else if (base === "unstaged") {
|
|
30326
|
+
gitCmd = "git --no-pager diff";
|
|
30327
|
+
} else {
|
|
30328
|
+
gitCmd = `git --no-pager diff ${base}`;
|
|
30329
|
+
}
|
|
30330
|
+
const numstatOutput = execSync(gitCmd + " --numstat " + pathSpec, {
|
|
30331
|
+
encoding: "utf-8",
|
|
30332
|
+
timeout: DIFF_TIMEOUT_MS
|
|
30333
|
+
});
|
|
30334
|
+
const fullDiffOutput = execSync(gitCmd + " -U3 " + pathSpec, {
|
|
30335
|
+
encoding: "utf-8",
|
|
30336
|
+
timeout: DIFF_TIMEOUT_MS,
|
|
30337
|
+
maxBuffer: MAX_BUFFER_BYTES
|
|
30338
|
+
});
|
|
30339
|
+
const files = [];
|
|
30340
|
+
const numstatLines = numstatOutput.split(`
|
|
30341
|
+
`);
|
|
30342
|
+
for (const line of numstatLines) {
|
|
30343
|
+
if (!line.trim())
|
|
30344
|
+
continue;
|
|
30345
|
+
const parts = line.split("\t");
|
|
30346
|
+
if (parts.length >= 3) {
|
|
30347
|
+
const additions = parseInt(parts[0]) || 0;
|
|
30348
|
+
const deletions = parseInt(parts[1]) || 0;
|
|
30349
|
+
const path7 = parts[2];
|
|
30350
|
+
files.push({ path: path7, additions, deletions });
|
|
30351
|
+
}
|
|
30352
|
+
}
|
|
30353
|
+
const contractChanges = [];
|
|
30354
|
+
const diffLines = fullDiffOutput.split(`
|
|
30355
|
+
`);
|
|
30356
|
+
let currentFile = "";
|
|
30357
|
+
for (const line of diffLines) {
|
|
30358
|
+
const gitLineMatch = line.match(/^diff --git.* b\/(.+)$/);
|
|
30359
|
+
if (gitLineMatch) {
|
|
30360
|
+
currentFile = gitLineMatch[1];
|
|
30361
|
+
}
|
|
30362
|
+
for (const pattern of CONTRACT_PATTERNS) {
|
|
30363
|
+
if (pattern.test(line)) {
|
|
30364
|
+
const trimmed = line.trim();
|
|
30365
|
+
if (currentFile) {
|
|
30366
|
+
contractChanges.push(`[${currentFile}] ${trimmed}`);
|
|
30367
|
+
} else {
|
|
30368
|
+
contractChanges.push(trimmed);
|
|
30369
|
+
}
|
|
30370
|
+
break;
|
|
30371
|
+
}
|
|
30372
|
+
}
|
|
30373
|
+
}
|
|
30374
|
+
const hasContractChanges = contractChanges.length > 0;
|
|
30375
|
+
const fileCount = files.length;
|
|
30376
|
+
const truncated = diffLines.length > MAX_DIFF_LINES;
|
|
30377
|
+
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"}`;
|
|
30378
|
+
const result = {
|
|
30379
|
+
files,
|
|
30380
|
+
contractChanges,
|
|
30381
|
+
hasContractChanges,
|
|
30382
|
+
summary
|
|
30383
|
+
};
|
|
30384
|
+
return JSON.stringify(result, null, 2);
|
|
30385
|
+
} catch (e) {
|
|
30386
|
+
const errorResult = {
|
|
30387
|
+
error: e instanceof Error ? `git diff failed: ${e.constructor.name}` : "git diff failed: unknown error",
|
|
30388
|
+
files: [],
|
|
30389
|
+
contractChanges: [],
|
|
30390
|
+
hasContractChanges: false
|
|
30391
|
+
};
|
|
30392
|
+
return JSON.stringify(errorResult, null, 2);
|
|
30393
|
+
}
|
|
30394
|
+
}
|
|
30395
|
+
});
|
|
29841
30396
|
// src/tools/domain-detector.ts
|
|
29842
30397
|
var DOMAIN_PATTERNS = {
|
|
29843
30398
|
windows: [
|
|
@@ -30222,9 +30777,10 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30222
30777
|
const contextBudgetHandler = createContextBudgetHandler(config3);
|
|
30223
30778
|
const commandHandler = createSwarmCommandHandler(ctx.directory, Object.fromEntries(agentDefinitions.map((agent) => [agent.name, agent])));
|
|
30224
30779
|
const activityHooks = createAgentActivityHooks(config3, ctx.directory);
|
|
30225
|
-
const delegationHandler = createDelegationTrackerHook(config3);
|
|
30226
30780
|
const delegationGateHandler = createDelegationGateHook(config3);
|
|
30227
|
-
const
|
|
30781
|
+
const guardrailsFallback = config3._loadedFromFile ? config3.guardrails ?? {} : { ...config3.guardrails, enabled: false };
|
|
30782
|
+
const guardrailsConfig = GuardrailsConfigSchema.parse(guardrailsFallback);
|
|
30783
|
+
const delegationHandler = createDelegationTrackerHook(config3, guardrailsConfig.enabled);
|
|
30228
30784
|
const guardrailsHooks = createGuardrailsHooks(guardrailsConfig);
|
|
30229
30785
|
const summaryConfig = SummaryConfigSchema.parse(config3.summaries ?? {});
|
|
30230
30786
|
const toolSummarizerHook = createToolSummarizerHook(summaryConfig, ctx.directory);
|
|
@@ -30251,7 +30807,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
30251
30807
|
tool: {
|
|
30252
30808
|
detect_domains,
|
|
30253
30809
|
extract_code_blocks,
|
|
30254
|
-
gitingest
|
|
30810
|
+
gitingest,
|
|
30811
|
+
diff
|
|
30255
30812
|
},
|
|
30256
30813
|
config: async (opencodeConfig) => {
|
|
30257
30814
|
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.1.0",
|
|
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",
|