opencode-swarm 7.63.0 → 7.64.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.
@@ -114,6 +114,130 @@ Before launching explorers (Phase 3), confirm the PR branch refs are available:
114
114
 
115
115
  If refs cannot be fetched or checked out, state the limitation in the context pack.
116
116
 
117
+ ## Phase 0A: Existing PR Comment Ingestion
118
+
119
+ When reviewing a PR that already has comments, reviews, or bot findings,
120
+ ingest and triage them BEFORE starting Phase 0. These are pre-existing signals
121
+ that must be validated, not ignored.
122
+
123
+ ### Step 1 — Fetch all PR feedback surfaces
124
+
125
+ ```bash
126
+ # Issue comments (general PR thread)
127
+ gh pr view <PR_NUMBER> --json comments
128
+
129
+ # Review comments (inline code comments)
130
+ gh api repos/{owner}/{repo}/pulls/{PR_NUMBER}/comments
131
+
132
+ # Review summaries (approve/request-changes/comment events)
133
+ gh pr view <PR_NUMBER> --json reviews
134
+
135
+ # Bot/automated reviews (Copilot, Codex, CodeRabbit, etc.)
136
+ # Inline review comments — use REST API for reliable bot detection via user.type
137
+ gh api repos/{owner}/{repo}/pulls/{PR_NUMBER}/comments --jq '.[] | select((.user.type // "") == "Bot" or (.user.login // "" | test("bot|copilot|coderabbit|codex"; "i")))'
138
+ ```
139
+
140
+ For general PR comments (not inline), use the issue comments endpoint:
141
+ ```bash
142
+ gh api repos/{owner}/{repo}/issues/{PR_NUMBER}/comments --jq '.[] | select((.user.type // "") == "Bot" or (.user.login // "" | test("bot|copilot|coderabbit|codex"; "i")))'
143
+ ```
144
+
145
+ ### Step 2 — Classify each comment
146
+
147
+ | Category | Action |
148
+ |----------|--------|
149
+ | **Human review with file:line evidence** | Add as candidate finding with `source: existing-review` — still needs reviewer validation |
150
+ | **Bot/automated finding with specific code reference** | Add as candidate finding with `source: bot-review` — high false-positive rate, treat as unverified |
151
+ | **General feedback / style preference** | Add as advisory obligation |
152
+ | **Resolved/outdated comment** | Skip — note in report under "Ingested Resolved Comments" |
153
+ | **Requested changes not yet addressed** | Add as HIGH-priority obligation |
154
+
155
+ ### Step 3 — Merge into review pipeline
156
+
157
+ All ingested comments become candidate findings or obligations. They follow the
158
+ same Phase 3-8 pipeline as freshly discovered findings. Ingested findings are
159
+ NOT pre-confirmed — they still require independent reviewer validation per the
160
+ Anti-Self-Review Rule.
161
+
162
+ **Comment-ledger output:**
163
+ ```
164
+ [INGESTED] | source | category | file:line (if applicable) | original_author | status: PENDING_VALIDATION / SKIPPED_OUTDATED / ADVISORY
165
+ ```
166
+
167
+ ### Anti-patterns
168
+ - ✗ Ignoring bot reviews because "bots produce false positives" — they also catch real issues
169
+ - ✗ Pre-confirming human review comments without independent validation — even senior reviewers make mistakes
170
+ - ✗ Skipping inline review comments and only reading the summary — inline comments contain the evidence
171
+
172
+ ## Phase 0B: Merge Conflict Detection and Resolution
173
+
174
+ Before investing effort in review lanes, verify the PR is mergeable. A
175
+ conflicted PR cannot merge regardless of review quality.
176
+
177
+ ### Step 1 — Check merge state
178
+
179
+ ```bash
180
+ gh pr view <PR_NUMBER> --json mergeable,mergeStateStatus
181
+ ```
182
+
183
+ The response has two independent fields. Handle each:
184
+
185
+ **`mergeable` field** — whether GitHub can compute mergeability:
186
+ | Value | Meaning | Action |
187
+ |-------|---------|--------|
188
+ | `MERGEABLE` | No conflicts detected | Proceed — check `mergeStateStatus` below |
189
+ | `CONFLICTING` | Merge conflicts exist | Resolve before reviewing |
190
+ | `UNKNOWN` | GitHub still computing | Wait 30s, re-check |
191
+
192
+ **`mergeStateStatus` field** — overall branch state:
193
+ | Value | Action |
194
+ |-------|--------|
195
+ | `CLEAN` | All checks pass, no conflicts — proceed to Phase 0 |
196
+ | `BEHIND` | Branch behind base — note in report; non-blocking if merge queue handles it |
197
+ | `DIRTY` | Merge conflicts exist — resolve before reviewing |
198
+ | `BLOCKED` | External blocker (branch protection, failing required check) — investigate |
199
+
200
+ ### Step 2 — Resolve conflicts (when CONFLICTING or DIRTY)
201
+
202
+ When the PR has merge conflicts:
203
+
204
+ 1. **Determine the PR's base branch and fetch:**
205
+ ```bash
206
+ BASE_REF=$(gh pr view <PR_NUMBER> --json baseRefName --jq '.baseRefName')
207
+ git fetch origin $BASE_REF
208
+ git checkout <pr-branch>
209
+ git merge origin/$BASE_REF --no-commit --no-ff
210
+ git diff --name-only --diff-filter=U # list conflicted files
211
+ ```
212
+
213
+ 2. **Assess conflict complexity:**
214
+ - **1-3 simple conflicts** (lockfile version bumps, whitespace): Resolve directly, commit, push.
215
+ - **4+ conflicts or semantic conflicts** (logic changes in same function): Route to coder for resolution. Do NOT guess at semantic merge resolutions.
216
+
217
+ 3. **Resolve and push:**
218
+ ```bash
219
+ # For simple conflicts (after resolving markers):
220
+ git add -A
221
+ git commit -m "merge: resolve conflicts with main"
222
+ git push origin <pr-branch>
223
+ ```
224
+
225
+ 4. **Post-resolution verification:**
226
+ ```bash
227
+ # Verify clean state
228
+ gh pr view <PR_NUMBER> --json mergeable,mergeStateStatus
229
+ # Run affected tests
230
+ bun test tests/unit/path/to/conflicted.test.ts --timeout 30000
231
+ ```
232
+
233
+ 5. **Document in report:** List all conflicted files, resolution approach, and whether semantic judgment was required.
234
+
235
+ ### Conflict resolution anti-patterns
236
+ - ✗ Accepting "ours" or "theirs" for all conflicts without reading them
237
+ - ✗ Resolving semantic conflicts without understanding both sides
238
+ - ✗ Pushing resolution without running tests on the merged result
239
+ - ✗ Reviewing a conflicted PR without resolving first — review effort is wasted if the merge changes the code
240
+
117
241
  ---
