opencode-swarm 7.15.0 → 7.17.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 CHANGED
@@ -36,7 +36,7 @@ Most AI coding tools let one model write code and ask that same model whether th
36
36
  - 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval
37
37
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion
38
38
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
39
- - 🌐 **20 languages** — TypeScript, Python, Go, Rust, Java, Kotlin, C/C++, C#, Ruby, Swift, Dart, PHP, JavaScript, CSS, Bash, PowerShell, INI, Regex
39
+ - 🌐 **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))
40
40
  - 🛡️ **Built-in security** — SAST, secrets scanning, dependency audit per task
41
41
  - 🆓 **Free tier** — works with OpenCode Zen's free model roster
42
42
  - ⚙️ **Fully configurable** — override any agent's model, disable agents, tune guardrails
@@ -1344,8 +1344,8 @@ Control how tool outputs are summarized for LLM context.
1344
1344
  | `/swarm specify [description]` | Generate or import a feature specification |
1345
1345
  | `/swarm clarify [topic]` | Clarify and refine an existing feature specification |
1346
1346
  | `/swarm analyze` | Analyze spec.md vs plan.md for requirement coverage gaps |
1347
- | `/swarm finalize [--prune-branches]` | Idempotent session close-out: retrospectives, lesson curation, evidence archive, context.md reset, config-backup cleanup, optional branch pruning |
1348
- | `/swarm close [--prune-branches]` | Deprecated alias for `/swarm finalize [--prune-branches]` |
1347
+ | `/swarm finalize [--prune-branches] [--skill-review]` | Idempotent session close-out: retrospectives, lesson curation, evidence archive, context.md reset, config-backup cleanup, optional branch pruning, optional skill-improver proposal |
1348
+ | `/swarm close [--prune-branches] [--skill-review]` | Deprecated alias for `/swarm finalize [--prune-branches] [--skill-review]` |
1349
1349
  | `/swarm write-retro` | Write a phase retrospective manually |
1350
1350
  | `/swarm handoff` | Generate a handoff summary for context-budget-critical sessions |
1351
1351
  | `/swarm simulate` | Simulate plan execution without writing code |
@@ -1,6 +1,7 @@
1
1
  import type { AgentConfig as SDKAgentConfig } from '@opencode-ai/sdk';
2
2
  import { type PluginConfig } from '../config';
3
3
  import { type AgentDefinition } from './architect';
4
+ import { type ProjectContext } from './template';
4
5
  export type { AgentDefinition } from './architect';
5
6
  /**
6
7
  * Strip the user-defined swarm prefix from an agent name to get the base
@@ -41,7 +42,7 @@ export declare function getSwarmAgents(): Record<string, {
41
42
  /**
42
43
  * Create all agent definitions with configuration applied
43
44
  */
44
- export declare function createAgents(config?: PluginConfig): AgentDefinition[];
45
+ export declare function createAgents(config?: PluginConfig, projectContext?: ProjectContext): AgentDefinition[];
45
46
  /**
46
47
  * Resolve the set of generated agent names that should be marked as primary
47
48
  * for OpenCode's session-default-agent resolution.
@@ -73,7 +74,7 @@ export declare function resolvePrimaryAgentNames(agentNames: string[], defaultAg
73
74
  /**
74
75
  * Get agent configurations formatted for the OpenCode SDK.
75
76
  */
76
- export declare function getAgentConfigs(config?: PluginConfig, directory?: string, sessionId?: string): Record<string, SDKAgentConfig>;
77
+ export declare function getAgentConfigs(config?: PluginConfig, directory?: string, sessionId?: string, projectContext?: ProjectContext): Record<string, SDKAgentConfig>;
77
78
  export { createArchitectAgent } from './architect';
78
79
  export { createCoderAgent } from './coder';
79
80
  export { DOMAIN_EXPERT_COUNCIL_PROMPT, GENERALIST_COUNCIL_PROMPT, SKEPTIC_COUNCIL_PROMPT, } from './council-prompts';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Build a `ProjectContext` for agent prompt template substitution.
