opencode-swarm 7.85.0 → 7.86.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/dist/cli/{guardrail-explain-w4txg349.js → guardrail-explain-rtd1x26f.js} +2 -2
- package/dist/cli/{index-yhqt45de.js → index-5q66xc88.js} +4 -4
- package/dist/cli/{index-x7qck34v.js → index-7r2b453y.js} +1 -1
- package/dist/cli/{index-qqabjns2.js → index-jwz50183.js} +2 -2
- package/dist/cli/index.js +1 -1
- package/dist/index.js +274 -16
- package/dist/tools/repo-graph/builder.d.ts +46 -0
- package/dist/tools/repo-graph/query.d.ts +26 -1
- package/dist/tools/repo-graph/types.d.ts +73 -1
- package/dist/tools/repo-graph.d.ts +4 -3
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
5
|
-
import"./index-
|
|
4
|
+
} from "./index-7r2b453y.js";
|
|
5
|
+
import"./index-5q66xc88.js";
|
|
6
6
|
import"./index-vq2321gg.js";
|
|
7
7
|
import"./index-yhsmmv2z.js";
|
|
8
8
|
import"./index-d9fbxaqd.js";
|
|
@@ -879,7 +879,7 @@ var init_executor = __esm(() => {
|
|
|
879
879
|
// package.json
|
|
880
880
|
var package_default = {
|
|
881
881
|
name: "opencode-swarm",
|
|
882
|
-
version: "7.
|
|
882
|
+
version: "7.86.0",
|
|
883
883
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
884
884
|
main: "dist/index.js",
|
|
885
885
|
types: "dist/index.d.ts",
|
|
@@ -27978,7 +27978,7 @@ function buildDetailedHelp(commandName, entry) {
|
|
|
27978
27978
|
async function handleHelpCommand(ctx) {
|
|
27979
27979
|
const targetCommand = ctx.args.join(" ");
|
|
27980
27980
|
if (!targetCommand) {
|
|
27981
|
-
const { buildHelpText } = await import("./index-
|
|
27981
|
+
const { buildHelpText } = await import("./index-jwz50183.js");
|
|
27982
27982
|
return buildHelpText();
|
|
27983
27983
|
}
|
|
27984
27984
|
const tokens = targetCommand.split(/\s+/);
|
|
@@ -27987,7 +27987,7 @@ async function handleHelpCommand(ctx) {
|
|
|
27987
27987
|
return _internals41.buildDetailedHelp(resolved.key, resolved.entry);
|
|
27988
27988
|
}
|
|
27989
27989
|
const similar = _internals41.findSimilarCommands(targetCommand);
|
|
27990
|
-
const { buildHelpText: fullHelp } = await import("./index-
|
|
27990
|
+
const { buildHelpText: fullHelp } = await import("./index-jwz50183.js");
|
|
27991
27991
|
if (similar.length > 0) {
|
|
27992
27992
|
return `Command '/swarm ${targetCommand}' not found.
|
|
27993
27993
|
|
|
@@ -28120,7 +28120,7 @@ var COMMAND_REGISTRY = {
|
|
|
28120
28120
|
},
|
|
28121
28121
|
"guardrail explain": {
|
|
28122
28122
|
handler: async (ctx) => {
|
|
28123
|
-
const { handleGuardrailExplain } = await import("./guardrail-explain-
|
|
28123
|
+
const { handleGuardrailExplain } = await import("./guardrail-explain-rtd1x26f.js");
|
|
28124
28124
|
return handleGuardrailExplain(ctx.directory, ctx.args);
|
|
28125
28125
|
},
|
|
28126
28126
|
description: "Dry-run: show what the guardrails would do to a command or write target (executes nothing)",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
handleGuardrailExplain
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-7r2b453y.js";
|
|
5
5
|
import {
|
|
6
6
|
handleGuardrailLog
|
|
7
7
|
} from "./index-5cb86007.js";
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
handleWriteRetroCommand,
|
|
77
77
|
normalizeSwarmCommandInput,
|
|
78
78
|
resolveCommand
|
|
79
|
-
} from "./index-
|
|
79
|
+
} from "./index-5q66xc88.js";
|
|
80
80
|
import"./index-vq2321gg.js";
|
|
81
81
|
import"./index-yhsmmv2z.js";
|
|
82
82
|
import"./index-d9fbxaqd.js";
|
package/dist/cli/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var package_default;
|
|
|
69
69
|
var init_package = __esm(() => {
|
|
70
70
|
package_default = {
|
|
71
71
|
name: "opencode-swarm",
|
|
72
|
-
version: "7.
|
|
72
|
+
version: "7.86.0",
|
|
73
73
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
74
74
|
main: "dist/index.js",
|
|
75
75
|
types: "dist/index.d.ts",
|
|
@@ -45068,7 +45068,7 @@ function recordSessionStart(directory, startMs) {
|
|
|
45068
45068
|
const filePath = path32.join(sessionDir, SESSION_START_FILE);
|
|
45069
45069
|
const line = JSON.stringify({ startMs, ts: Date.now() });
|
|
45070
45070
|
mkdirSync11(sessionDir, { recursive: true });
|
|
45071
|
-
writeFileSync6(filePath, line
|
|
45071
|
+
writeFileSync6(filePath, `${line}
|
|
45072
45072
|
`, { flag: "a" });
|
|
45073
45073
|
} catch {}
|
|
45074
45074
|
}
|
|
@@ -113317,7 +113317,25 @@ function safeRealpathSync(targetPath, fallback, realpathResolver = realpathSync1
|
|
|
113317
113317
|
// src/tools/repo-graph/types.ts
|
|
113318
113318
|
import * as path117 from "node:path";
|
|
113319
113319
|
var REPO_GRAPH_FILENAME = "repo-graph.json";
|
|
113320
|
-
var GRAPH_SCHEMA_VERSION = "1.
|
|
113320
|
+
var GRAPH_SCHEMA_VERSION = "1.1.0";
|
|
113321
|
+
function isSchemaVersionAtLeast(version5, minimum) {
|
|
113322
|
+
const parse10 = (v) => v.split(".").map((part) => {
|
|
113323
|
+
const n = Number.parseInt(part, 10);
|
|
113324
|
+
return Number.isFinite(n) ? n : 0;
|
|
113325
|
+
});
|
|
113326
|
+
const a = parse10(version5 ?? "");
|
|
113327
|
+
const b = parse10(minimum);
|
|
113328
|
+
const len = Math.max(a.length, b.length);
|
|
113329
|
+
for (let i2 = 0;i2 < len; i2++) {
|
|
113330
|
+
const av = a[i2] ?? 0;
|
|
113331
|
+
const bv = b[i2] ?? 0;
|
|
113332
|
+
if (av > bv)
|
|
113333
|
+
return true;
|
|
113334
|
+
if (av < bv)
|
|
113335
|
+
return false;
|
|
113336
|
+
}
|
|
113337
|
+
return true;
|
|
113338
|
+
}
|
|
113321
113339
|
var FILE_ROLE_VALUES = [
|
|
113322
113340
|
"api_route",
|
|
113323
113341
|
"middleware",
|
|
@@ -113492,6 +113510,19 @@ function validateGraphNode(node) {
|
|
|
113492
113510
|
throw new Error(`Invalid node: imports contains control characters (file=${node.filePath}, value="${preview}")`);
|
|
113493
113511
|
}
|
|
113494
113512
|
}
|
|
113513
|
+
if (node.exportLines !== undefined) {
|
|
113514
|
+
if (typeof node.exportLines !== "object" || node.exportLines === null || Array.isArray(node.exportLines)) {
|
|
113515
|
+
throw new Error("Invalid node: exportLines must be an object");
|
|
113516
|
+
}
|
|
113517
|
+
for (const [name2, line] of Object.entries(node.exportLines)) {
|
|
113518
|
+
if (containsControlChars(name2)) {
|
|
113519
|
+
throw new Error("Invalid node: exportLines key contains control characters");
|
|
113520
|
+
}
|
|
113521
|
+
if (typeof line !== "number" || !Number.isFinite(line) || line < 0) {
|
|
113522
|
+
throw new Error("Invalid node: exportLines values must be non-negative numbers");
|
|
113523
|
+
}
|
|
113524
|
+
}
|
|
113525
|
+
}
|
|
113495
113526
|
if (node.ontology !== undefined) {
|
|
113496
113527
|
validateOntologyStrings(node);
|
|
113497
113528
|
}
|
|
@@ -113609,6 +113640,19 @@ function validateGraphEdge(edge) {
|
|
|
113609
113640
|
}
|
|
113610
113641
|
}
|
|
113611
113642
|
}
|
|
113643
|
+
if (edge.usedSymbols !== undefined) {
|
|
113644
|
+
if (!Array.isArray(edge.usedSymbols)) {
|
|
113645
|
+
throw new Error("Invalid edge: usedSymbols must be an array");
|
|
113646
|
+
}
|
|
113647
|
+
for (const symbol3 of edge.usedSymbols) {
|
|
113648
|
+
if (typeof symbol3 !== "string") {
|
|
113649
|
+
throw new Error("Invalid edge: usedSymbols must be an array of strings");
|
|
113650
|
+
}
|
|
113651
|
+
if (containsControlChars(symbol3)) {
|
|
113652
|
+
throw new Error("Invalid edge: usedSymbols contains control characters");
|
|
113653
|
+
}
|
|
113654
|
+
}
|
|
113655
|
+
}
|
|
113612
113656
|
}
|
|
113613
113657
|
|
|
113614
113658
|
// src/tools/repo-graph/builder.ts
|
|
@@ -113617,7 +113661,9 @@ var _internals74 = {
|
|
|
113617
113661
|
extractTSSymbols,
|
|
113618
113662
|
extractPythonSymbols,
|
|
113619
113663
|
parseFileImports,
|
|
113620
|
-
extractFileOntology
|
|
113664
|
+
extractFileOntology,
|
|
113665
|
+
stripComments: stripComments2,
|
|
113666
|
+
computeUsedSymbols
|
|
113621
113667
|
};
|
|
113622
113668
|
var SKIP_DIRECTORIES3 = new Set([
|
|
113623
113669
|
"node_modules",
|
|
@@ -113914,11 +113960,89 @@ function parseFileImports(rawContent) {
|
|
|
113914
113960
|
imports.push({
|
|
113915
113961
|
specifier: modulePath,
|
|
113916
113962
|
importType,
|
|
113917
|
-
importedSymbols: parseImportedSymbols(matchedString, importType)
|
|
113963
|
+
importedSymbols: parseImportedSymbols(matchedString, importType),
|
|
113964
|
+
bindings: parseImportBindings(matchedString, importType),
|
|
113965
|
+
reExport: /^\s*export\b/.test(matchedString)
|
|
113918
113966
|
});
|
|
113919
113967
|
}
|
|
113920
113968
|
return imports;
|
|
113921
113969
|
}
|
|
113970
|
+
function parseImportBindings(matchedString, importType) {
|
|
113971
|
+
if (importType === "namespace")
|
|
113972
|
+
return [];
|
|
113973
|
+
if (importType === "default") {
|
|
113974
|
+
const defaultMatch = matchedString.match(/^import\s+(\w+)\s+from\s+['"`]/);
|
|
113975
|
+
return defaultMatch ? [{ imported: "default", local: defaultMatch[1] }] : [];
|
|
113976
|
+
}
|
|
113977
|
+
if (importType !== "named")
|
|
113978
|
+
return [];
|
|
113979
|
+
const braceMatch = matchedString.match(/\{\s*([\s\S]*?)\s*\}/);
|
|
113980
|
+
if (!braceMatch)
|
|
113981
|
+
return [];
|
|
113982
|
+
const bindings = [];
|
|
113983
|
+
const seen = new Set;
|
|
113984
|
+
for (const rawPart of braceMatch[1].split(",")) {
|
|
113985
|
+
const part = rawPart.trim().replace(/^type\s+/, "");
|
|
113986
|
+
if (!part)
|
|
113987
|
+
continue;
|
|
113988
|
+
const aliasSplit = part.split(/\s+as\s+/i);
|
|
113989
|
+
const imported = aliasSplit[0].trim();
|
|
113990
|
+
const local = (aliasSplit[1] ?? aliasSplit[0]).trim();
|
|
113991
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(imported))
|
|
113992
|
+
continue;
|
|
113993
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(local))
|
|
113994
|
+
continue;
|
|
113995
|
+
if (seen.has(imported))
|
|
113996
|
+
continue;
|
|
113997
|
+
seen.add(imported);
|
|
113998
|
+
bindings.push({ imported, local });
|
|
113999
|
+
}
|
|
114000
|
+
return bindings;
|
|
114001
|
+
}
|
|
114002
|
+
var SAFE_USAGE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
114003
|
+
function computeUsedSymbols(strippedContent, bindings) {
|
|
114004
|
+
if (bindings.length === 0)
|
|
114005
|
+
return [];
|
|
114006
|
+
const used = new Set;
|
|
114007
|
+
for (const binding of bindings) {
|
|
114008
|
+
if (!SAFE_USAGE_IDENTIFIER.test(binding.local)) {
|
|
114009
|
+
used.add(binding.imported);
|
|
114010
|
+
continue;
|
|
114011
|
+
}
|
|
114012
|
+
const re = new RegExp(`\\b${binding.local}\\b`, "g");
|
|
114013
|
+
let count = 0;
|
|
114014
|
+
for (const _match of strippedContent.matchAll(re)) {
|
|
114015
|
+
count++;
|
|
114016
|
+
if (count > 1)
|
|
114017
|
+
break;
|
|
114018
|
+
}
|
|
114019
|
+
if (count > 1)
|
|
114020
|
+
used.add(binding.imported);
|
|
114021
|
+
}
|
|
114022
|
+
return [...used].sort((a, b) => a.localeCompare(b));
|
|
114023
|
+
}
|
|
114024
|
+
function usedSymbolsForImport(parsed, strippedContent) {
|
|
114025
|
+
if (parsed.importType === "namespace" || parsed.importType === "sideeffect" || parsed.importType === "require") {
|
|
114026
|
+
return;
|
|
114027
|
+
}
|
|
114028
|
+
if (parsed.reExport) {
|
|
114029
|
+
return [...new Set(parsed.bindings.map((b) => b.imported))].sort((a, b) => a.localeCompare(b));
|
|
114030
|
+
}
|
|
114031
|
+
return computeUsedSymbols(strippedContent, parsed.bindings);
|
|
114032
|
+
}
|
|
114033
|
+
function collectExports(symbols2) {
|
|
114034
|
+
const exported = symbols2.filter((s) => s.exported);
|
|
114035
|
+
const exports = exported.map((s) => s.signature === `default ${s.name}` ? "default" : s.name);
|
|
114036
|
+
const exportLines = {};
|
|
114037
|
+
for (let i2 = 0;i2 < exported.length; i2++) {
|
|
114038
|
+
const s = exported[i2];
|
|
114039
|
+
const name2 = exports[i2];
|
|
114040
|
+
if (typeof s.line === "number" && Number.isFinite(s.line) && exportLines[name2] === undefined) {
|
|
114041
|
+
exportLines[name2] = s.line;
|
|
114042
|
+
}
|
|
114043
|
+
}
|
|
114044
|
+
return { exports, exportLines };
|
|
114045
|
+
}
|
|
113922
114046
|
function parseImportedSymbols(matchedString, importType) {
|
|
113923
114047
|
if (importType === "namespace")
|
|
113924
114048
|
return ["*"];
|
|
@@ -114062,22 +114186,23 @@ function scanFile(filePath, absoluteRoot, maxFileSize) {
|
|
|
114062
114186
|
}
|
|
114063
114187
|
const ext = path118.extname(filePath).toLowerCase();
|
|
114064
114188
|
let exports = [];
|
|
114189
|
+
let exportLines = {};
|
|
114065
114190
|
try {
|
|
114066
114191
|
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
|
|
114067
114192
|
const relativePath = path118.relative(absoluteRoot, filePath);
|
|
114068
|
-
|
|
114069
|
-
exports = symbols2.filter((s) => s.exported).map((s) => s.name);
|
|
114193
|
+
({ exports, exportLines } = collectExports(_internals74.extractTSSymbols(relativePath, absoluteRoot)));
|
|
114070
114194
|
} else if (ext === ".py") {
|
|
114071
114195
|
const relativePath = path118.relative(absoluteRoot, filePath);
|
|
114072
|
-
|
|
114073
|
-
exports = symbols2.filter((s) => s.exported).map((s) => s.name);
|
|
114196
|
+
({ exports, exportLines } = collectExports(_internals74.extractPythonSymbols(relativePath, absoluteRoot)));
|
|
114074
114197
|
}
|
|
114075
114198
|
const parsedImports = _internals74.parseFileImports(content);
|
|
114199
|
+
const strippedForUsage = parsedImports.length > 0 ? _internals74.stripComments(content) : "";
|
|
114076
114200
|
const moduleName = toModuleName(filePath, absoluteRoot);
|
|
114077
114201
|
const node = {
|
|
114078
114202
|
filePath,
|
|
114079
114203
|
moduleName,
|
|
114080
114204
|
exports,
|
|
114205
|
+
...Object.keys(exportLines).length > 0 ? { exportLines } : {},
|
|
114081
114206
|
imports: parsedImports.map((p) => p.specifier),
|
|
114082
114207
|
language: getLanguage(filePath),
|
|
114083
114208
|
mtime: fileStats.mtime.toISOString(),
|
|
@@ -114095,12 +114220,14 @@ function scanFile(filePath, absoluteRoot, maxFileSize) {
|
|
|
114095
114220
|
for (const parsed of sortedImports) {
|
|
114096
114221
|
const resolvedTarget = resolveModuleSpecifier(absoluteRoot, filePath, parsed.specifier);
|
|
114097
114222
|
if (resolvedTarget !== null) {
|
|
114223
|
+
const usedSymbols = usedSymbolsForImport(parsed, strippedForUsage);
|
|
114098
114224
|
edges.push({
|
|
114099
114225
|
source: filePath,
|
|
114100
114226
|
target: resolvedTarget,
|
|
114101
114227
|
importSpecifier: parsed.specifier,
|
|
114102
114228
|
importType: parsed.importType,
|
|
114103
|
-
importedSymbols: parsed.importedSymbols
|
|
114229
|
+
importedSymbols: parsed.importedSymbols,
|
|
114230
|
+
...usedSymbols !== undefined ? { usedSymbols } : {}
|
|
114104
114231
|
});
|
|
114105
114232
|
}
|
|
114106
114233
|
}
|
|
@@ -114356,6 +114483,113 @@ function getSymbolConsumers(graph, filePath, symbolName) {
|
|
|
114356
114483
|
refs.sort((a, b) => a.file.localeCompare(b.file));
|
|
114357
114484
|
return refs;
|
|
114358
114485
|
}
|
|
114486
|
+
function getCallers(graph, filePath, symbolName) {
|
|
114487
|
+
const node = getGraphNode(graph, filePath);
|
|
114488
|
+
if (!node)
|
|
114489
|
+
return [];
|
|
114490
|
+
const targetKey = normalizeGraphPath(node.filePath);
|
|
114491
|
+
const refs = [];
|
|
114492
|
+
const seen = new Set;
|
|
114493
|
+
for (const edge of graph.edges) {
|
|
114494
|
+
if (normalizeGraphPath(edge.target) !== targetKey)
|
|
114495
|
+
continue;
|
|
114496
|
+
const file3 = moduleNameForEdgePath(graph, edge.source);
|
|
114497
|
+
if (seen.has(file3))
|
|
114498
|
+
continue;
|
|
114499
|
+
if (edge.usedSymbols !== undefined) {
|
|
114500
|
+
if (edge.usedSymbols.includes(symbolName)) {
|
|
114501
|
+
seen.add(file3);
|
|
114502
|
+
refs.push({ file: file3, resolution: "used" });
|
|
114503
|
+
}
|
|
114504
|
+
continue;
|
|
114505
|
+
}
|
|
114506
|
+
const imported = edge.importedSymbols ?? [];
|
|
114507
|
+
if (edge.importType === "namespace" || imported.includes(symbolName)) {
|
|
114508
|
+
seen.add(file3);
|
|
114509
|
+
refs.push({ file: file3, resolution: "imported" });
|
|
114510
|
+
}
|
|
114511
|
+
}
|
|
114512
|
+
refs.sort((a, b) => a.file.localeCompare(b.file));
|
|
114513
|
+
return refs;
|
|
114514
|
+
}
|
|
114515
|
+
var DEAD_EXPORT_EXCLUDED_ROLES = new Set([
|
|
114516
|
+
"api_route",
|
|
114517
|
+
"cli_command",
|
|
114518
|
+
"test_file",
|
|
114519
|
+
"agent",
|
|
114520
|
+
"hook",
|
|
114521
|
+
"middleware"
|
|
114522
|
+
]);
|
|
114523
|
+
function getDeadExports(graph, options) {
|
|
114524
|
+
if (!isSchemaVersionAtLeast(graph.schema_version, "1.1.0")) {
|
|
114525
|
+
return {
|
|
114526
|
+
schemaSupported: false,
|
|
114527
|
+
analyzedFiles: 0,
|
|
114528
|
+
skippedUnresolvable: 0,
|
|
114529
|
+
candidates: [],
|
|
114530
|
+
note: 'Graph predates schema 1.1.0 (no usedSymbols data). Run repo_map action="build" to enable dead_exports.'
|
|
114531
|
+
};
|
|
114532
|
+
}
|
|
114533
|
+
const usage = new Map;
|
|
114534
|
+
for (const edge of graph.edges) {
|
|
114535
|
+
const target = normalizeGraphPath(edge.target);
|
|
114536
|
+
let entry = usage.get(target);
|
|
114537
|
+
if (!entry) {
|
|
114538
|
+
entry = { used: new Set, unresolvable: false };
|
|
114539
|
+
usage.set(target, entry);
|
|
114540
|
+
}
|
|
114541
|
+
if (edge.importType === "namespace" || edge.importType === "sideeffect" || edge.importType === "require") {
|
|
114542
|
+
entry.unresolvable = true;
|
|
114543
|
+
} else if (edge.usedSymbols) {
|
|
114544
|
+
for (const symbol3 of edge.usedSymbols)
|
|
114545
|
+
entry.used.add(symbol3);
|
|
114546
|
+
}
|
|
114547
|
+
}
|
|
114548
|
+
const reverse = getReverseIndex(graph);
|
|
114549
|
+
const candidates = [];
|
|
114550
|
+
let analyzedFiles = 0;
|
|
114551
|
+
let skippedUnresolvable = 0;
|
|
114552
|
+
for (const node of Object.values(graph.nodes)) {
|
|
114553
|
+
if (node.exports.length === 0)
|
|
114554
|
+
continue;
|
|
114555
|
+
const key = normalizeGraphPath(node.filePath);
|
|
114556
|
+
const importerCount = reverse.get(key)?.length ?? 0;
|
|
114557
|
+
if (importerCount === 0)
|
|
114558
|
+
continue;
|
|
114559
|
+
const roles = node.ontology?.roles ?? [];
|
|
114560
|
+
if (roles.some((r) => DEAD_EXPORT_EXCLUDED_ROLES.has(r)))
|
|
114561
|
+
continue;
|
|
114562
|
+
const entry = usage.get(key);
|
|
114563
|
+
if (entry?.unresolvable) {
|
|
114564
|
+
skippedUnresolvable++;
|
|
114565
|
+
continue;
|
|
114566
|
+
}
|
|
114567
|
+
analyzedFiles++;
|
|
114568
|
+
const used = entry?.used ?? new Set;
|
|
114569
|
+
for (const symbol3 of node.exports) {
|
|
114570
|
+
if (symbol3 === "default")
|
|
114571
|
+
continue;
|
|
114572
|
+
if (used.has(symbol3))
|
|
114573
|
+
continue;
|
|
114574
|
+
candidates.push({
|
|
114575
|
+
file: node.moduleName,
|
|
114576
|
+
symbol: symbol3,
|
|
114577
|
+
line: node.exportLines?.[symbol3],
|
|
114578
|
+
importerCount
|
|
114579
|
+
});
|
|
114580
|
+
}
|
|
114581
|
+
}
|
|
114582
|
+
candidates.sort((a, b) => a.file.localeCompare(b.file) || a.symbol.localeCompare(b.symbol));
|
|
114583
|
+
const limit = options?.maxCandidates ?? 100;
|
|
114584
|
+
const truncated = candidates.length > limit;
|
|
114585
|
+
return {
|
|
114586
|
+
schemaSupported: true,
|
|
114587
|
+
analyzedFiles,
|
|
114588
|
+
skippedUnresolvable,
|
|
114589
|
+
candidates: candidates.slice(0, limit),
|
|
114590
|
+
note: "Advisory: exported symbols with no detected in-repo reference, limited to files imported by >=1 other file. " + "Regex analysis cannot see dynamic dispatch, string-keyed access, or namespace/barrel re-export usage; verify before removing." + (truncated ? ` Showing ${limit} of ${candidates.length}.` : "")
|
|
114591
|
+
};
|
|
114592
|
+
}
|
|
114359
114593
|
function getBlastRadius(graph, filePaths, maxDepth = 3) {
|
|
114360
114594
|
const targetNodes = filePaths.map((filePath) => getGraphNode(graph, filePath)).filter((node) => node !== undefined);
|
|
114361
114595
|
const targets = targetNodes.map((node) => normalizeLookupPath(node.moduleName));
|
|
@@ -142569,7 +142803,9 @@ var VALID_ACTIONS = [
|
|
|
142569
142803
|
"key_files",
|
|
142570
142804
|
"ontology",
|
|
142571
142805
|
"package_boundaries",
|
|
142572
|
-
"preflight_packet"
|
|
142806
|
+
"preflight_packet",
|
|
142807
|
+
"callers",
|
|
142808
|
+
"dead_exports"
|
|
142573
142809
|
];
|
|
142574
142810
|
var MAX_FILE_PATH_LENGTH3 = 500;
|
|
142575
142811
|
var MAX_SYMBOL_LENGTH2 = 256;
|
|
@@ -142633,7 +142869,7 @@ async function loadOrError(directory, action) {
|
|
|
142633
142869
|
}
|
|
142634
142870
|
}
|
|
142635
142871
|
var repo_map = createSwarmTool({
|
|
142636
|
-
description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers.",
|
|
142872
|
+
description: "Query the repository code graph for structural awareness before editing. " + 'Actions: "build" (build/refresh .swarm/repo-graph.json), "importers" (who imports a file), ' + '"dependencies" (what a file imports), "blast_radius" (transitive dependents + risk), ' + '"localization" (compact context block for a target file), "key_files" (top-N most-imported files), ' + '"ontology" (file roles/routes/data/security/findings), "package_boundaries" (inferred package/layer boundaries), ' + '"preflight_packet" (bounded ontology packet for planning), ' + '"callers" (files that reference an exported symbol, call-site granularity; needs file+symbol), ' + '"dead_exports" (advisory: exported symbols with no detected in-repo reference). ' + "Use this before refactoring shared modules to avoid breaking unseen consumers. " + 'Note: "callers"/"dead_exports" use conservative regex analysis (TS/JS/Python) and cannot see ' + 'dynamic dispatch or namespace/barrel re-export usage; "dead_exports" results are review candidates, not delete directives.',
|
|
142637
142873
|
args: {
|
|
142638
142874
|
action: exports_external.enum([
|
|
142639
142875
|
"build",
|
|
@@ -142644,12 +142880,14 @@ var repo_map = createSwarmTool({
|
|
|
142644
142880
|
"key_files",
|
|
142645
142881
|
"ontology",
|
|
142646
142882
|
"package_boundaries",
|
|
142647
|
-
"preflight_packet"
|
|
142648
|
-
|
|
142883
|
+
"preflight_packet",
|
|
142884
|
+
"callers",
|
|
142885
|
+
"dead_exports"
|
|
142886
|
+
]).describe('Query action: "build" | "importers" | "dependencies" | "blast_radius" | "localization" | "key_files" | "ontology" | "package_boundaries" | "preflight_packet" | "callers" | "dead_exports"'),
|
|
142649
142887
|
file: exports_external.string().optional().describe("Target file (workspace-relative or absolute). Required for importers/dependencies/localization/ontology. Optional for preflight_packet."),
|
|
142650
142888
|
files: exports_external.array(exports_external.string()).optional().describe("Multiple target files for blast_radius/preflight_packet. If omitted, falls back to `file`."),
|
|
142651
|
-
symbol: exports_external.string().optional().describe('
|
|
142652
|
-
top_n: exports_external.number().int().min(1).max(100).optional().describe('For action="key_files"
|
|
142889
|
+
symbol: exports_external.string().optional().describe('Exported symbol name. Restricts consumers on action="importers"; required for action="callers".'),
|
|
142890
|
+
top_n: exports_external.number().int().min(1).max(100).optional().describe('For action="key_files"/"package_boundaries": entries to return (default 10). For action="dead_exports": max candidates (default 100).'),
|
|
142653
142891
|
max_depth: exports_external.number().int().min(1).max(10).optional().describe('For action="blast_radius": max BFS depth (default 3).')
|
|
142654
142892
|
},
|
|
142655
142893
|
async execute(args2, directory, _ctx) {
|
|
@@ -142711,6 +142949,10 @@ var repo_map = createSwarmTool({
|
|
|
142711
142949
|
stale
|
|
142712
142950
|
});
|
|
142713
142951
|
}
|
|
142952
|
+
if (action === "dead_exports") {
|
|
142953
|
+
const result = getDeadExports(graph, { maxCandidates: a.top_n ?? 100 });
|
|
142954
|
+
return ok(action, { ...result, stale });
|
|
142955
|
+
}
|
|
142714
142956
|
if (action === "preflight_packet") {
|
|
142715
142957
|
const inputs = a.files && a.files.length > 0 ? a.files : a.file ? [a.file] : [];
|
|
142716
142958
|
for (const f of inputs) {
|
|
@@ -142770,6 +143012,22 @@ var repo_map = createSwarmTool({
|
|
|
142770
143012
|
stale
|
|
142771
143013
|
});
|
|
142772
143014
|
}
|
|
143015
|
+
if (action === "callers") {
|
|
143016
|
+
if (a.symbol === undefined) {
|
|
143017
|
+
return err2(action, "callers requires `symbol` (the exported name)");
|
|
143018
|
+
}
|
|
143019
|
+
const sErr = validateSymbol(a.symbol);
|
|
143020
|
+
if (sErr)
|
|
143021
|
+
return err2(action, `invalid symbol: ${sErr}`);
|
|
143022
|
+
const callers = getCallers(graph, target, a.symbol);
|
|
143023
|
+
return ok(action, {
|
|
143024
|
+
target,
|
|
143025
|
+
symbol: a.symbol,
|
|
143026
|
+
count: callers.length,
|
|
143027
|
+
callers,
|
|
143028
|
+
stale
|
|
143029
|
+
});
|
|
143030
|
+
}
|
|
142773
143031
|
if (action === "dependencies") {
|
|
142774
143032
|
const deps = getDependencies(graph, target);
|
|
142775
143033
|
return ok(action, {
|
|
@@ -26,6 +26,8 @@ export declare const _internals: {
|
|
|
26
26
|
extractPythonSymbols: typeof extractPythonSymbols;
|
|
27
27
|
parseFileImports: typeof parseFileImports;
|
|
28
28
|
extractFileOntology: typeof extractFileOntology;
|
|
29
|
+
stripComments: typeof stripComments;
|
|
30
|
+
computeUsedSymbols: typeof computeUsedSymbols;
|
|
29
31
|
};
|
|
30
32
|
/**
|
|
31
33
|
* Add or update a node in the graph.
|
|
@@ -66,6 +68,16 @@ export declare function resolveModuleSpecifier(workspaceRoot: string, sourceFile
|
|
|
66
68
|
/**
|
|
67
69
|
* A parsed import with its specifier and type.
|
|
68
70
|
*/
|
|
71
|
+
/**
|
|
72
|
+
* A single imported binding: the symbol's *exported* name in the target file
|
|
73
|
+
* and the *local* name it is bound to in the importing file (differs when an
|
|
74
|
+
* `as` alias or default import is used). Used to attribute call-site usage back
|
|
75
|
+
* to the correct exported symbol.
|
|
76
|
+
*/
|
|
77
|
+
interface ImportBinding {
|
|
78
|
+
imported: string;
|
|
79
|
+
local: string;
|
|
80
|
+
}
|
|
69
81
|
interface ParsedImport {
|
|
70
82
|
/** The module specifier (e.g., './foo', 'lodash') */
|
|
71
83
|
specifier: string;
|
|
@@ -73,8 +85,42 @@ interface ParsedImport {
|
|
|
73
85
|
importType: 'default' | 'named' | 'namespace' | 'require' | 'sideeffect';
|
|
74
86
|
/** Named imported symbols when statically detectable */
|
|
75
87
|
importedSymbols: string[];
|
|
88
|
+
/** Alias-aware imported→local bindings for usage attribution */
|
|
89
|
+
bindings: ImportBinding[];
|
|
90
|
+
/** True for `export { x } from '...'` re-exports (symbols are re-exposed). */
|
|
91
|
+
reExport: boolean;
|
|
76
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Strip line (`//…`) and block (`/* … *\/`) comments from JS/TS source while
|
|
95
|
+
* preserving string, template-literal, and regex-literal contents (DD-C010).
|
|
96
|
+
* Import specifiers live inside string literals, so strings must be kept
|
|
97
|
+
* intact; only comment spans are removed. This is a bounded single-pass scanner
|
|
98
|
+
* — not a full parser (AST parsing in the repo-graph init path would violate
|
|
99
|
+
* AGENTS.md invariant 1) — and it eliminates the most common source of false
|
|
100
|
+
* import edges: import-like text inside comments (`// import x from "y"`).
|
|
101
|
+
*
|
|
102
|
+
* It is string-aware (a `//` inside `"http://…"` is not a comment) and
|
|
103
|
+
* regex-aware (a regex literal such as `/[/*]/` must not be mistaken for the
|
|
104
|
+
* start of a block comment, which would otherwise run to EOF and delete real
|
|
105
|
+
* imports). Regex-vs-division is disambiguated by the previous significant
|
|
106
|
+
* character (REGEX_ALLOWED_AFTER).
|
|
107
|
+
*/
|
|
108
|
+
declare function stripComments(content: string): string;
|
|
77
109
|
declare function parseFileImports(rawContent: string): ParsedImport[];
|
|
110
|
+
/**
|
|
111
|
+
* Conservatively determine which imported bindings are actually referenced in
|
|
112
|
+
* the importing file's body.
|
|
113
|
+
*
|
|
114
|
+
* Heuristic: in a well-formed import statement, each local binding name appears
|
|
115
|
+
* exactly once. Counting occurrences of the local name across the
|
|
116
|
+
* comment-stripped file content, a count > 1 means at least one body reference.
|
|
117
|
+
* Strings are intentionally *not* stripped, so the bias is toward "used" — a
|
|
118
|
+
* conservative direction that avoids false dead-export positives. Bindings whose
|
|
119
|
+
* local name cannot be safely word-boundary matched are assumed used.
|
|
120
|
+
*
|
|
121
|
+
* @returns the *exported* names (binding.imported) judged to be used.
|
|
122
|
+
*/
|
|
123
|
+
declare function computeUsedSymbols(strippedContent: string, bindings: readonly ImportBinding[]): string[];
|
|
78
124
|
/**
|
|
79
125
|
* Result of scanning a single file for graph updates.
|
|
80
126
|
*/
|
|
@@ -1,10 +1,35 @@
|
|
|
1
|
-
import type { BlastRadiusResult, FileOntology, FileReference, GraphNode, LocalizationBlock, PackageBoundarySummary, RepoGraph, SymbolReference } from './types';
|
|
1
|
+
import type { BlastRadiusResult, CallerReference, DeadExportsResult, FileOntology, FileReference, GraphNode, LocalizationBlock, PackageBoundarySummary, RepoGraph, SymbolReference } from './types';
|
|
2
2
|
export declare function getGraphNode(graph: RepoGraph, input: string): GraphNode | undefined;
|
|
3
3
|
export declare function resetQueryCache(): void;
|
|
4
4
|
export declare function isGraphFresh(graph: RepoGraph | null, maxAgeMs?: number): boolean;
|
|
5
5
|
export declare function getImporters(graph: RepoGraph, filePath: string): FileReference[];
|
|
6
6
|
export declare function getDependencies(graph: RepoGraph, filePath: string): FileReference[];
|
|
7
7
|
export declare function getSymbolConsumers(graph: RepoGraph, filePath: string, symbolName: string): SymbolReference[];
|
|
8
|
+
/**
|
|
9
|
+
* Files that actually *reference* an exported symbol of `filePath` — call-site
|
|
10
|
+
* granularity, not just "imports the file". On schema >= 1.1.0 graphs this uses
|
|
11
|
+
* per-edge `usedSymbols`; on older graphs (or namespace imports) it falls back
|
|
12
|
+
* to import-level matching, flagged via `resolution: 'imported'`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getCallers(graph: RepoGraph, filePath: string, symbolName: string): CallerReference[];
|
|
15
|
+
export interface DeadExportsOptions {
|
|
16
|
+
/** Max candidates returned (default 100). */
|
|
17
|
+
maxCandidates?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Conservatively detect exported symbols with no detected in-repo reference.
|
|
21
|
+
*
|
|
22
|
+
* Scoping for precision (advisory "candidate" output, never a delete directive):
|
|
23
|
+
* - Requires schema >= 1.1.0 (per-edge usedSymbols); otherwise returns
|
|
24
|
+
* schemaSupported=false so the caller can prompt a rebuild.
|
|
25
|
+
* - Only considers files imported by >= 1 other file — a file with no
|
|
26
|
+
* importers is a likely public-API entry / CLI / test, not dead code.
|
|
27
|
+
* - Skips files imported anywhere via namespace/side-effect/require/dynamic
|
|
28
|
+
* imports, where per-symbol usage is unresolvable.
|
|
29
|
+
* - Excludes framework-invoked roles (routes, CLIs, tests, agents, hooks,
|
|
30
|
+
* middleware) and the synthetic 'default' export.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getDeadExports(graph: RepoGraph, options?: DeadExportsOptions): DeadExportsResult;
|
|
8
33
|
export declare function getBlastRadius(graph: RepoGraph, filePaths: string[], maxDepth?: number): BlastRadiusResult;
|
|
9
34
|
export declare function getKeyFiles(graph: RepoGraph, topN?: number): GraphNode[];
|
|
10
35
|
export declare function getFileOntology(graph: RepoGraph, filePath: string): FileOntology | null;
|
|
@@ -7,7 +7,23 @@
|
|
|
7
7
|
* Every other submodule imports from here.
|
|
8
8
|
*/
|
|
9
9
|
export declare const REPO_GRAPH_FILENAME = "repo-graph.json";
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Graph schema version.
|
|
12
|
+
*
|
|
13
|
+
* 1.1.0 added per-edge `usedSymbols` (imported symbols actually referenced in
|
|
14
|
+
* the importing file) and per-node `exportLines`, enabling the `callers` and
|
|
15
|
+
* `dead_exports` queries. Both fields are optional, so graphs written by older
|
|
16
|
+
* versions (1.0.0) still load — but `dead_exports` requires >= 1.1.0 data and
|
|
17
|
+
* self-gates via {@link isSchemaVersionAtLeast} rather than relying on the
|
|
18
|
+
* loader (which only checks that a version string is present, not its value).
|
|
19
|
+
*/
|
|
20
|
+
export declare const GRAPH_SCHEMA_VERSION = "1.1.0";
|
|
21
|
+
/**
|
|
22
|
+
* Compare dotted numeric version strings (e.g. '1.1.0' >= '1.1.0').
|
|
23
|
+
* Missing/non-numeric segments are treated as 0. Returns true when `version`
|
|
24
|
+
* is greater than or equal to `minimum`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isSchemaVersionAtLeast(version: string | undefined, minimum: string): boolean;
|
|
11
27
|
export declare const FILE_ROLE_VALUES: readonly ["api_route", "middleware", "service_module", "data_module", "swarm_tool", "agent", "hook", "config", "schema", "test_file", "cli_command", "documentation", "source_module"];
|
|
12
28
|
export type FileRole = (typeof FILE_ROLE_VALUES)[number];
|
|
13
29
|
export declare const ROUTE_METHOD_VALUES: readonly ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD", "ALL"];
|
|
@@ -73,6 +89,13 @@ export interface GraphNode {
|
|
|
73
89
|
moduleName: string;
|
|
74
90
|
/** Exported symbols from this file */
|
|
75
91
|
exports: string[];
|
|
92
|
+
/**
|
|
93
|
+
* Definition line for each exported symbol, keyed by symbol name (1-based).
|
|
94
|
+
* Optional and best-effort: present on graphs built at schema >= 1.1.0,
|
|
95
|
+
* absent for symbols whose line could not be determined. Used to point
|
|
96
|
+
* `dead_exports` candidates at a location.
|
|
97
|
+
*/
|
|
98
|
+
exportLines?: Record<string, number>;
|
|
76
99
|
/** Imported module specifiers */
|
|
77
100
|
imports: string[];
|
|
78
101
|
/** Language/extension of the file */
|
|
@@ -98,6 +121,14 @@ export interface GraphEdge {
|
|
|
98
121
|
importType: ImportType;
|
|
99
122
|
/** Named symbols imported from the target, when statically detectable */
|
|
100
123
|
importedSymbols?: string[];
|
|
124
|
+
/**
|
|
125
|
+
* The subset of the target's exported symbols (by their *exported* name)
|
|
126
|
+
* that are actually referenced in the source file's body — not merely
|
|
127
|
+
* imported. Computed at build time via a conservative, alias-aware textual
|
|
128
|
+
* scan (schema >= 1.1.0). Absent on namespace/side-effect/require/dynamic
|
|
129
|
+
* imports, where individual symbol usage is not statically resolvable.
|
|
130
|
+
*/
|
|
131
|
+
usedSymbols?: string[];
|
|
101
132
|
}
|
|
102
133
|
export interface FileReference {
|
|
103
134
|
file: string;
|
|
@@ -109,6 +140,47 @@ export interface SymbolReference {
|
|
|
109
140
|
line?: number;
|
|
110
141
|
importedAs: string;
|
|
111
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* A file that references a specific exported symbol of a target file.
|
|
145
|
+
* `resolution` records how confidently the usage was attributed:
|
|
146
|
+
* - 'used' → the symbol was found referenced in the source body
|
|
147
|
+
* - 'imported' → fallback for graphs predating usedSymbols (schema < 1.1.0);
|
|
148
|
+
* the symbol is imported but body usage was not analyzed
|
|
149
|
+
*/
|
|
150
|
+
export interface CallerReference {
|
|
151
|
+
file: string;
|
|
152
|
+
resolution: 'used' | 'imported';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* An exported symbol with no detected in-repo reference. Advisory only —
|
|
156
|
+
* regex-based analysis cannot see dynamic dispatch, string-keyed access, or
|
|
157
|
+
* usage through namespace/barrel re-exports, so this is a *candidate* for
|
|
158
|
+
* review, never a directive to delete.
|
|
159
|
+
*/
|
|
160
|
+
export interface DeadExportCandidate {
|
|
161
|
+
/** Module name (workspace-relative) of the file that owns the export */
|
|
162
|
+
file: string;
|
|
163
|
+
/** The exported symbol name */
|
|
164
|
+
symbol: string;
|
|
165
|
+
/** Definition line, when known (from exportLines) */
|
|
166
|
+
line?: number;
|
|
167
|
+
/** How many other in-repo files import this file at all */
|
|
168
|
+
importerCount: number;
|
|
169
|
+
}
|
|
170
|
+
export interface DeadExportsResult {
|
|
171
|
+
/** False when the graph predates schema 1.1.0 (rebuild required). */
|
|
172
|
+
schemaSupported: boolean;
|
|
173
|
+
/** Files whose exports were analyzed (imported by >= 1 other file). */
|
|
174
|
+
analyzedFiles: number;
|
|
175
|
+
/**
|
|
176
|
+
* Files skipped because at least one importer used a namespace/side-effect/
|
|
177
|
+
* require/dynamic import, making per-symbol usage unresolvable.
|
|
178
|
+
*/
|
|
179
|
+
skippedUnresolvable: number;
|
|
180
|
+
candidates: DeadExportCandidate[];
|
|
181
|
+
/** Human-readable note describing scope and limitations of the result. */
|
|
182
|
+
note: string;
|
|
183
|
+
}
|
|
112
184
|
export interface BlastRadiusResult {
|
|
113
185
|
target: string[];
|
|
114
186
|
directDependents: string[];
|
|
@@ -20,8 +20,9 @@ export { clearCache, getCachedGraph, getCachedMtime, isDirty, markDirty, setCach
|
|
|
20
20
|
export { updateGraphForFiles } from './repo-graph/incremental';
|
|
21
21
|
export type { ExtractFileOntologyInput } from './repo-graph/ontology';
|
|
22
22
|
export { extractFileOntology } from './repo-graph/ontology';
|
|
23
|
-
export {
|
|
23
|
+
export type { DeadExportsOptions } from './repo-graph/query';
|
|
24
|
+
export { buildOntologyPreflightPacket, getBlastRadius, getCallers, getDeadExports, getDependencies, getFileOntology, getGraphNode, getImporters, getKeyFiles, getLocalizationContext, getPackageBoundaries, getSymbolConsumers, isGraphFresh, resetQueryCache, } from './repo-graph/query';
|
|
24
25
|
export { getGraphPath, loadGraph, loadGraphSync, loadOrCreateGraph, saveGraph, saveIfDirty, } from './repo-graph/storage';
|
|
25
|
-
export type { BlastRadiusResult, BuildWorkspaceGraphOptions, ConventionFact, DataOperationFact, FileOntology, FileReference, FileRole, GraphEdge, GraphNode, LocalizationBlock, OntologyFinding, PackageBoundarySummary, RepoGraph, RouteFact, RouteMethod, SecurityFact, SymbolReference, } from './repo-graph/types';
|
|
26
|
-
export { createEmptyGraph, GRAPH_SCHEMA_VERSION, normalizeGraphPath, REPO_GRAPH_FILENAME, updateGraphMetadata, } from './repo-graph/types';
|
|
26
|
+
export type { BlastRadiusResult, BuildWorkspaceGraphOptions, CallerReference, ConventionFact, DataOperationFact, DeadExportCandidate, DeadExportsResult, FileOntology, FileReference, FileRole, GraphEdge, GraphNode, LocalizationBlock, OntologyFinding, PackageBoundarySummary, RepoGraph, RouteFact, RouteMethod, SecurityFact, SymbolReference, } from './repo-graph/types';
|
|
27
|
+
export { createEmptyGraph, GRAPH_SCHEMA_VERSION, isSchemaVersionAtLeast, normalizeGraphPath, REPO_GRAPH_FILENAME, updateGraphMetadata, } from './repo-graph/types';
|
|
27
28
|
export { validateGraphEdge, validateGraphNode, validateWorkspace, } from './repo-graph/validation';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.86.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",
|