118
242
 
119
243
  # Default Review Workflow
package/README.md CHANGED
@@ -35,6 +35,7 @@ Most AI coding tools let one model write code and ask that same model whether th
35
35
  - 🏗️ **Specialized core, optional, and conditional agents** — architect, coder, reviewer, test_engineer, critic, explorer, sme, docs, designer, critic_oversight, critic_sounding_board, critic_drift_verifier, critic_hallucination_verifier, curator_init, curator_phase, council_generalist, council_skeptic, council_domain_expert. Run `/swarm agents` for the live roster — that is the source of truth, not this list.
36
36
  - 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval
37
37
  - 🔍 **DEEP_DIVE Protocol** — High-rigor, on-demand read-only codebase audit via specialized skills
38
+ - 🔬 **External Skill Curation Pipeline** — Opt-in discovery, quarantine, evaluation, and promotion of external skill candidates from configured sources (disabled by default; enable via `external_skills.curation_enabled: true` in config). Includes 7 tools: `external_skill_discover`, `external_skill_list`, `external_skill_inspect`, `external_skill_promote`, `external_skill_reject`, `external_skill_delete`, `external_skill_revoke`. Candidates pass through a 3-gate validation pipeline before evaluation: **prompt injection scan** (12 regex patterns), **unsafe instruction scan** (25 patterns), and **provenance integrity check** (SHA-256, timestamp, URL, publisher, and hash verification).
38
39
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion
39
40
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
40
41
  - 🌐 **20 languages** — TypeScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP, JavaScript, CSS, Bash, PowerShell, INI, Regex (extending: see [docs/adding-a-language.md](docs/adding-a-language.md))
