swarmdo 1.16.0 → 1.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/.claude/helpers/statusline.cjs +1 -1
- package/README.md +2 -1
- package/package.json +1 -1
- package/v3/@swarmdo/cli/dist/src/affected/affected.d.ts +36 -0
- package/v3/@swarmdo/cli/dist/src/affected/affected.js +78 -0
- package/v3/@swarmdo/cli/dist/src/commands/affected.d.ts +17 -0
- package/v3/@swarmdo/cli/dist/src/commands/affected.js +100 -0
- package/v3/@swarmdo/cli/dist/src/commands/index.js +6 -3
- package/v3/@swarmdo/cli/dist/src/mcp-client.js +2 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/affected-tools.d.ts +12 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/affected-tools.js +36 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/index.d.ts +1 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/index.js +1 -0
- package/v3/@swarmdo/cli/dist/src/mcp-tools/profiles.js +1 -0
- package/v3/@swarmdo/cli/package.json +1 -1
|
@@ -515,7 +515,7 @@ function getCostFromStdin() {
|
|
|
515
515
|
// misses, the displayed version is meaningful (matches what the user
|
|
516
516
|
// installed), not a stale hard-coded string.
|
|
517
517
|
function getPkgVersion() {
|
|
518
|
-
let ver = '1.
|
|
518
|
+
let ver = '1.17.0';
|
|
519
519
|
try {
|
|
520
520
|
const home = os.homedir();
|
|
521
521
|
const pkgPaths = [
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://swarmdo.com)
|
|
4
4
|
|
|
5
|
-
[](https://img.shields.io/badge/npx%20swarmdo-v1.17.0-cb3837?style=for-the-badge&logo=npm&logoColor=white)](https://github.com/SwarmDo/swarmdo/releases)
|
|
6
6
|
[](https://github.com/SwarmDo/swarmdo/blob/main/LICENSE)
|
|
7
7
|
[](https://swarmdo.com)
|
|
8
8
|
[](https://github.com/SwarmDo/swarmdo)
|
|
@@ -285,6 +285,7 @@ The recent release train added a full day-to-day operations layer around the swa
|
|
|
285
285
|
| `swarmdo sbom` | **Software Bill of Materials from the lockfile** — emit a CycloneDX (default) or SPDX JSON manifest of every dependency with version, purl, license, and integrity hash, for compliance/vuln tooling. `--spec spdx`, `-o sbom.json`, `--production`. Completes the env/license/sbom supply-chain trio. Deterministic |
|
|
286
286
|
| `swarmdo apply` | **A forgiving `git apply`** — apply a unified diff with fuzzy context matching, so an agent's patch lands even when line numbers have drifted or a context line is slightly off, and reports exactly which hunks couldn't. `--dry-run` to preview, `--fuzz N` to tolerate more drift. Also an MCP tool (`apply_patch`). Deterministic |
|
|
287
287
|
| `swarmdo hotspots` | **Change-risk hotspots from git history** — rank files by churn × recency × author-spread to surface the technical debt worth refactoring or testing, answered from data instead of a guess. `--since 90d`, `--by risk\|churn\|commits\|authors`, `--top N`, `--format json`. Pairs with `codegraph`; also an MCP tool (`hotspots`). Deterministic |
|
|
288
|
+
| `swarmdo affected` | **Run only the tests your change touches** — from a git diff, walk `codegraph`'s import graph to list every file (and test file) a change could break (reverse-dependency closure, nx/turbo/`jest --findRelatedTests` style). `--base main`, `--tests` (pipeable list), `--format json`. Also an MCP tool (`affected`). Deterministic |
|
|
288
289
|
| `swarmdo integrations` (alias `integrate`) | **Use swarmdo from Codex CLI, GitHub Copilot CLI, and pi** — one command wires AGENTS.md + each CLI's MCP config (idempotent, dry-run first, never touches your Claude Code setup) |
|
|
289
290
|
| OpenRouter model pool | **Let swarms pick from any models you configure** — declare tier-mapped OpenRouter models in `swarmdo.config.json`; the router Thompson-samples among them per task and the execution layer dispatches the winner |
|
|
290
291
|
| `swarmdo changelog` (alias `notes`) | **Release notes from conventional commits** — `--out NOTES.md` feeds `gh release create --notes-file` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swarmdo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "Swarmdo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* affected.ts — given changed files, compute the transitive set of files that
|
|
3
|
+
* (directly or indirectly) import them, and the minimal set of TEST files worth
|
|
4
|
+
* running. The deterministic core behind "only run what my change could break",
|
|
5
|
+
* à la `nx affected` / `jest --findRelatedTests` / `turbo --filter`.
|
|
6
|
+
*
|
|
7
|
+
* Composes codegraph's import graph: a change to X affects X, everything that
|
|
8
|
+
* imports X, everything that imports those, and so on (reverse-dependency
|
|
9
|
+
* closure). Pure — CodeIndex + changed paths in, affected/tests out; the git
|
|
10
|
+
* diff and index load live in ../commands/affected.ts, so it's fixture-testable.
|
|
11
|
+
*/
|
|
12
|
+
import type { CodeIndex } from '../codegraph/codegraph.js';
|
|
13
|
+
/** Default matcher for test files (Jest/Vitest/Mocha conventions). */
|
|
14
|
+
export declare function isTestFile(path: string): boolean;
|
|
15
|
+
/** file it resolves to → files that import it (reverse of the import edges). Pure. */
|
|
16
|
+
export declare function reverseDeps(index: CodeIndex): Map<string, string[]>;
|
|
17
|
+
export interface AffectedOptions {
|
|
18
|
+
/** custom test-file matcher (default isTestFile) */
|
|
19
|
+
isTest?: (path: string) => boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface AffectedResult {
|
|
22
|
+
/** every file transitively impacted, including the changed files (sorted) */
|
|
23
|
+
affected: string[];
|
|
24
|
+
/** the subset of `affected` that are test files (sorted) */
|
|
25
|
+
tests: string[];
|
|
26
|
+
/** changed files that the index knows nothing about (sorted) — may under-report */
|
|
27
|
+
unknown: string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reverse-dependency closure from `changed` up the import graph. Deterministic:
|
|
31
|
+
* BFS over reverse edges, output sorted. A changed file not present anywhere in
|
|
32
|
+
* the index (never imported, declares nothing) is still reported as affected
|
|
33
|
+
* (it changed) but flagged in `unknown` since we can't see its dependents.
|
|
34
|
+
*/
|
|
35
|
+
export declare function computeAffected(changed: string[], index: CodeIndex, opts?: AffectedOptions): AffectedResult;
|
|
36
|
+
//# sourceMappingURL=affected.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* affected.ts — given changed files, compute the transitive set of files that
|
|
3
|
+
* (directly or indirectly) import them, and the minimal set of TEST files worth
|
|
4
|
+
* running. The deterministic core behind "only run what my change could break",
|
|
5
|
+
* à la `nx affected` / `jest --findRelatedTests` / `turbo --filter`.
|
|
6
|
+
*
|
|
7
|
+
* Composes codegraph's import graph: a change to X affects X, everything that
|
|
8
|
+
* imports X, everything that imports those, and so on (reverse-dependency
|
|
9
|
+
* closure). Pure — CodeIndex + changed paths in, affected/tests out; the git
|
|
10
|
+
* diff and index load live in ../commands/affected.ts, so it's fixture-testable.
|
|
11
|
+
*/
|
|
12
|
+
/** Default matcher for test files (Jest/Vitest/Mocha conventions). */
|
|
13
|
+
export function isTestFile(path) {
|
|
14
|
+
return /(^|\/)__tests__\//.test(path) || /\.(test|spec)\.[cm]?[jt]sx?$/.test(path);
|
|
15
|
+
}
|
|
16
|
+
/** file it resolves to → files that import it (reverse of the import edges). Pure. */
|
|
17
|
+
export function reverseDeps(index) {
|
|
18
|
+
const rev = new Map();
|
|
19
|
+
for (const e of index.imports) {
|
|
20
|
+
if (!e.resolved)
|
|
21
|
+
continue; // external/unresolved — no internal reverse edge
|
|
22
|
+
let importers = rev.get(e.resolved);
|
|
23
|
+
if (!importers) {
|
|
24
|
+
importers = [];
|
|
25
|
+
rev.set(e.resolved, importers);
|
|
26
|
+
}
|
|
27
|
+
if (!importers.includes(e.from))
|
|
28
|
+
importers.push(e.from);
|
|
29
|
+
}
|
|
30
|
+
return rev;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reverse-dependency closure from `changed` up the import graph. Deterministic:
|
|
34
|
+
* BFS over reverse edges, output sorted. A changed file not present anywhere in
|
|
35
|
+
* the index (never imported, declares nothing) is still reported as affected
|
|
36
|
+
* (it changed) but flagged in `unknown` since we can't see its dependents.
|
|
37
|
+
*/
|
|
38
|
+
export function computeAffected(changed, index, opts = {}) {
|
|
39
|
+
const isTest = opts.isTest ?? isTestFile;
|
|
40
|
+
const rev = reverseDeps(index);
|
|
41
|
+
// Files the index actually knows about (declares symbols or participates in imports).
|
|
42
|
+
const known = new Set();
|
|
43
|
+
for (const s of index.symbols)
|
|
44
|
+
known.add(s.file);
|
|
45
|
+
for (const e of index.imports) {
|
|
46
|
+
known.add(e.from);
|
|
47
|
+
if (e.resolved)
|
|
48
|
+
known.add(e.resolved);
|
|
49
|
+
}
|
|
50
|
+
const affected = new Set();
|
|
51
|
+
const queue = [];
|
|
52
|
+
for (const c of changed) {
|
|
53
|
+
if (!affected.has(c)) {
|
|
54
|
+
affected.add(c);
|
|
55
|
+
queue.push(c);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// BFS: for each affected file, add everyone who imports it.
|
|
59
|
+
while (queue.length > 0) {
|
|
60
|
+
const file = queue.shift();
|
|
61
|
+
const importers = rev.get(file);
|
|
62
|
+
if (!importers)
|
|
63
|
+
continue;
|
|
64
|
+
for (const imp of importers) {
|
|
65
|
+
if (!affected.has(imp)) {
|
|
66
|
+
affected.add(imp);
|
|
67
|
+
queue.push(imp);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const affectedList = [...affected].sort();
|
|
72
|
+
return {
|
|
73
|
+
affected: affectedList,
|
|
74
|
+
tests: affectedList.filter(isTest),
|
|
75
|
+
unknown: changed.filter((c) => !known.has(c)).sort(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=affected.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `swarmdo affected` — from a git diff, list the files (and the test files)
|
|
3
|
+
* that a change could break, by walking codegraph's import graph. The "only run
|
|
4
|
+
* what my change touches" query, à la `nx affected` / `jest --findRelatedTests`.
|
|
5
|
+
*
|
|
6
|
+
* swarmdo affected # vs working tree (staged+unstaged)
|
|
7
|
+
* swarmdo affected --base main # vs a ref
|
|
8
|
+
* swarmdo affected --tests # just the test files to run
|
|
9
|
+
* swarmdo affected --tests --format json # feed a test runner
|
|
10
|
+
*
|
|
11
|
+
* Engine (../affected/affected.ts) is pure + tested; this captures the diff and
|
|
12
|
+
* loads (or builds) the index. Composes `codegraph`.
|
|
13
|
+
*/
|
|
14
|
+
import type { Command } from '../types.js';
|
|
15
|
+
export declare const affectedCommand: Command;
|
|
16
|
+
export default affectedCommand;
|
|
17
|
+
//# sourceMappingURL=affected.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `swarmdo affected` — from a git diff, list the files (and the test files)
|
|
3
|
+
* that a change could break, by walking codegraph's import graph. The "only run
|
|
4
|
+
* what my change touches" query, à la `nx affected` / `jest --findRelatedTests`.
|
|
5
|
+
*
|
|
6
|
+
* swarmdo affected # vs working tree (staged+unstaged)
|
|
7
|
+
* swarmdo affected --base main # vs a ref
|
|
8
|
+
* swarmdo affected --tests # just the test files to run
|
|
9
|
+
* swarmdo affected --tests --format json # feed a test runner
|
|
10
|
+
*
|
|
11
|
+
* Engine (../affected/affected.ts) is pure + tested; this captures the diff and
|
|
12
|
+
* loads (or builds) the index. Composes `codegraph`.
|
|
13
|
+
*/
|
|
14
|
+
import { execFileSync } from 'node:child_process';
|
|
15
|
+
import { output } from '../output.js';
|
|
16
|
+
import { computeAffected } from '../affected/affected.js';
|
|
17
|
+
import { loadIndex, scanRepo, saveIndex } from '../codegraph/store.js';
|
|
18
|
+
/** Collect changed repo-relative files: vs a base ref, or the working tree. */
|
|
19
|
+
function changedFiles(root, base) {
|
|
20
|
+
const run = (args) => {
|
|
21
|
+
try {
|
|
22
|
+
return execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
let out;
|
|
29
|
+
if (base) {
|
|
30
|
+
// Everything that differs from the base ref (three-dot: since the merge-base).
|
|
31
|
+
out = run(['diff', '--name-only', `${base}...`]);
|
|
32
|
+
if (!out.trim())
|
|
33
|
+
out = run(['diff', '--name-only', base]); // fallback: two-dot
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Working tree: staged + unstaged + untracked.
|
|
37
|
+
out = run(['diff', '--name-only', 'HEAD']);
|
|
38
|
+
out += run(['ls-files', '--others', '--exclude-standard']);
|
|
39
|
+
}
|
|
40
|
+
return [...new Set(out.split('\n').map((l) => l.trim()).filter(Boolean))]
|
|
41
|
+
// Never count swarmdo's own state dir (incl. the codegraph.json we may write).
|
|
42
|
+
.filter((f) => f !== '.swarm' && !f.startsWith('.swarm/'))
|
|
43
|
+
.sort();
|
|
44
|
+
}
|
|
45
|
+
async function run(ctx) {
|
|
46
|
+
const root = ctx.cwd || process.cwd();
|
|
47
|
+
const base = typeof ctx.flags.base === 'string' ? ctx.flags.base : undefined;
|
|
48
|
+
const testsOnly = ctx.flags.tests === true;
|
|
49
|
+
const asJson = ctx.flags.format === 'json';
|
|
50
|
+
const changed = changedFiles(root, base);
|
|
51
|
+
if (changed.length === 0) {
|
|
52
|
+
if (asJson)
|
|
53
|
+
process.stdout.write(JSON.stringify({ changed: [], affected: [], tests: [], unknown: [] }, null, 2) + '\n');
|
|
54
|
+
else
|
|
55
|
+
output.writeln(output.dim(base ? `no changes vs ${base}` : 'no working-tree changes'));
|
|
56
|
+
return { success: true, exitCode: 0 };
|
|
57
|
+
}
|
|
58
|
+
// Prefer a saved codegraph index; build a fresh one if absent.
|
|
59
|
+
let index = loadIndex(root);
|
|
60
|
+
if (!index) {
|
|
61
|
+
index = scanRepo(root);
|
|
62
|
+
try {
|
|
63
|
+
saveIndex(root, index);
|
|
64
|
+
}
|
|
65
|
+
catch { /* read-only fs — fine, we have it in memory */ }
|
|
66
|
+
}
|
|
67
|
+
const result = computeAffected(changed, index);
|
|
68
|
+
const list = testsOnly ? result.tests : result.affected;
|
|
69
|
+
if (asJson) {
|
|
70
|
+
process.stdout.write(JSON.stringify({ changed, ...result }, null, 2) + '\n');
|
|
71
|
+
}
|
|
72
|
+
else if (testsOnly) {
|
|
73
|
+
// Plain list on stdout so it pipes straight into a test runner.
|
|
74
|
+
if (list.length)
|
|
75
|
+
process.stdout.write(list.join('\n') + '\n');
|
|
76
|
+
process.stderr.write(output.dim(`${result.tests.length} affected test file(s) from ${changed.length} changed\n`));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
output.writeln(output.bold(`Affected by ${changed.length} changed file(s)${base ? ` vs ${base}` : ''}:`));
|
|
80
|
+
for (const f of list)
|
|
81
|
+
output.writeln(` ${f}`);
|
|
82
|
+
output.writeln(output.dim(`${result.affected.length} affected · ${result.tests.length} test file(s)${result.unknown.length ? ` · ${result.unknown.length} not in index` : ''}`));
|
|
83
|
+
}
|
|
84
|
+
return { success: true, exitCode: 0 };
|
|
85
|
+
}
|
|
86
|
+
export const affectedCommand = {
|
|
87
|
+
name: 'affected',
|
|
88
|
+
description: 'List files (and test files) a change could break, via the import graph — run only the tests your diff impacts (nx/turbo-style)',
|
|
89
|
+
options: [
|
|
90
|
+
{ name: 'base', description: 'diff against a git ref (e.g. main); default is the working tree', type: 'string' },
|
|
91
|
+
{ name: 'tests', description: 'output only the affected test files (one per line, for a test runner)', type: 'boolean' },
|
|
92
|
+
],
|
|
93
|
+
examples: [
|
|
94
|
+
{ command: 'swarmdo affected --base main', description: 'Files impacted since branching from main' },
|
|
95
|
+
{ command: 'swarmdo affected --tests | xargs vitest run', description: 'Run only the tests your change affects' },
|
|
96
|
+
],
|
|
97
|
+
action: run,
|
|
98
|
+
};
|
|
99
|
+
export default affectedCommand;
|
|
100
|
+
//# sourceMappingURL=affected.js.map
|
|
@@ -44,6 +44,9 @@ const commandLoaders = {
|
|
|
44
44
|
// Change-risk hotspots from git history (code-maat demand) — rank files by
|
|
45
45
|
// churn × recency × author-spread; "where is the tech debt?" from data.
|
|
46
46
|
hotspots: () => import('./hotspots.js'),
|
|
47
|
+
// Affected-file/test set from a git diff (nx/turbo/jest --findRelatedTests
|
|
48
|
+
// demand) — reverse-dependency closure over codegraph's import graph.
|
|
49
|
+
affected: () => import('./affected.js'),
|
|
47
50
|
// Queryable exported-symbol index (codegraph demand) — where things are
|
|
48
51
|
// defined without grep+read round-trips.
|
|
49
52
|
codegraph: () => import('./codegraph.js'),
|
|
@@ -277,7 +280,7 @@ export async function getCommandsByCategory() {
|
|
|
277
280
|
// three slots from 'statusline' onward (completionsCmd received the
|
|
278
281
|
// statusline module, analyzeCmd received completions, …), scrambling the
|
|
279
282
|
// categorized help. Every load now has a named slot.
|
|
280
|
-
const [daemonCmd, doctorCmd, embeddingsCmd, neuralCmd, performanceCmd, securityCmd, swarmvectorCmd, hiveMindCmd, configCmd, statuslineCmd, compressCmd, efficiencyCmd, completionsCmd, migrateCmd, workflowCmd, analyzeCmd, routeCmd, progressCmd, providersCmd, pluginsCmd, deploymentCmd, claimsCmd, issuesCmd, updateCmd, processCmd, guidanceCmd, applianceCmd, cleanupCmd, autopilotCmd, demoCmd, usageCmd, repairCmd, hudCmd, compactCmd, codegraphCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd,] = await Promise.all([
|
|
283
|
+
const [daemonCmd, doctorCmd, embeddingsCmd, neuralCmd, performanceCmd, securityCmd, swarmvectorCmd, hiveMindCmd, configCmd, statuslineCmd, compressCmd, efficiencyCmd, completionsCmd, migrateCmd, workflowCmd, analyzeCmd, routeCmd, progressCmd, providersCmd, pluginsCmd, deploymentCmd, claimsCmd, issuesCmd, updateCmd, processCmd, guidanceCmd, applianceCmd, cleanupCmd, autopilotCmd, demoCmd, usageCmd, repairCmd, hudCmd, compactCmd, codegraphCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, affectedCmd,] = await Promise.all([
|
|
281
284
|
loadCommand('daemon'), loadCommand('doctor'), loadCommand('embeddings'), loadCommand('neural'),
|
|
282
285
|
loadCommand('performance'), loadCommand('security'), loadCommand('swarmvector'), loadCommand('hive-mind'),
|
|
283
286
|
loadCommand('config'), loadCommand('statusline'), loadCommand('compress'), loadCommand('efficiency'),
|
|
@@ -285,7 +288,7 @@ export async function getCommandsByCategory() {
|
|
|
285
288
|
loadCommand('analyze'), loadCommand('route'), loadCommand('progress'), loadCommand('providers'),
|
|
286
289
|
loadCommand('plugins'), loadCommand('deployment'), loadCommand('claims'), loadCommand('issues'),
|
|
287
290
|
loadCommand('update'), loadCommand('process'), loadCommand('guidance'), loadCommand('appliance'),
|
|
288
|
-
loadCommand('cleanup'), loadCommand('autopilot'), loadCommand('demo'), loadCommand('usage'), loadCommand('repair'), loadCommand('hud'), loadCommand('compact'), loadCommand('codegraph'), loadCommand('redact'), loadCommand('pack'), loadCommand('env'), loadCommand('license'), loadCommand('sbom'), loadCommand('apply'), loadCommand('hotspots'),
|
|
291
|
+
loadCommand('cleanup'), loadCommand('autopilot'), loadCommand('demo'), loadCommand('usage'), loadCommand('repair'), loadCommand('hud'), loadCommand('compact'), loadCommand('codegraph'), loadCommand('redact'), loadCommand('pack'), loadCommand('env'), loadCommand('license'), loadCommand('sbom'), loadCommand('apply'), loadCommand('hotspots'), loadCommand('affected'),
|
|
289
292
|
]);
|
|
290
293
|
return {
|
|
291
294
|
primary: [
|
|
@@ -301,7 +304,7 @@ export async function getCommandsByCategory() {
|
|
|
301
304
|
utility: [
|
|
302
305
|
configCmd, doctorCmd, daemonCmd, completionsCmd,
|
|
303
306
|
migrateCmd, workflowCmd, demoCmd,
|
|
304
|
-
statuslineCmd, compressCmd, compactCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, efficiencyCmd,
|
|
307
|
+
statuslineCmd, compressCmd, compactCmd, redactCmd, packCmd, envCmd, licenseCmd, sbomCmd, applyCmd, hotspotsCmd, affectedCmd, efficiencyCmd,
|
|
305
308
|
].filter(Boolean),
|
|
306
309
|
analysis: [
|
|
307
310
|
analyzeCmd, routeCmd, progressCmd, usageCmd, hudCmd, codegraphCmd,
|
|
@@ -25,6 +25,7 @@ import { envTools } from './mcp-tools/env-tools.js';
|
|
|
25
25
|
import { licenseTools } from './mcp-tools/license-tools.js';
|
|
26
26
|
import { applyTools } from './mcp-tools/apply-tools.js';
|
|
27
27
|
import { hotspotsTools } from './mcp-tools/hotspots-tools.js';
|
|
28
|
+
import { affectedTools } from './mcp-tools/affected-tools.js';
|
|
28
29
|
import { progressTools } from './mcp-tools/progress-tools.js';
|
|
29
30
|
import { embeddingsTools } from './mcp-tools/embeddings-tools.js';
|
|
30
31
|
import { claimsTools } from './mcp-tools/claims-tools.js';
|
|
@@ -114,6 +115,7 @@ const TOOL_GROUPS = {
|
|
|
114
115
|
license: () => licenseTools,
|
|
115
116
|
apply: () => applyTools,
|
|
116
117
|
hotspots: () => hotspotsTools,
|
|
118
|
+
affected: () => affectedTools,
|
|
117
119
|
progress: () => progressTools,
|
|
118
120
|
embeddings: () => embeddingsTools,
|
|
119
121
|
claims: () => claimsTools,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Affected MCP Tool
|
|
3
|
+
*
|
|
4
|
+
* Given the files an agent just changed, return the transitive set of files —
|
|
5
|
+
* and the test files — that the change could break, by walking codegraph's
|
|
6
|
+
* import graph. Lets an agent run only the relevant tests after an edit instead
|
|
7
|
+
* of the whole suite. Shares the pure engine in ../affected/affected.ts; loads
|
|
8
|
+
* (or builds) the codegraph index for the repo.
|
|
9
|
+
*/
|
|
10
|
+
import type { MCPTool } from './types.js';
|
|
11
|
+
export declare const affectedTools: MCPTool[];
|
|
12
|
+
//# sourceMappingURL=affected-tools.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Affected MCP Tool
|
|
3
|
+
*
|
|
4
|
+
* Given the files an agent just changed, return the transitive set of files —
|
|
5
|
+
* and the test files — that the change could break, by walking codegraph's
|
|
6
|
+
* import graph. Lets an agent run only the relevant tests after an edit instead
|
|
7
|
+
* of the whole suite. Shares the pure engine in ../affected/affected.ts; loads
|
|
8
|
+
* (or builds) the codegraph index for the repo.
|
|
9
|
+
*/
|
|
10
|
+
import { computeAffected } from '../affected/affected.js';
|
|
11
|
+
import { loadIndex, scanRepo } from '../codegraph/store.js';
|
|
12
|
+
const affectedTool = {
|
|
13
|
+
name: 'affected',
|
|
14
|
+
description: 'Given the repo-relative files you just changed, return the transitive set of files (and the test files) that the change could break, via the import graph. Call this after editing to run only the relevant tests instead of the whole suite. Composes codegraph.',
|
|
15
|
+
category: 'affected',
|
|
16
|
+
tags: ['tests', 'impact', 'import-graph', 'diff'],
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
changed: { type: 'array', items: { type: 'string' }, description: 'repo-relative paths you changed' },
|
|
21
|
+
path: { type: 'string', description: 'repo root to resolve the import graph in (default cwd)' },
|
|
22
|
+
},
|
|
23
|
+
required: ['changed'],
|
|
24
|
+
},
|
|
25
|
+
handler: async (params) => {
|
|
26
|
+
if (!Array.isArray(params.changed) || params.changed.some((c) => typeof c !== 'string')) {
|
|
27
|
+
return { error: true, message: 'changed (string[]) is required' };
|
|
28
|
+
}
|
|
29
|
+
const root = typeof params.path === 'string' ? params.path : process.cwd();
|
|
30
|
+
const index = loadIndex(root) ?? scanRepo(root);
|
|
31
|
+
const result = computeAffected(params.changed, index);
|
|
32
|
+
return { changed: params.changed, ...result };
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
export const affectedTools = [affectedTool];
|
|
36
|
+
//# sourceMappingURL=affected-tools.js.map
|
|
@@ -21,6 +21,7 @@ export { envTools } from './env-tools.js';
|
|
|
21
21
|
export { licenseTools } from './license-tools.js';
|
|
22
22
|
export { applyTools } from './apply-tools.js';
|
|
23
23
|
export { hotspotsTools } from './hotspots-tools.js';
|
|
24
|
+
export { affectedTools } from './affected-tools.js';
|
|
24
25
|
export { progressTools } from './progress-tools.js';
|
|
25
26
|
export { transferTools } from './transfer-tools.js';
|
|
26
27
|
export { securityTools } from './security-tools.js';
|
|
@@ -20,6 +20,7 @@ export { envTools } from './env-tools.js';
|
|
|
20
20
|
export { licenseTools } from './license-tools.js';
|
|
21
21
|
export { applyTools } from './apply-tools.js';
|
|
22
22
|
export { hotspotsTools } from './hotspots-tools.js';
|
|
23
|
+
export { affectedTools } from './affected-tools.js';
|
|
23
24
|
export { progressTools } from './progress-tools.js';
|
|
24
25
|
export { transferTools } from './transfer-tools.js';
|
|
25
26
|
export { securityTools } from './security-tools.js';
|
|
@@ -27,6 +27,7 @@ const LEAN_GROUPS = [
|
|
|
27
27
|
'env', // env_check — env-var drift (missing/unused/undocumented)
|
|
28
28
|
'apply', // apply_patch — fuzzy unified-diff applier for agent edits
|
|
29
29
|
'hotspots', // hotspots — git-history change-risk ranking ("where's the debt?")
|
|
30
|
+
'affected', // affected — impacted files/tests from a change via the import graph
|
|
30
31
|
];
|
|
31
32
|
/** Balanced = lean + orchestration/session/system the average user reaches for. */
|
|
32
33
|
const BALANCED_EXTRA = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmdo/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Swarmdo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|