opencode-swarm 7.16.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 +1 -1
- package/dist/agents/index.d.ts +3 -2
- package/dist/agents/project-context.d.ts +58 -0
- package/dist/agents/template.d.ts +85 -0
- package/dist/build/discovery.d.ts +2 -4
- package/dist/cli/index.js +1745 -343
- package/dist/index.js +2939 -1367
- package/dist/lang/backend.d.ts +199 -0
- package/dist/lang/backends/go.d.ts +21 -0
- package/dist/lang/backends/index.d.ts +27 -0
- package/dist/lang/backends/python.d.ts +25 -0
- package/dist/lang/backends/typescript.d.ts +56 -0
- package/dist/lang/default-backend.d.ts +105 -0
- package/dist/lang/dispatch.d.ts +52 -0
- package/dist/lang/profiles.d.ts +28 -0
- package/dist/lang/registry-backend.d.ts +35 -0
- package/dist/test-impact/analyzer.d.ts +7 -0
- package/dist/tools/test-runner.d.ts +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LanguageBackend — behavior-bearing extension of LanguageProfile.
|
|
3
|
+
*
|
|
4
|
+
* `LanguageProfile` (in `./profiles.ts`) is a passive data record: it
|
|
5
|
+
* declares which build commands, test frameworks, linters etc. exist for a
|
|
6
|
+
* language, but does not know how to run them. A `LanguageBackend` adds
|
|
7
|
+
* optional behavior hooks. Every hook has a registry-driven default in
|
|
8
|
+
* `./default-backend.ts`, so a backend that overrides nothing still works.
|
|
9
|
+
*
|
|
10
|
+
* Invariant boundaries (per AGENTS.md):
|
|
11
|
+
* - Backends NEVER spawn subprocesses. They return command-arrays only.
|
|
12
|
+
* The single spawn site stays in `src/tools/test-runner.ts` (and the
|
|
13
|
+
* existing helpers in `src/build/discovery.ts:isCommandAvailable`),
|
|
14
|
+
* each of which already satisfies invariant 3 (cwd, stdin: 'ignore',
|
|
15
|
+
* timeout, bounded stdio, killable). This rule is enforced by
|
|
16
|
+
* `tests/unit/lang/backend-purity.test.ts`.
|
|
17
|
+
* - Backends do no top-level `bun:` imports and no direct `Bun.*` calls
|
|
18
|
+
* (invariant 2 — runtime portability). Same purity test enforces this.
|
|
19
|
+
*
|
|
20
|
+
* Extension model: a new language is a single new file under
|
|
21
|
+
* `src/lang/backends/<id>.ts` plus one import line in
|
|
22
|
+
* `src/lang/backends/index.ts`. The default backend handles everything the
|
|
23
|
+
* new file does not override.
|
|
24
|
+
*/
|
|
25
|
+
import type { LanguageProfile } from './profiles';
|
|
26
|
+
/**
|
|
27
|
+
* Selected test framework for a project, including the concrete spawn argv
|
|
28
|
+
* and explicit cwd. Returned by `LanguageBackend.selectTestFramework`.
|
|
29
|
+
*/
|
|
30
|
+
export interface TestFrameworkSelection {
|
|
31
|
+
/** Framework id matching one of LanguageProfile.test.frameworks[*].name. */
|
|
32
|
+
name: string;
|
|
33
|
+
/**
|
|
34
|
+
* Spawn-arg array. Never includes shell metacharacters or relies on shell
|
|
35
|
+
* interpretation — passed directly to `bunSpawn(cmd, ...)`. Backends that
|
|
36
|
+
* cannot avoid a shell-mediated invocation (e.g. PowerShell `-EncodedCommand`)
|
|
37
|
+
* still produce an array; the array's first element is the binary and the
|
|
38
|
+
* rest are individual arguments.
|
|
39
|
+
*/
|
|
40
|
+
cmd: string[];
|
|
41
|
+
/** Explicit cwd for the spawn (invariant 3). */
|
|
42
|
+
cwd: string;
|
|
43
|
+
/** Human-readable note: "package.json scripts.test", "Cargo.toml", etc. */
|
|
44
|
+
detectedVia: string;
|
|
45
|
+
/**
|
|
46
|
+
* When true, the `files` argument to `buildTestCommand` is ignored — the
|
|
47
|
+
* framework runs all tests in the project by default (e.g. cargo test,
|
|
48
|
+
* go test ./..., swift test). Per-file selection is the framework's
|
|
49
|
+
* concern, not the backend's.
|
|
50
|
+
*/
|
|
51
|
+
filesIgnored?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Structured summary of a test run. The default backend returns only
|
|
55
|
+
* exit-code-driven `ok` and the raw streams; richer parsing is opt-in per
|
|
56
|
+
* backend (e.g. the TypeScript backend parses bun:test JSON output).
|
|
57
|
+
*/
|
|
58
|
+
export interface TestRunSummary {
|
|
59
|
+
ok: boolean;
|
|
60
|
+
raw: {
|
|
61
|
+
stdout: string;
|
|
62
|
+
stderr: string;
|
|
63
|
+
exitCode: number;
|
|
64
|
+
};
|
|
65
|
+
passed?: number;
|
|
66
|
+
failed?: number;
|
|
67
|
+
skipped?: number;
|
|
68
|
+
durationMs?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Total tests reported by the framework. When undefined the caller may
|
|
71
|
+
* compute `passed + failed + skipped`.
|
|
72
|
+
*/
|
|
73
|
+
total?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Coverage percentage parsed from the framework's output. Optional —
|
|
76
|
+
* frameworks without a uniform coverage-line format (mocha, go-test,
|
|
77
|
+
* etc.) leave this undefined.
|
|
78
|
+
*/
|
|
79
|
+
coveragePercent?: number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Scope strings used by the test-runner tool. Re-exported here so backends
|
|
83
|
+
* can accept them in `buildTestCommand` without a circular import back to
|
|
84
|
+
* `src/tools/test-runner.ts`.
|
|
85
|
+
*/
|
|
86
|
+
export type TestScope = 'all' | 'convention' | 'graph' | 'impact';
|
|
87
|
+
/**
|
|
88
|
+
* Options influencing build-command construction. Backends may ignore
|
|
89
|
+
* unrecognized opts; the default backend honors all of these.
|
|
90
|
+
*/
|
|
91
|
+
export interface BuildTestCommandOpts {
|
|
92
|
+
scope?: TestScope;
|
|
93
|
+
coverage?: boolean;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Selected web/UI framework for a project (PROJECT_FRAMEWORK template
|
|
97
|
+
* variable). Best-effort detection — backends return null when no
|
|
98
|
+
* framework signal is available, and the architect's prompt then ships
|
|
99
|
+
* with the `unresolved (run /swarm preflight)` sentinel.
|
|
100
|
+
*/
|
|
101
|
+
export interface FrameworkSelection {
|
|
102
|
+
/** Display name, e.g. "react", "vue", "django", "gin". */
|
|
103
|
+
name: string;
|
|
104
|
+
/** Human-readable note describing what evidence supports this. */
|
|
105
|
+
detectedVia: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Selected build command for a project.
|
|
109
|
+
*/
|
|
110
|
+
export interface BuildCommandSelection {
|
|
111
|
+
/** Display name matching `LanguageProfile.build.commands[*].name`. */
|
|
112
|
+
name: string;
|
|
113
|
+
/** Spawn-arg array. Same constraints as TestFrameworkSelection.cmd. */
|
|
114
|
+
cmd: string[];
|
|
115
|
+
/** Explicit cwd. */
|
|
116
|
+
cwd: string;
|
|
117
|
+
/** Human-readable note: "Cargo.toml", "package.json#scripts.build", etc. */
|
|
118
|
+
detectedVia: string;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The behavior surface for a language. Every method is optional; the
|
|
122
|
+
* default-backend implementation in `./default-backend.ts` provides
|
|
123
|
+
* registry-driven fallbacks that work for most languages out of the box.
|
|
124
|
+
*/
|
|
125
|
+
export interface LanguageBackend extends LanguageProfile {
|
|
126
|
+
/**
|
|
127
|
+
* Stronger signal than extension matching alone. Default behavior
|
|
128
|
+
* (provided by the default backend) checks that any of
|
|
129
|
+
* `profile.build.detectFiles` is present in `dir`. A backend may override
|
|
130
|
+
* to add language-specific heuristics (e.g. the TypeScript backend reads
|
|
131
|
+
* `package.json#scripts.test` to confirm a test runner is configured).
|
|
132
|
+
*/
|
|
133
|
+
detectProject?(dir: string): Promise<boolean>;
|
|
134
|
+
/**
|
|
135
|
+
* Pick the highest-priority test framework whose detect file exists in
|
|
136
|
+
* `dir` AND whose binary is on PATH. Returns `null` if no framework is
|
|
137
|
+
* configured + available. Default behavior consults
|
|
138
|
+
* `profile.test.frameworks` sorted by priority and uses
|
|
139
|
+
* `isCommandAvailable` from `src/build/discovery.ts`.
|
|
140
|
+
*/
|
|
141
|
+
selectTestFramework?(dir: string): Promise<TestFrameworkSelection | null>;
|
|
142
|
+
/**
|
|
143
|
+
* Build the spawn argv for a given framework + file list. Default
|
|
144
|
+
* behavior implements the full 14-framework legacy switch from
|
|
145
|
+
* `src/tools/test-runner.ts` (coverage flags, scope-dependent file
|
|
146
|
+
* inclusion, platform-specific python/python3, pester -EncodedCommand,
|
|
147
|
+
* gradle wrapper detection, ctest build-dir detection, dart/flutter
|
|
148
|
+
* selection, bundle/rspec detection, minitest require_relative).
|
|
149
|
+
* Backends with non-trivial language-specific shape override this.
|
|
150
|
+
*
|
|
151
|
+
* Returns `null` when the framework is unknown to this backend; callers
|
|
152
|
+
* (test-runner dispatch) treat that as "no test command available".
|
|
153
|
+
*/
|
|
154
|
+
buildTestCommand?(framework: string, files: string[], dir: string, opts?: BuildTestCommandOpts): string[] | null;
|
|
155
|
+
/**
|
|
156
|
+
* Parse stdout/stderr into a structured summary. Default behavior
|
|
157
|
+
* returns only `{ ok: exitCode === 0, raw: { stdout, stderr, exitCode } }`
|
|
158
|
+
* — no regex, no framework-specific assumptions. Backends that want
|
|
159
|
+
* pass/fail counts (e.g. the TypeScript backend's bun:test JSON parser)
|
|
160
|
+
* override this.
|
|
161
|
+
*/
|
|
162
|
+
parseTestOutput?(framework: string, stdout: string, stderr: string, exitCode: number): TestRunSummary;
|
|
163
|
+
/**
|
|
164
|
+
* Map a source file to candidate test files (convention scope). Default
|
|
165
|
+
* behavior: swap `src/` ↔ `tests/` and the extension to one of the
|
|
166
|
+
* profile's test-file conventions. Returns the candidate paths sorted by
|
|
167
|
+
* likelihood.
|
|
168
|
+
*/
|
|
169
|
+
testFilesFor?(sourceFile: string, dir: string): Promise<string[]>;
|
|
170
|
+
/**
|
|
171
|
+
* Extract import paths from a source file (graph/impact scope). Default
|
|
172
|
+
* behavior: returns `[]` — the analyzer falls back to convention scope
|
|
173
|
+
* with an explicit "graph scope unavailable for {lang}" notice. Backends
|
|
174
|
+
* with import-graph support (TypeScript, Python, Go in this phase set)
|
|
175
|
+
* override this.
|
|
176
|
+
*/
|
|
177
|
+
extractImports?(sourceFile: string, source: string): string[];
|
|
178
|
+
/**
|
|
179
|
+
* Pick the build command for this project. Default behavior consults
|
|
180
|
+
* `profile.build.commands` sorted by priority + binary-on-PATH check.
|
|
181
|
+
*/
|
|
182
|
+
selectBuildCommand?(dir: string): Promise<BuildCommandSelection | null>;
|
|
183
|
+
/**
|
|
184
|
+
* Detect the dominant web/UI framework in this project (React, Django,
|
|
185
|
+
* Gin, etc.) for the architect's PROJECT_FRAMEWORK template variable.
|
|
186
|
+
* Returns null when no framework signal is present — the default
|
|
187
|
+
* backend's implementation looks for common framework manifest fields
|
|
188
|
+
* (package.json deps, requirements.txt entries, go.mod requires).
|
|
189
|
+
*/
|
|
190
|
+
selectFramework?(dir: string): Promise<FrameworkSelection | null>;
|
|
191
|
+
/**
|
|
192
|
+
* Identify primary entry-point files for this project (ENTRY_POINTS
|
|
193
|
+
* template variable). Default behavior: read profile-specific manifests
|
|
194
|
+
* (package.json `main`/`bin`, pyproject `[project.scripts]`, go.mod
|
|
195
|
+
* + main.go, etc.). Returns absolute or repo-relative paths sorted by
|
|
196
|
+
* confidence. Empty list maps to the sentinel.
|
|
197
|
+
*/
|
|
198
|
+
selectEntryPoints?(dir: string): Promise<string[]>;
|
|
199
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go backend.
|
|
3
|
+
*
|
|
4
|
+
* Phase 5 of language-agnostic plugin work. Overrides `extractImports`
|
|
5
|
+
* with Go-specific import regexes — both single-line `import "x"` and
|
|
6
|
+
* grouped `import (\n "a"\n "b"\n)` forms — so the test-impact analyzer
|
|
7
|
+
* can build a graph for Go projects.
|
|
8
|
+
*
|
|
9
|
+
* Invariants identical to other backends — see `python.ts` and
|
|
10
|
+
* `typescript.ts` for the rationale; backend-purity test enforces.
|
|
11
|
+
*/
|
|
12
|
+
import type { LanguageBackend } from '../backend';
|
|
13
|
+
declare function extractImports(_sourceFile: string, source: string): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Build the Go backend from the registered profile.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildGoBackend(): LanguageBackend;
|
|
18
|
+
export declare const _internals: {
|
|
19
|
+
extractImports: typeof extractImports;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend registration surface.
|
|
3
|
+
*
|
|
4
|
+
* Adding a new language with first-class behavior is a single new file
|
|
5
|
+
* under this directory plus one `import` line + one `register(...)` call
|
|
6
|
+
* here. Importing this module triggers all registrations.
|
|
7
|
+
*
|
|
8
|
+
* Phase 2 ships only the TypeScript backend. Phase 5 will add Python and
|
|
9
|
+
* Go backends with import-graph extractors.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Register all known backends. Idempotent via the module-level `registered`
|
|
13
|
+
* flag.
|
|
14
|
+
*
|
|
15
|
+
* The flag is load-bearing, not redundant with the registry's own duplicate
|
|
16
|
+
* guard: `buildTypescriptBackend()` constructs a fresh object every call, so
|
|
17
|
+
* a second call without the flag would produce a different reference and
|
|
18
|
+
* trip `LanguageBackendRegistry.register`'s `existing !== backend` throw.
|
|
19
|
+
* The flag prevents that by short-circuiting before constructing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerAllBackends(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Test-only: reset the registration flag and unregister all known
|
|
24
|
+
* backends. Allows tests to verify the registration logic itself without
|
|
25
|
+
* cross-file singleton pollution.
|
|
26
|
+
*/
|
|
27
|
+
export declare function _resetForTesting(): void;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python backend.
|
|
3
|
+
*
|
|
4
|
+
* Phase 5 of language-agnostic plugin work. Overrides `extractImports`
|
|
5
|
+
* with Python-specific import regexes (`import x`, `from x import y`)
|
|
6
|
+
* so the test-impact analyzer can build a graph for Python projects.
|
|
7
|
+
* Other hooks (selectTestFramework, selectBuildCommand, parseTestOutput,
|
|
8
|
+
* testFilesFor) inherit the registry-driven defaults.
|
|
9
|
+
*
|
|
10
|
+
* Invariants (same as typescript.ts):
|
|
11
|
+
* - No subprocess calls; defers binary checks to `isCommandAvailable`.
|
|
12
|
+
* - No `bun:` imports, no `Bun.*` calls.
|
|
13
|
+
* - Backend-purity test in `tests/unit/lang/backend-purity.test.ts`
|
|
14
|
+
* enforces both at PR time.
|
|
15
|
+
*/
|
|
16
|
+
import type { LanguageBackend } from '../backend';
|
|
17
|
+
declare function extractImports(_sourceFile: string, source: string): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Build the Python backend from the registered profile.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildPythonBackend(): LanguageBackend;
|
|
22
|
+
export declare const _internals: {
|
|
23
|
+
extractImports: typeof extractImports;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript / JavaScript backend.
|
|
3
|
+
*
|
|
4
|
+
* Overrides the default backend's `selectTestFramework` to honor
|
|
5
|
+
* `package.json#scripts.test` (the canonical signal in the JS ecosystem)
|
|
6
|
+
* and `extractImports` to parse ES6 + CommonJS imports for the
|
|
7
|
+
* graph/impact analyzer.
|
|
8
|
+
*
|
|
9
|
+
* Phase 2 deliverable: this backend exists and registers itself, but
|
|
10
|
+
* `src/tools/test-runner.ts` and `src/test-impact/analyzer.ts` do not yet
|
|
11
|
+
* call into it — they still use their existing switch-statement helpers.
|
|
12
|
+
* Phase 3 wires the test-runner dispatch through this backend.
|
|
13
|
+
*
|
|
14
|
+
* Invariants:
|
|
15
|
+
* - No subprocess calls (defers to `isCommandAvailable` from
|
|
16
|
+
* `../../build/discovery` for binary checks; that helper already
|
|
17
|
+
* satisfies invariant 3).
|
|
18
|
+
* - No `bun:` imports, no `Bun.*` calls (invariant 2).
|
|
19
|
+
* - No mutation of LANGUAGE_REGISTRY at import time — only registers a
|
|
20
|
+
* backend in LANGUAGE_BACKEND_REGISTRY via `backends/index.ts`.
|
|
21
|
+
*/
|
|
22
|
+
import type { LanguageBackend } from '../backend';
|
|
23
|
+
interface PackageJsonShape {
|
|
24
|
+
scripts?: Record<string, string>;
|
|
25
|
+
dependencies?: Record<string, string>;
|
|
26
|
+
devDependencies?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Read package.json. Returns null when missing or malformed. Bounded by a
|
|
30
|
+
* single sync `fs.readFileSync` — no subprocess.
|
|
31
|
+
*
|
|
32
|
+
* Routed through `_internals.readPackageJsonRaw` so tests can substitute a
|
|
33
|
+
* different reader without touching the filesystem. The adversarial review
|
|
34
|
+
* (PR #825) flagged that this seam was advertised but unused.
|
|
35
|
+
*/
|
|
36
|
+
declare function readPackageJsonRaw(dir: string): PackageJsonShape | null;
|
|
37
|
+
/** Convenience: read just `scripts.test` (used by tests). */
|
|
38
|
+
declare function readPackageJsonTestScript(dir: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Map a `package.json#scripts.test` invocation to a framework name. The
|
|
41
|
+
* mapping mirrors `detectTestFramework` in `src/tools/test-runner.ts:286–326`.
|
|
42
|
+
*/
|
|
43
|
+
declare function frameworkFromScriptsTest(script: string): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Build the TypeScript backend from the registered profile. Backend
|
|
46
|
+
* registration happens in `./index.ts` (the single import-and-register
|
|
47
|
+
* surface) — this module just exports the factory so the registration
|
|
48
|
+
* site is explicit.
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildTypescriptBackend(): LanguageBackend;
|
|
51
|
+
export declare const _internals: {
|
|
52
|
+
readPackageJsonRaw: typeof readPackageJsonRaw;
|
|
53
|
+
readPackageJsonTestScript: typeof readPackageJsonTestScript;
|
|
54
|
+
frameworkFromScriptsTest: typeof frameworkFromScriptsTest;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default backend — registry-driven implementations of every optional hook
|
|
3
|
+
* on `LanguageBackend`. A backend that overrides nothing still works for
|
|
4
|
+
* common cases: any profile with build.commands + test.frameworks +
|
|
5
|
+
* lint.linters declared correctly will get a working `selectTestFramework`,
|
|
6
|
+
* `selectBuildCommand`, etc. without writing any backend code.
|
|
7
|
+
*
|
|
8
|
+
* No subprocess calls happen here — `isCommandAvailable` is the only seam
|
|
9
|
+
* to the environment, and it lives in `src/build/discovery.ts` with full
|
|
10
|
+
* invariant-3 properties (cwd, stdin: 'ignore', timeout, bounded stdio).
|
|
11
|
+
*/
|
|
12
|
+
import type { BuildCommandSelection, BuildTestCommandOpts, FrameworkSelection, LanguageBackend, TestFrameworkSelection, TestRunSummary } from './backend';
|
|
13
|
+
import type { LanguageProfile } from './profiles';
|
|
14
|
+
/**
|
|
15
|
+
* Tokenize a string command into an array. Splits on whitespace; respects
|
|
16
|
+
* single and double quotes for argument grouping. Used to convert profile
|
|
17
|
+
* `cmd` strings (which today are written as "npx tsc --noEmit" etc.) into
|
|
18
|
+
* the array form `bunSpawn` expects.
|
|
19
|
+
*
|
|
20
|
+
* This deliberately does NOT support shell metacharacters (`;`, `&`, `|`,
|
|
21
|
+
* `>`, `<`, backticks, `$()`) — backends with non-trivial commands must
|
|
22
|
+
* override `buildTestCommand`/`selectBuildCommand` to return a custom
|
|
23
|
+
* `cmd: string[]`. Splitting a profile string into words is a 90% case;
|
|
24
|
+
* the 10% override their backend.
|
|
25
|
+
*/
|
|
26
|
+
export declare function tokenizeCommand(cmd: string): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Default selectTestFramework: highest-priority framework whose detect
|
|
29
|
+
* file exists AND whose binary is on PATH. Returns null if none.
|
|
30
|
+
*/
|
|
31
|
+
export declare function defaultSelectTestFramework(profile: LanguageProfile, dir: string): Promise<TestFrameworkSelection | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Default buildTestCommand: full 14-framework switch ported verbatim from
|
|
34
|
+
* the legacy logic that lived in `src/tools/test-runner.ts:buildTestCommand`
|
|
35
|
+
* (pre-Phase-3b). Handles per-framework coverage flags, scope-dependent
|
|
36
|
+
* file inclusion, platform-specific python/python3, pester
|
|
37
|
+
* `-EncodedCommand` for safe path passing, gradlew detection, ctest build-
|
|
38
|
+
* directory probing, flutter-vs-dart selection, bundle/rspec detection,
|
|
39
|
+
* and the minitest `require_relative` trick for multi-file runs.
|
|
40
|
+
*
|
|
41
|
+
* Backends are free to override individual framework cases via their own
|
|
42
|
+
* `buildTestCommand` — this default is a single source of truth so adding
|
|
43
|
+
* a 15th framework only requires one switch arm.
|
|
44
|
+
*
|
|
45
|
+
* `dir` is the base directory used for gradlew/ctest manifest probing.
|
|
46
|
+
* `opts.scope` defaults to `'all'`; `opts.coverage` defaults to `false`.
|
|
47
|
+
* Returns null when the framework name is not in the supported set.
|
|
48
|
+
*/
|
|
49
|
+
export declare function defaultBuildTestCommand(profile: LanguageProfile, framework: string, files: string[], dir?: string, opts?: BuildTestCommandOpts): string[] | null;
|
|
50
|
+
/**
|
|
51
|
+
* Default parseTestOutput: full 14-framework switch ported verbatim from
|
|
52
|
+
* `src/tools/test-runner.ts:parseTestOutput`. Returns a TestRunSummary
|
|
53
|
+
* with `passed`/`failed`/`skipped`/`total`/`coveragePercent` populated
|
|
54
|
+
* for every supported framework. Unknown frameworks return an
|
|
55
|
+
* exit-code-only summary.
|
|
56
|
+
*
|
|
57
|
+
* `framework` is the union-name string (e.g. 'bun', 'vitest', 'pytest').
|
|
58
|
+
* Callers pass the combined stdout+stderr as `stdout` and an empty
|
|
59
|
+
* string for `stderr` per the legacy convention — the legacy parser
|
|
60
|
+
* always concatenated streams before parsing.
|
|
61
|
+
*/
|
|
62
|
+
export declare function defaultParseTestOutput(framework: string, stdout: string, stderr: string, exitCode: number): TestRunSummary;
|
|
63
|
+
/**
|
|
64
|
+
* Default detectProject: any of `profile.build.detectFiles` is present in
|
|
65
|
+
* `dir`. Honors simple glob patterns the same way `detectFileExists` does.
|
|
66
|
+
*/
|
|
67
|
+
export declare function defaultDetectProject(profile: LanguageProfile, dir: string): Promise<boolean>;
|
|
68
|
+
/**
|
|
69
|
+
* Default selectBuildCommand: highest-priority command whose detectFile
|
|
70
|
+
* (if specified) exists AND whose binary is on PATH. Returns null if none.
|
|
71
|
+
*/
|
|
72
|
+
export declare function defaultSelectBuildCommand(profile: LanguageProfile, dir: string): Promise<BuildCommandSelection | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Default testFilesFor: convention swap `src/<x>.<ext>` ↔ `tests/<x>.<ext>`
|
|
75
|
+
* (and `tests/<x>_test.<ext>`, `tests/<x>.test.<ext>`). Returns candidates
|
|
76
|
+
* sorted by likelihood. Best-effort — backends with established patterns
|
|
77
|
+
* (e.g. Python's `tests/test_<x>.py`) override.
|
|
78
|
+
*/
|
|
79
|
+
export declare function defaultTestFilesFor(profile: LanguageProfile, sourceFile: string, dir: string): Promise<string[]>;
|
|
80
|
+
/**
|
|
81
|
+
* Default extractImports: returns []. The analyzer treats this as
|
|
82
|
+
* "graph scope unavailable for {lang}" and falls back to convention scope
|
|
83
|
+
* with an explicit notice. Backends with parser-driven extraction
|
|
84
|
+
* (TypeScript, Python, Go in the language-agnostic plan's Phase 5) override.
|
|
85
|
+
*/
|
|
86
|
+
export declare function defaultExtractImports(): string[];
|
|
87
|
+
/**
|
|
88
|
+
* Default selectFramework: returns null. Frameworks (React, Django, Gin)
|
|
89
|
+
* are not detectable from a profile alone — a concrete backend must read
|
|
90
|
+
* its language-specific manifest. Returning null causes the architect's
|
|
91
|
+
* PROJECT_FRAMEWORK placeholder to resolve to the `unresolved` sentinel.
|
|
92
|
+
*/
|
|
93
|
+
export declare function defaultSelectFramework(): Promise<FrameworkSelection | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Default selectEntryPoints: returns []. Concrete backends override per
|
|
96
|
+
* language. Empty list maps to the `unresolved` sentinel.
|
|
97
|
+
*/
|
|
98
|
+
export declare function defaultSelectEntryPoints(): Promise<string[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Build a backend object that delegates every hook to the registry-driven
|
|
101
|
+
* defaults. Used by `pickBackend` when no language-specific override has
|
|
102
|
+
* been registered. The returned object is a structural `LanguageBackend`
|
|
103
|
+
* (it spreads the profile, then attaches default method bindings).
|
|
104
|
+
*/
|
|
105
|
+
export declare function defaultBackendFor(profile: LanguageProfile): LanguageBackend;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch: pick the right `LanguageBackend` for a directory.
|
|
3
|
+
*
|
|
4
|
+
* `pickBackend(dir)` walks up from `dir` to find the nearest project
|
|
5
|
+
* manifest, runs language detection on that root, and returns the
|
|
6
|
+
* registered (or defaulted) backend for the dominant language. Caches
|
|
7
|
+
* results in a bounded LRU keyed by (dir, manifest-hash) so repeated calls
|
|
8
|
+
* during a session do not re-walk the filesystem.
|
|
9
|
+
*
|
|
10
|
+
* Per the language-agnostic plan, hot-path callers (hooks, tools) wrap
|
|
11
|
+
* this in `withTimeout(200ms)` and fail open on the cache miss; session-
|
|
12
|
+
* start callers use `withTimeout(2000ms)`. Both budgets are caller-set —
|
|
13
|
+
* the dispatch function itself does not impose timeouts.
|
|
14
|
+
*
|
|
15
|
+
* Invariant 4: this module never writes to `.swarm/`. All caching is
|
|
16
|
+
* in-process. `dir` is treated as caller-supplied and not validated as a
|
|
17
|
+
* project root — callers are responsible for passing the right directory.
|
|
18
|
+
*/
|
|
19
|
+
import type { LanguageBackend } from './backend';
|
|
20
|
+
import './backends';
|
|
21
|
+
import { detectProjectLanguages } from './detector';
|
|
22
|
+
declare const _internals: {
|
|
23
|
+
detectProjectLanguages: typeof detectProjectLanguages;
|
|
24
|
+
cacheCapacity: number;
|
|
25
|
+
};
|
|
26
|
+
export { _internals };
|
|
27
|
+
/**
|
|
28
|
+
* Pick the most appropriate `LanguageBackend` for `dir`. Walks up to find
|
|
29
|
+
* the manifest root, detects languages there, returns the highest-tier
|
|
30
|
+
* backend (with the default backend synthesized for ids that have no
|
|
31
|
+
* registered override). Returns null if no language is detected.
|
|
32
|
+
*
|
|
33
|
+
* The dispatch is cached by `(manifestRoot, manifestHash)`; cache entries
|
|
34
|
+
* are invalidated automatically when any manifest's size or mtime changes.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pickBackend(dir: string): Promise<LanguageBackend | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Return the ranked language profile list `pickBackend` last detected for
|
|
39
|
+
* `dir`. Used by `buildProjectContext` to populate
|
|
40
|
+
* `PROJECT_CONTEXT_SECONDARY_LANGUAGES` without re-running
|
|
41
|
+
* `detectProjectLanguages`. Returns an empty array when no cached entry
|
|
42
|
+
* matches (caller should invoke `pickBackend(dir)` first to warm the
|
|
43
|
+
* cache).
|
|
44
|
+
*/
|
|
45
|
+
export declare function pickedProfiles(dir: string): ReadonlyArray<{
|
|
46
|
+
id: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Test-only: clear the dispatch cache. Production code should never call
|
|
50
|
+
* this — the cache is invalidated automatically by manifest hashes.
|
|
51
|
+
*/
|
|
52
|
+
export declare function clearDispatchCache(): void;
|
package/dist/lang/profiles.d.ts
CHANGED
|
@@ -27,9 +27,29 @@ export interface LanguageProfile {
|
|
|
27
27
|
displayName: string;
|
|
28
28
|
tier: 1 | 2 | 3;
|
|
29
29
|
extensions: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Reserved for future "parser-only" entries (e.g. css, bash, ini, regex,
|
|
32
|
+
* and the tsx parser-grammar split) that should register a tree-sitter
|
|
33
|
+
* parser but never participate in test/build/lint dispatch. Currently
|
|
34
|
+
* unused — populated in a later phase.
|
|
35
|
+
*/
|
|
36
|
+
parserOnly?: boolean;
|
|
30
37
|
treeSitter: {
|
|
31
38
|
grammarId: string;
|
|
32
39
|
wasmFile: string;
|
|
40
|
+
/**
|
|
41
|
+
* Tree-sitter node names that represent comments for this language.
|
|
42
|
+
* Used by tools that strip comments (e.g. ast-diff, syntax-check).
|
|
43
|
+
*
|
|
44
|
+
* Optional in the type because tests construct ad-hoc profiles for
|
|
45
|
+
* fixtures that don't exercise comment-stripping. Production profiles
|
|
46
|
+
* MUST populate this — enforced by
|
|
47
|
+
* `tests/unit/lang/profile-registry-parity.test.ts` against every
|
|
48
|
+
* profile in `LANGUAGE_REGISTRY`. Source of truth lives here;
|
|
49
|
+
* `src/lang/registry.ts` exposes a parity subset for the parser-only
|
|
50
|
+
* registry.
|
|
51
|
+
*/
|
|
52
|
+
commentNodes?: string[];
|
|
33
53
|
};
|
|
34
54
|
build: {
|
|
35
55
|
detectFiles: string[];
|
|
@@ -62,6 +82,14 @@ export declare class LanguageRegistry {
|
|
|
62
82
|
private profiles;
|
|
63
83
|
private extensionIndex;
|
|
64
84
|
constructor();
|
|
85
|
+
/**
|
|
86
|
+
* Remove a previously registered profile and any extensions it claimed.
|
|
87
|
+
* Primarily used by tests to clean up after registering fixture profiles
|
|
88
|
+
* into the shared singleton — without this, fixture entries leak across
|
|
89
|
+
* test files in Bun's per-file-but-shared-process test runner. No-op if
|
|
90
|
+
* the id is not registered.
|
|
91
|
+
*/
|
|
92
|
+
unregister(id: string): void;
|
|
65
93
|
register(profile: LanguageProfile): void;
|
|
66
94
|
get(id: string): LanguageProfile | undefined;
|
|
67
95
|
getById(id: string): LanguageProfile | undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend registry — maps language id → concrete `LanguageBackend`. Sits
|
|
3
|
+
* alongside `LANGUAGE_REGISTRY` (the data registry) and `languageDefinitions`
|
|
4
|
+
* (the parser registry). When a backend is not registered for a language id,
|
|
5
|
+
* the default backend (registry-driven defaults) is synthesized on demand.
|
|
6
|
+
*
|
|
7
|
+
* Adding a new language with first-class behavior is a single new file
|
|
8
|
+
* under `src/lang/backends/<id>.ts` plus one import line in
|
|
9
|
+
* `src/lang/backends/index.ts`. The new file calls
|
|
10
|
+
* `LANGUAGE_BACKEND_REGISTRY.register(myBackend)` at module load.
|
|
11
|
+
*/
|
|
12
|
+
import type { LanguageBackend } from './backend';
|
|
13
|
+
declare class LanguageBackendRegistry {
|
|
14
|
+
private backends;
|
|
15
|
+
register(backend: LanguageBackend): void;
|
|
16
|
+
/**
|
|
17
|
+
* Get a registered backend by id, or `undefined` if no backend is
|
|
18
|
+
* registered. Callers usually want `getOrDefault` instead.
|
|
19
|
+
*/
|
|
20
|
+
get(id: string): LanguageBackend | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Get the registered backend for `id`, or synthesize a default backend
|
|
23
|
+
* by wrapping the matching `LanguageProfile`. Returns `undefined` only
|
|
24
|
+
* when no profile exists for the id (i.e. unknown language).
|
|
25
|
+
*/
|
|
26
|
+
getOrDefault(id: string): LanguageBackend | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Test-only: remove a registered backend. Mirrors
|
|
29
|
+
* `LANGUAGE_REGISTRY.unregister` for the same singleton-pollution
|
|
30
|
+
* rationale (see comment there).
|
|
31
|
+
*/
|
|
32
|
+
unregister(id: string): void;
|
|
33
|
+
}
|
|
34
|
+
export declare const LANGUAGE_BACKEND_REGISTRY: LanguageBackendRegistry;
|
|
35
|
+
export {};
|
|
@@ -7,6 +7,12 @@ export interface TestImpactResult {
|
|
|
7
7
|
declare function normalizePath(p: string): string;
|
|
8
8
|
declare function isCacheStale(impactMap: Record<string, string[]>, generatedAtMs: number): boolean;
|
|
9
9
|
declare function resolveRelativeImport(fromDir: string, importPath: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Test-only: clear the go-module memoization cache. Production code
|
|
12
|
+
* should never need this — the cache is per-call-graph scoped, but tests
|
|
13
|
+
* that reuse the same tempDir benefit from a fresh start.
|
|
14
|
+
*/
|
|
15
|
+
declare function _clearGoModuleCache(): void;
|
|
10
16
|
declare function findTestFilesSync(cwd: string): string[];
|
|
11
17
|
declare function extractImports(content: string): string[];
|
|
12
18
|
declare function buildImpactMapInternal(cwd: string): Promise<Record<string, string[]>>;
|
|
@@ -21,6 +27,7 @@ export declare const _internals: {
|
|
|
21
27
|
loadImpactMap: typeof loadImpactMap;
|
|
22
28
|
saveImpactMap: typeof saveImpactMap;
|
|
23
29
|
analyzeImpact: typeof analyzeImpact;
|
|
30
|
+
_clearGoModuleCache: typeof _clearGoModuleCache;
|
|
24
31
|
};
|
|
25
32
|
export declare function buildImpactMap(cwd: string): Promise<Record<string, string[]>>;
|
|
26
33
|
export declare function loadImpactMap(cwd: string): Promise<Record<string, string[]>>;
|
|
@@ -49,6 +49,28 @@ export interface TestErrorResult {
|
|
|
49
49
|
attempted_scope?: 'graph';
|
|
50
50
|
}
|
|
51
51
|
export type TestResult = TestSuccessResult | TestErrorResult;
|
|
52
|
+
export declare function detectTestFrameworkViaDispatch(cwd: string): Promise<TestFramework>;
|
|
53
|
+
/**
|
|
54
|
+
* Build a test command via the LanguageBackend dispatch path. Reverse-maps
|
|
55
|
+
* the union TestFramework string back to the profile name and asks the
|
|
56
|
+
* matching backend to produce a command. Falls back to the legacy switch
|
|
57
|
+
* (via `defaultBuildTestCommand` import) when no backend is registered or
|
|
58
|
+
* the backend has no `buildTestCommand` hook.
|
|
59
|
+
*
|
|
60
|
+
* Returns null on framework=`none` or when dispatch fails — callers (the
|
|
61
|
+
* test-runner) then surface "no test command available".
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildTestCommandViaDispatch(framework: TestFramework, scope: 'all' | 'convention' | 'graph' | 'impact', files: string[], coverage: boolean, baseDir: string): Promise<string[] | null>;
|
|
64
|
+
/**
|
|
65
|
+
* Parse test output via the LanguageBackend dispatch path. Calls
|
|
66
|
+
* `backend.parseTestOutput` for the directory's resolved backend and
|
|
67
|
+
* returns the legacy-shaped `{ totals, coveragePercent? }` for the
|
|
68
|
+
* test-runner. Returns null when dispatch fails.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseTestOutputViaDispatch(framework: TestFramework, output: string, baseDir: string): Promise<{
|
|
71
|
+
totals: TestTotals;
|
|
72
|
+
coveragePercent?: number;
|
|
73
|
+
} | null>;
|
|
52
74
|
export declare function detectTestFramework(cwd: string): Promise<TestFramework>;
|
|
53
75
|
/**
|
|
54
76
|
* Returns true when `basename` matches a language-specific test file naming
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.17.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",
|