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.
@@ -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.16.0';
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
  [![swarmdo](swarmdo/assets/brand/logo-full.svg)](https://swarmdo.com)
4
4
 
5
- [![npm version (swarmdo)](https://img.shields.io/badge/npx%20swarmdo-v1.16.0-cb3837?style=for-the-badge&logo=npm&logoColor=white)](https://github.com/SwarmDo/swarmdo/releases)
5
+ [![npm version (swarmdo)](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
  [![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=for-the-badge)](https://github.com/SwarmDo/swarmdo/blob/main/LICENSE)
7
7
  [![Website](https://img.shields.io/badge/swarmdo.com-e2a33c?style=for-the-badge&logoColor=black)](https://swarmdo.com)
8
8
  [![Star on GitHub](https://img.shields.io/github/stars/SwarmDo/swarmdo?style=for-the-badge&logo=github&color=gold)](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.16.0",
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.16.0",
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",