@@ -624,6 +625,62 @@ Swarm provides tools for managing generated skill lifecycles:
624
625
 
625
626
  - **Proposal cleanup** — When a draft skill proposal is activated via `skill_apply`, the source proposal file is deleted as part of the activation process (best-effort; permission errors are logged but do not block activation).
626
627
 
628
+ ### External Skill Curation
629
+
630
+ Swarm provides an opt-in, quarantine-first pipeline for discovering, validating, and promoting external skills. Disabled by default — no network calls are made until explicitly enabled.
631
+
632
+ #### Enabling
633
+
634
+ ```yaml
635
+ external_skills:
636
+ curation_enabled: true
637
+ sources:
638
+ - type: url
639
+ location: https://example.com/skills/
640
+ trust_level: medium
641
+ ```
642
+
643
+ #### Validation Pipeline
644
+
645
+ Every candidate passes a 3-gate pipeline before entering quarantine:
646
+
647
+ | Gate | Name | Description |
648
+ |------|------|-------------|
649
+ | 1 | Prompt Injection Scan | 12 regex patterns plus oversized field, invisible character, and suspicious formatting checks detect system instruction injection, role hijacking, and instruction override attempts |
650
+ | 2 | Unsafe Instruction Scan | 25 patterns detect shell commands, file system attacks, network exfiltration, and privilege escalation |
651
+ | 3 | Provenance Integrity | SHA-256 content hash, timestamp validation, URL format checks, and publisher presence validation |
652
+
653
+ **Trust level modulation**: `low` trust promotes warning-severity findings to errors (stricter); `medium` and `high` trust levels keep warnings advisory. Error-severity findings always block regardless of trust level.
654
+
655
+ #### Tool Reference
656
+
657
+ | Tool | Description |
658
+ |------|-------------|
659
+ | `external_skill_discover` | Fetch and validate a skill from a configured source |
660
+ | `external_skill_list` | List candidates with status filters |
661
+ | `external_skill_inspect` | View full candidate details |
662
+ | `external_skill_promote` | Promote validated candidate to active skill (user approval required) |
663
+ | `external_skill_reject` | Reject candidate with reason |
664
+ | `external_skill_delete` | Remove candidate from quarantine store |
665
+ | `external_skill_revoke` | Retire a previously promoted skill |
666
+
667
+ #### Security Guarantees
668
+
669
+ - Disabled by default — no network calls until explicitly enabled
670
+ - All candidates quarantined until human review and promotion
671
+ - TOCTOU re-validation at promotion time
672
+ - Content hash verification prevents tampering
673
+ - Bounded concurrent fetches (5 simultaneous) and discovery limits (50 candidates per invocation)
674
+ - Max candidate size and count bounds
675
+ - Source origin validation (URLs must match configured sources)
676
+
677
+ #### Limitations
678
+
679
+ - Static regex patterns only (no LLM-based detection)
680
+ - No cryptographic signing (deferred)
681
+ - No batch import (deferred)
682
+ - No auto-promotion (human approval always required)
683
+
627
684
  ### Configuration Reference
628
685
 
629
686
  | Key | Type | Default | Description |
@@ -70,4 +70,4 @@ export declare function buildCouncilWorkflow(council?: CouncilWorkflowConfig): s
70
70
  * BRAINSTORM, and PLAN inline paths stay in lockstep.
71
71
  */
72
72
  export declare function buildQaGateSelectionDialogue(modeLabel: 'BRAINSTORM' | 'SPECIFY' | 'PLAN'): string;