3
+ *
4
+ * Called from `src/index.ts:initializeOpenCodeSwarm` immediately before
5
+ * `getAgentConfigs(...)` (Phase 4b of the language-agnostic plugin work).
6
+ * Wrapped in `withTimeout(2000ms)` by the caller; on timeout or any
7
+ * failure, the caller falls open to `emptyProjectContext()` per
8
+ * Invariant 1 (plugin init bounded + fail-open).
9
+ *
10
+ * Imported lazily by the caller via `await import('./agents/project-context')`
11
+ * to keep the dispatch import graph off the synchronous init prelude.
12
+ *
13
+ * Invariant 1 budget — Phase 4b NOTE:
14
+ * This module DOES NOT spawn subprocesses on the session-init critical
15
+ * path. The full `LanguageBackend.selectTestFramework` /
16
+ * `selectBuildCommand` hooks call `isCommandAvailable` (which spawns
17
+ * `where`/`which` and can take 200–500ms per call on Windows). Even with
18
+ * the 2000ms `withTimeout` wrapper, multiple sequential spawns
19
+ * (typically 3–5 per buildProjectContext call) easily push `server()`
20
+ * past the 400ms Invariant 1 deadline asserted by
21
+ * `scripts/repro-704.mjs:TIMING_DEADLINE_MS`.
22
+ *
23
+ * The architect prompt's `TEST_CMD` / `BUILD_CMD` / `LINT_CMD` values are
24
+ * HINTS for the LLM. If the user doesn't have the named binary installed,
25
+ * the actual test-runner / build-runner tool will surface a clear error
26
+ * at invocation time — there is no correctness regression from skipping
27
+ * the PATH probe at session init.
28
+ */
29
+ import { pickBackend, pickedProfiles } from '../lang/dispatch';
30
+ import { type ProjectContext } from './template';
31
+ /**
32
+ * Wall-clock budget for the session-init language-backend resolution step.
33
+ * Caller (`src/index.ts:initializeOpenCodeSwarm`) wraps `buildProjectContext`
34
+ * in `withTimeout(LANG_BACKEND_DETECTION_TIMEOUT_MS)`. Exceeding the budget
35
+ * fails open with `null` so the manifest still returns to the OpenCode
36
+ * plugin host (Invariant 1).
37
+ */
38
+ export declare const LANG_BACKEND_DETECTION_TIMEOUT_MS = 300;
39
+ declare const _internals: {
40
+ pickBackend: typeof pickBackend;
41
+ pickedProfiles: typeof pickedProfiles;
42
+ };
43
+ export { _internals };
44
+ /**
45
+ * Resolve the `ProjectContext` for `directory`. Uses `pickBackend` to find
46
+ * the dominant language, then queries the backend's PROFILE DATA (not its
47
+ * spawn-bearing hooks) for build/test/lint commands. Calls the optional
48
+ * `selectFramework` and `selectEntryPoints` hooks because those are
49
+ * filesystem-only (no spawn) per the backend purity invariant.
50
+ *
51
+ * Per-backend constraint blocks (coder/test/reviewer) come from
52
+ * `backend.prompts` — pure data.
53
+ *
54
+ * Returns `null` (caller substitutes `emptyProjectContext()`) when no
55
+ * backend is detected — the architect's existing DISCOVER mode handles
56
+ * the resulting `unresolved` sentinel placeholders.
57
+ */
58
+ export declare function buildProjectContext(directory: string): Promise<ProjectContext | null>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Agent prompt template renderer.
3
+ *
4
+ * Replaces `{{KEY}}` placeholders in agent prompt strings with values from
5
+ * a `ProjectContext` resolved at session-init time. Strict by design:
6
+ * unknown placeholders raise (caught at build time by
7
+ * `tests/unit/agents/template-substitution.test.ts`), so a typo never
8
+ * leaks to the model.
9
+ *
10
+ * Phase 4b of language-agnostic plugin work. Pinned call site is
11
+ * `src/index.ts:initializeOpenCodeSwarm` immediately before
12
+ * `getAgentConfigs(...)` — see the `withTimeout(2000ms)` wrapping there
13
+ * to honor invariant 1 (plugin init bounded + fail-open).
14
+ */
15
+ /**
16
+ * Variables available for substitution into agent prompts. Every prompt's
17
+ * `{{KEY}}` placeholders must be a key of this interface; the renderer
18
+ * rejects unknown placeholders. New variables go here AND in
19
+ * `buildProjectContext` in `src/index.ts`.
20
+ */
21
+ export interface ProjectContext {
22
+ PROJECT_LANGUAGE: string;
23
+ PROJECT_FRAMEWORK: string;
24
+ BUILD_CMD: string;
25
+ TEST_CMD: string;
26
+ LINT_CMD: string;
27
+ ENTRY_POINTS: string;
28
+ /**
29
+ * Per-language coder constraint bullets (already escaped for inclusion
30
+ * in a TypeScript template literal — see `escapeForTemplate`).
31
+ */
32
+ CODER_CONSTRAINTS: string;
33
+ /** Per-language test-writing constraint bullets. */
34
+ TEST_CONSTRAINTS: string;
35
+ /** Per-language reviewer-checklist bullets. */
36
+ REVIEWER_CHECKLIST: string;
37
+ /**
38
+ * When backend detection finds multiple equal-tier languages, this is
39
+ * a comma-separated list of the runner-up language ids; empty string
40
+ * when only one language is detected.
41
+ */
42
+ PROJECT_CONTEXT_SECONDARY_LANGUAGES: string;
43
+ }
44
+ /**
45
+ * Sentinel substituted into placeholders when the backend cannot resolve
46
+ * a value (no manifest, binary missing, detection timed out). The
47
+ * architect prompt's existing DISCOVER mode handles this — same contract
48
+ * as today, but the trigger is now a literal sentinel string rather than
49
+ * a templating leak.
50
+ */
51
+ export declare const UNRESOLVED = "unresolved (run /swarm preflight)";
52
+ /** Empty `ProjectContext` — used by fail-open paths and tests. */
53
+ export declare function emptyProjectContext(): ProjectContext;
54
+ /**
55
+ * Escape a string for safe inclusion inside a TypeScript template literal.
56
+ * Specifically:
57
+ * - Backticks `` ` `` become `` \` `` (otherwise terminate the literal).
58
+ * - `${` becomes `\${` (otherwise begins an interpolation).
59
+ * - Backslashes are preserved as-is (template literals don't double-escape
60
+ * them when read at runtime — only at parse time, which we're past).
61
+ *
62
+ * See `.claude/skills/engineering-conventions/SKILL.md` "Agent prompt
63
+ * strings — escaping pitfalls" for context. Profile-author-supplied
64
+ * constraint strings (e.g. `LanguageProfile.prompts.coderConstraints`)
65
+ * routinely contain backticks (when describing code idioms like `bun:test`).
66
+ * The renderer auto-escapes them so a profile author can't accidentally
67
+ * break agent compilation.
68
+ */
69
+ export declare function escapeForTemplate(s: string): string;
70
+ /**
71
+ * Render `prompt` with `vars`. Replaces every `{{KEY}}` whose KEY is a
72
+ * documented `ProjectContext` field. Unknown placeholders raise.
73
+ *
74
+ * The renderer is intentionally simple — single-pass, no nesting, no
75
+ * conditionals, no loops. Agent prompts that need conditional sections
76
+ * should pre-compute a string variable in `buildProjectContext` and
77
+ * substitute it as a single placeholder.
78
+ */
79
+ export declare function renderPrompt(prompt: string, vars: ProjectContext): string;
80
+ /**
81
+ * Convert an array of constraint strings into a bulleted block ready for
82
+ * inclusion in an agent prompt via `{{CODER_CONSTRAINTS}}` etc.
83
+ * Each item is escaped for template-literal safety.
84
+ */
85
+ export declare function bulletList(items: readonly string[]): string;
@@ -1,4 +1,5 @@
1
1
  import { tool } from '@opencode-ai/plugin';
2
+ import { bunSpawnSync } from '../utils/bun-compat';
2
3
  export interface BuildCommand {
3
4
  ecosystem: string;
4
5
  command: string;
@@ -16,10 +17,6 @@ export interface BuildDiscoveryOptions {
16
17
  scope?: 'changed' | 'all';
17
18
  changedFiles?: string[];
18
19
  }
19
- /**
20
- * Check if a command exists on PATH
21
- * Uses 'where' on Windows, 'which' on Unix
22
- */
23
20
  export declare function isCommandAvailable(command: string): boolean;
24
21
  /**
25
22
  * Discover build commands using language profiles (primary detection path)
@@ -30,6 +27,7 @@ export declare const _internals: {
30
27
  discoverBuildCommands: typeof discoverBuildCommands;
31
28
  clearToolchainCache: typeof clearToolchainCache;
32
29
  getEcosystems: typeof getEcosystems;
30
+ spawnSyncImpl: typeof bunSpawnSync;
33
31
  };
34
32
  export declare function discoverBuildCommandsFromProfiles(workingDir: string): Promise<BuildDiscoveryResult>;
35
33
  /**