73
- export declare function createArchitectAgent(model: string, customPrompt?: string, customAppendPrompt?: string, adversarialTesting?: AdversarialTestingConfig, council?: CouncilWorkflowConfig, uiReview?: UIReviewConfig, memoryEnabled?: boolean, architecturalSupervision?: ArchitectureSupervisionWorkflowConfig, designDocsEnabled?: boolean): AgentDefinition;
73
+ export declare function createArchitectAgent(model: string, customPrompt?: string, customAppendPrompt?: string, adversarialTesting?: AdversarialTestingConfig, council?: CouncilWorkflowConfig, uiReview?: UIReviewConfig, memoryEnabled?: boolean, architecturalSupervision?: ArchitectureSupervisionWorkflowConfig, designDocsEnabled?: boolean, externalSkillsEnabled?: boolean): AgentDefinition;
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ var package_default;
52
52
  var init_package = __esm(() => {
53
53
  package_default = {
54
54
  name: "opencode-swarm",
55
- version: "7.63.0",
55
+ version: "7.64.0",
56
56
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
57
57
  main: "dist/index.js",
58
58
  types: "dist/index.d.ts",
@@ -17629,6 +17629,34 @@ var init_tool_metadata = __esm(() => {
17629
17629
  apply_patch: {
17630
17630
  description: "Apply a unified diff patch to workspace files with exact context matching, atomic writes, and path validation",
17631
17631
  agents: ["coder"]
17632
+ },
17633
+ external_skill_discover: {
17634
+ description: "Discover external skill candidates from configured sources. Returns a disabled message when external_skills.curation_enabled is false.",
17635
+ agents: []
17636
+ },
17637
+ external_skill_list: {
17638
+ description: "List external skill candidates in the quarantine store. Returns a disabled message when external_skills.curation_enabled is false.",
17639
+ agents: []
17640
+ },
17641
+ external_skill_inspect: {
17642
+ description: "Inspect a specific external skill candidate by ID. Returns a disabled message when external_skills.curation_enabled is false.",
17643
+ agents: []
17644
+ },
17645
+ external_skill_promote: {
17646
+ description: "Promote a validated external skill candidate to an active generated skill. Returns a disabled message when external_skills.curation_enabled is false.",
17647
+ agents: []
17648
+ },
17649
+ external_skill_reject: {
17650
+ description: "Reject an external skill candidate after evaluation. Returns a disabled message when external_skills.curation_enabled is false.",
17651
+ agents: []
17652
+ },
17653
+ external_skill_delete: {
17654
+ description: "Delete an external skill candidate from the quarantine store. Returns a disabled message when external_skills.curation_enabled is false.",
17655
+ agents: []
17656
+ },
17657
+ external_skill_revoke: {
17658
+ description: "Revoke a previously promoted external skill. Returns a disabled message when external_skills.curation_enabled is false.",
17659
+ agents: []
17632
17660
  }
17633
17661
  };
17634
17662
  TOOL_NAMES = Object.keys(TOOL_METADATA);
@@ -17682,7 +17710,7 @@ function freezeSet(items) {
17682
17710
  });
17683
17711
  return proxy;
17684
17712
  }
17685
- var OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, DEFAULT_AGENT_CONFIGS;
17713
+ var OPENCODE_NATIVE_AGENTS, CLAUDE_CODE_NATIVE_COMMANDS, EXTERNAL_SKILL_TOOL_NAMES, EXTERNAL_SKILL_AGENT_TOOL_MAP, DEFAULT_AGENT_CONFIGS;
17686
17714
  var init_constants = __esm(() => {
17687
17715
  init_agent_names();
17688
17716
  init_tool_metadata();
@@ -17803,6 +17831,18 @@ var init_constants = __esm(() => {
17803
17831
  "team-onboarding",
17804
17832
  "bashes"
17805
17833
  ]);
17834
+ EXTERNAL_SKILL_TOOL_NAMES = [
17835
+ "external_skill_discover",
17836
+ "external_skill_list",
17837
+ "external_skill_inspect",
17838
+ "external_skill_promote",
17839
+ "external_skill_reject",
17840
+ "external_skill_delete",
17841
+ "external_skill_revoke"
17842
+ ];
17843
+ EXTERNAL_SKILL_AGENT_TOOL_MAP = {
17844
+ architect: [...EXTERNAL_SKILL_TOOL_NAMES]
17845
+ };
17806
17846
  DEFAULT_AGENT_CONFIGS = {
17807
17847
  coder: {
17808
17848
  model: "opencode/minimax-m2.5-free",
@@ -17908,7 +17948,26 @@ function getCanonicalAgentRole(agentName, generatedAgentNames) {
17908
17948
  function stripKnownSwarmPrefix(agentName) {
17909
17949
  return getCanonicalAgentRole(agentName);
17910
17950
  }
17911
- var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, PluginConfigSchema;
17951
+ function resolveExternalSkillsConfig(input) {
17952
+ if (input === undefined || input === null || typeof input !== "object" || Array.isArray(input)) {
17953
+ return { ...DEFAULT_EXTERNAL_SKILLS_CONFIG };
17954
+ }
17955
+ const config2 = input;
17956
+ const merged = {
17957
+ curation_enabled: config2.curation_enabled ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.curation_enabled,
17958
+ max_candidates: config2.max_candidates ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.max_candidates,
17959
+ max_bytes_per_candidate: config2.max_bytes_per_candidate ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.max_bytes_per_candidate,
17960
+ eviction_policy: config2.eviction_policy ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.eviction_policy,
17961
+ ttl_days: config2.ttl_days ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.ttl_days,
17962
+ evaluation_enabled: config2.evaluation_enabled ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.evaluation_enabled,
17963
+ sources: Array.isArray(config2.sources) ? config2.sources : [],
17964
+ max_candidates_per_discovery: config2.max_candidates_per_discovery ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.max_candidates_per_discovery,
17965
+ max_concurrent_fetches: config2.max_concurrent_fetches ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.max_concurrent_fetches,
17966
+ fetch_timeout_ms: config2.fetch_timeout_ms ?? DEFAULT_EXTERNAL_SKILLS_CONFIG.fetch_timeout_ms
17967
+ };
17968
+ return merged;
17969
+ }
17970
+ var SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, ExternalSkillCandidateSourceTypeSchema, ExternalSkillCandidateEvaluationVerdictSchema, DiscoverySourceSchema, ExternalSkillCandidateSchema, ExternalSkillsConfigSchema, DEFAULT_EXTERNAL_SKILLS_CONFIG, PluginConfigSchema;
17912
17971
  var init_schema = __esm(() => {
17913
17972
  init_zod();
17914
17973
  init_constants();
@@ -18624,6 +18683,93 @@ var init_schema = __esm(() => {
18624
18683
  StandardTurboConfigSchema,
18625
18684
  LeanTurboStrategyConfigSchema
18626
18685
  ]);
18686
+ ExternalSkillCandidateSourceTypeSchema = exports_external.enum([
18687
+ "github",
18688
+ "url",
18689
+ "collection",
18690
+ "manual_import"
18691
+ ]);
18692
+ ExternalSkillCandidateEvaluationVerdictSchema = exports_external.enum([
18693
+ "pending",
18694
+ "in_review",
18695
+ "quarantined",
18696
+ "passed",
18697
+ "rejected",
18698
+ "promoted",
18699
+ "revoked"
18700
+ ]);
18701
+ DiscoverySourceSchema = exports_external.object({
18702
+ type: ExternalSkillCandidateSourceTypeSchema,
18703
+ location: exports_external.string().min(1),
18704
+ enabled: exports_external.boolean().default(true),
18705
+ trust_level: exports_external.enum(["low", "medium", "high"]).default("low").optional()
18706
+ });
18707
+ ExternalSkillCandidateSchema = exports_external.object({
18708
+ id: exports_external.string().uuid(),
18709
+ source_url: exports_external.string().url(),
18710
+ source_type: ExternalSkillCandidateSourceTypeSchema,
18711
+ publisher: exports_external.string().min(1),
18712
+ sha256: exports_external.string().regex(/^[a-f0-9]{64}$/),
18713
+ fetched_at: exports_external.string().datetime(),
18714
+ skill_name: exports_external.string().optional(),
18715
+ skill_description: exports_external.string().optional(),
18716
+ skill_body: exports_external.string(),
18717
+ risk_flags: exports_external.array(exports_external.string()).default([]),
18718
+ evaluation_verdict: ExternalSkillCandidateEvaluationVerdictSchema.default("pending"),
18719
+ evaluation_history: exports_external.array(exports_external.object({
18720
+ verdict: ExternalSkillCandidateEvaluationVerdictSchema,
18721
+ timestamp: exports_external.string().datetime(),
18722
+ actor: exports_external.string(),
18723
+ reason: exports_external.string().optional(),
18724
+ candidate_id: exports_external.string().optional(),
18725
+ original_verdict: ExternalSkillCandidateEvaluationVerdictSchema.optional(),
18726
+ gate_results: exports_external.array(exports_external.object({
18727
+ gate: exports_external.string(),
18728
+ verdict: exports_external.string()
18729
+ })).optional(),
18730
+ risk_assessment: exports_external.object({
18731
+ total_flags: exports_external.number().int().nonnegative(),
18732
+ findings: exports_external.array(exports_external.object({
18733
+ severity: exports_external.enum(["error", "warning"]),
18734
+ category: exports_external.string()
18735
+ }))
18736
+ }).optional(),
18737
+ risk_flags_count: exports_external.number().int().nonnegative().optional(),
18738
+ provenance_snapshot: exports_external.object({
18739
+ sha256: exports_external.string(),
18740
+ source_url: exports_external.string(),
18741
+ publisher: exports_external.string(),
18742
+ fetched_at: exports_external.string().datetime().optional()
18743
+ }).optional(),
18744
+ target_path: exports_external.string().optional(),
18745
+ promoted_content_hash: exports_external.string().regex(/^[a-f0-9]{64}$/).optional(),
18746
+ original_evaluation: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
18747
+ })).default([])
18748
+ });
18749
+ ExternalSkillsConfigSchema = exports_external.object({
18750
+ curation_enabled: exports_external.boolean().default(false),
18751
+ max_candidates: exports_external.number().int().min(1).max(1e4).default(500),
18752
+ max_bytes_per_candidate: exports_external.number().int().min(1024).max(10485760).default(1048576),
18753
+ eviction_policy: exports_external.enum(["fifo"]).default("fifo"),
18754
+ ttl_days: exports_external.number().int().min(1).max(3650).default(90),
18755
+ evaluation_enabled: exports_external.boolean().default(false),
18756
+ sources: exports_external.array(DiscoverySourceSchema).default([]),
18757
+ max_candidates_per_discovery: exports_external.number().int().min(1).max(1000).default(50),
18758
+ max_concurrent_fetches: exports_external.number().int().min(1).max(20).default(5),
18759
+ fetch_timeout_ms: exports_external.number().int().min(1000).max(300000).default(30000)
18760
+ });
18761
+ DEFAULT_EXTERNAL_SKILLS_CONFIG = {
18762
+ curation_enabled: false,
18763
+ max_candidates: 500,
18764
+ max_bytes_per_candidate: 1048576,
18765
+ eviction_policy: "fifo",
18766
+ ttl_days: 90,
18767
+ evaluation_enabled: false,
18768
+ sources: [],
18769
+ max_candidates_per_discovery: 50,
18770
+ max_concurrent_fetches: 5,
18771
+ fetch_timeout_ms: 30000
18772
+ };
18627
18773
  PluginConfigSchema = exports_external.object({
18628
18774
  agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
18629
18775
  default_agent: exports_external.string().optional().transform((v) => {
@@ -18842,7 +18988,8 @@ var init_schema = __esm(() => {
18842
18988
  every_architect_turns: 5,
18843
18989
  every_minutes: 20
18844
18990
  }
18845
- }))
18991
+ })),
18992
+ external_skills: ExternalSkillsConfigSchema.optional()
18846
18993
  });
18847
18994
  });
18848
18995
 
@@ -18981,6 +19128,24 @@ function migratePresetsConfig(raw) {
18981
19128
  }
18982
19129
  return raw;
18983
19130
  }
19131
+ function sanitizeExternalSkillsConfig(raw) {
19132
+ if (!("external_skills" in raw) || raw.external_skills === undefined) {
19133
+ return raw;
19134
+ }
19135
+ const esResult = ExternalSkillsConfigSchema.safeParse(raw.external_skills);
19136
+ if (esResult.success) {
19137
+ return {
19138
+ ...raw,
19139
+ external_skills: resolveExternalSkillsConfig(esResult.data)
19140
+ };
19141
+ }
19142
+ console.warn("[opencode-swarm] external_skills config validation failed:");
19143
+ console.warn(esResult.error.format());
19144
+ console.warn("[opencode-swarm] External skills curation disabled due to invalid config. Fix the external_skills section to enable it.");
19145
+ const cleaned = { ...raw };
19146
+ delete cleaned.external_skills;
19147
+ return cleaned;
19148
+ }
18984
19149
  function loadPluginConfig(directory) {
18985
19150
  const userConfigPath = path7.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
18986
19151
  const projectConfigPath = path7.join(directory, ".opencode", CONFIG_FILENAME);
@@ -18995,10 +19160,11 @@ function loadPluginConfig(directory) {
18995
19160
  mergedRaw = deepMerge(mergedRaw, rawProjectConfig);
18996
19161
  }
18997
19162
  mergedRaw = migratePresetsConfig(mergedRaw);
19163
+ mergedRaw = sanitizeExternalSkillsConfig(mergedRaw);
18998
19164
  const result = PluginConfigSchema.safeParse(mergedRaw);
18999
19165
  if (!result.success) {
19000
19166
  if (rawUserConfig) {
19001
- const userParseResult = PluginConfigSchema.safeParse(rawUserConfig);
19167
+ const userParseResult = PluginConfigSchema.safeParse(sanitizeExternalSkillsConfig(rawUserConfig ?? {}));
19002
19168
  if (userParseResult.success) {
19003
19169
  console.warn("[opencode-swarm] Project config ignored due to validation errors. Using user config.");
19004
19170
  return userParseResult.data;
@@ -37648,7 +37814,8 @@ var init_knowledge_validator = __esm(() => {
37648
37814
  SOURCE_REF_FORBIDDEN = /(\.\.\/|\.\.\\|\0|[\x00-\x1f\x7f])/;
37649
37815
  ALLOWED_SKILL_PATH_PREFIXES = [
37650
37816
  ".opencode/skills/generated/",
37651
- ".swarm/skills/proposals/"
37817
+ ".swarm/skills/proposals/",
37818
+ ".swarm/skills/candidates/"
37652
37819
  ];
37653
37820
  VALID_DIRECTIVE_PRIORITIES = new Set([
37654
37821
  "low",
@@ -7,6 +7,8 @@ export declare const OPENCODE_NATIVE_AGENTS: Set<"compaction" | "title" | "build
7
7
  export declare const CLAUDE_CODE_NATIVE_COMMANDS: ReadonlySet<string>;
8
8
  export declare const MEMORY_TOOL_NAMES: readonly ["swarm_memory_recall", "swarm_memory_propose"];
9
9
  export declare const MEMORY_AGENT_TOOL_MAP: Partial<Record<AgentName, ToolName[]>>;
10
+ export declare const EXTERNAL_SKILL_TOOL_NAMES: readonly ["external_skill_discover", "external_skill_list", "external_skill_inspect", "external_skill_promote", "external_skill_reject", "external_skill_delete", "external_skill_revoke"];
11
+ export declare const EXTERNAL_SKILL_AGENT_TOOL_MAP: Partial<Record<AgentName, ToolName[]>>;
10
12
  /**
11
13
  * Human-readable descriptions for tools shown in the architect Available Tools block.
12
14
  * Used to generate the Available Tools section of the architect prompt at construction time.