@swarmvaultai/engine 3.15.0 → 3.16.1

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.
Files changed (122) hide show
  1. package/README.md +6 -1
  2. package/dist/{chunk-NUWZUYE7.js → chunk-NON6BVEI.js} +4 -0
  3. package/dist/{chunk-JJDJF2P3.js → chunk-TQIIJVVG.js} +341 -10
  4. package/dist/index.d.ts +77 -2
  5. package/dist/index.js +234 -4
  6. package/dist/{memory-A4VPLUBA.js → memory-4BDKH4Y2.js} +2 -2
  7. package/dist/{registry-2QC3VN7M.js → registry-ZZ6NESFD.js} +1 -1
  8. package/package.json +3 -3
  9. package/dist/chunk-2CH2WWS4.js +0 -1359
  10. package/dist/chunk-2PN46RDI.js +0 -26846
  11. package/dist/chunk-333AMRSV.js +0 -1056
  12. package/dist/chunk-3GVEUYQZ.js +0 -1641
  13. package/dist/chunk-4MSSM2GH.js +0 -1476
  14. package/dist/chunk-563TZ4TZ.js +0 -26573
  15. package/dist/chunk-5GEPTIZE.js +0 -26010
  16. package/dist/chunk-5HNZ2WQI.js +0 -1341
  17. package/dist/chunk-5Q4IV4O3.js +0 -1336
  18. package/dist/chunk-6MO57J5C.js +0 -988
  19. package/dist/chunk-6UPHDGEB.js +0 -1073
  20. package/dist/chunk-75BU5TQ6.js +0 -1690
  21. package/dist/chunk-7O2HJSWQ.js +0 -1686
  22. package/dist/chunk-7QHDATCQ.js +0 -1673
  23. package/dist/chunk-B3FC4J3P.js +0 -1214
  24. package/dist/chunk-BTWPJEP2.js +0 -1421
  25. package/dist/chunk-CG67P2HB.js +0 -1420
  26. package/dist/chunk-CSPDMCON.js +0 -26846
  27. package/dist/chunk-CVFY54CF.js +0 -24893
  28. package/dist/chunk-CWLDFLH2.js +0 -1163
  29. package/dist/chunk-DAJAZPPO.js +0 -26865
  30. package/dist/chunk-EEWB4WGH.js +0 -1056
  31. package/dist/chunk-EXD4RWT3.js +0 -1131
  32. package/dist/chunk-F7HZZ3VM.js +0 -931
  33. package/dist/chunk-FD3LJQ4T.js +0 -1216
  34. package/dist/chunk-G2TH6ZTA.js +0 -1468
  35. package/dist/chunk-H3CDZYRE.js +0 -1701
  36. package/dist/chunk-HFU5S5NO.js +0 -838
  37. package/dist/chunk-HKU2T5JX.js +0 -25213
  38. package/dist/chunk-HOJ7NSYC.js +0 -937
  39. package/dist/chunk-HRRPWXRZ.js +0 -1335
  40. package/dist/chunk-HW72C7O2.js +0 -1690
  41. package/dist/chunk-IAEYFTUS.js +0 -1159
  42. package/dist/chunk-IHMJCCXR.js +0 -1146
  43. package/dist/chunk-JTRE7C7P.js +0 -26062
  44. package/dist/chunk-LEUV6TWJ.js +0 -1131
  45. package/dist/chunk-MB7HPUTR.js +0 -1364
  46. package/dist/chunk-MZSUYTSL.js +0 -998
  47. package/dist/chunk-N56FAH4N.js +0 -1404
  48. package/dist/chunk-NCSZ4AKP.js +0 -1057
  49. package/dist/chunk-NECZ4MUE.js +0 -1416
  50. package/dist/chunk-NHGS4LOI.js +0 -1346
  51. package/dist/chunk-OK5752AP.js +0 -1325
  52. package/dist/chunk-QMW7OISM.js +0 -1063
  53. package/dist/chunk-RN56HUXA.js +0 -26972
  54. package/dist/chunk-RSQRF4FV.js +0 -1424
  55. package/dist/chunk-S2E65WRI.js +0 -26062
  56. package/dist/chunk-SRHM3HP4.js +0 -944
  57. package/dist/chunk-U7JO257M.js +0 -25017
  58. package/dist/chunk-UQCF65BN.js +0 -1623
  59. package/dist/chunk-USSP4GVB.js +0 -25064
  60. package/dist/chunk-V7KX3AQD.js +0 -26010
  61. package/dist/chunk-WOA5LSNB.js +0 -26559
  62. package/dist/chunk-WWP3VPEJ.js +0 -26080
  63. package/dist/chunk-YFKWMXJ6.js +0 -26066
  64. package/dist/chunk-Z552HHPV.js +0 -26846
  65. package/dist/chunk-ZQ5T64AR.js +0 -1365
  66. package/dist/memory-DNSQCDHC.js +0 -32
  67. package/dist/memory-ECS3TSGC.js +0 -32
  68. package/dist/memory-FVIBFROA.js +0 -32
  69. package/dist/memory-G6I3DBW4.js +0 -32
  70. package/dist/memory-HE6VWUPV.js +0 -32
  71. package/dist/memory-HEA7XNKB.js +0 -32
  72. package/dist/memory-JRYTVHNH.js +0 -32
  73. package/dist/memory-K3NL5E3K.js +0 -32
  74. package/dist/memory-KANI73CX.js +0 -32
  75. package/dist/memory-KI5G2A4C.js +0 -32
  76. package/dist/memory-PK55JUKG.js +0 -32
  77. package/dist/memory-PK5JJNAG.js +0 -32
  78. package/dist/memory-SAQPBIB4.js +0 -32
  79. package/dist/memory-SVGRP5KS.js +0 -32
  80. package/dist/memory-TQ46BGCI.js +0 -32
  81. package/dist/memory-YKQWWIVY.js +0 -32
  82. package/dist/memory-Z7BP5OSC.js +0 -32
  83. package/dist/registry-2REAPKPO.js +0 -12
  84. package/dist/registry-2XHXZDGH.js +0 -12
  85. package/dist/registry-4C55ZCPL.js +0 -12
  86. package/dist/registry-4QRMVAHX.js +0 -12
  87. package/dist/registry-5SYH3Y3U.js +0 -12
  88. package/dist/registry-6KZMA3XM.js +0 -12
  89. package/dist/registry-7QACDJQQ.js +0 -12
  90. package/dist/registry-B7UXRBW3.js +0 -12
  91. package/dist/registry-FKEREVDO.js +0 -12
  92. package/dist/registry-FLSGGY2R.js +0 -12
  93. package/dist/registry-G7NSRYCO.js +0 -12
  94. package/dist/registry-GH4O3A7H.js +0 -12
  95. package/dist/registry-IBH6K2KK.js +0 -12
  96. package/dist/registry-ILDEBNCW.js +0 -12
  97. package/dist/registry-JFEW5RUP.js +0 -12
  98. package/dist/registry-JQYQOZYN.js +0 -12
  99. package/dist/registry-JR5WY22P.js +0 -12
  100. package/dist/registry-KLO5YIHP.js +0 -12
  101. package/dist/registry-KVJAO5DF.js +0 -12
  102. package/dist/registry-MYJX6AEE.js +0 -12
  103. package/dist/registry-NBLIJHZT.js +0 -12
  104. package/dist/registry-NLRWSN5J.js +0 -12
  105. package/dist/registry-NMXDBYIZ.js +0 -12
  106. package/dist/registry-OUB6W3LM.js +0 -12
  107. package/dist/registry-P5KRT66L.js +0 -12
  108. package/dist/registry-PGZWRXMD.js +0 -12
  109. package/dist/registry-QAG2ZYH3.js +0 -12
  110. package/dist/registry-SUXWCWB4.js +0 -12
  111. package/dist/registry-SYCRRA65.js +0 -12
  112. package/dist/registry-TYROWPR5.js +0 -12
  113. package/dist/registry-U23ML76I.js +0 -12
  114. package/dist/registry-U76DBOV3.js +0 -12
  115. package/dist/registry-UA42LQUQ.js +0 -12
  116. package/dist/registry-W6ZFRI73.js +0 -12
  117. package/dist/registry-X5PMZTZY.js +0 -12
  118. package/dist/registry-XIL5F33J.js +0 -12
  119. package/dist/registry-XOPLQNZY.js +0 -12
  120. package/dist/registry-YDXVCE4Q.js +0 -12
  121. package/dist/registry-YGVTLIZH.js +0 -12
  122. package/dist/registry-ZNW3FDED.js +0 -12
package/README.md CHANGED
@@ -215,6 +215,7 @@ The engine supports:
215
215
  - `together`
216
216
  - `xai`
217
217
  - `cerebras`
218
+ - `local-whisper`
218
219
  - `openai-compatible`
219
220
  - `custom`
220
221
 
@@ -228,6 +229,7 @@ Providers are capability-driven. Each provider declares support for features suc
228
229
  - `streaming`
229
230
  - `local`
230
231
  - `image_generation`
232
+ - `audio`
231
233
 
232
234
  This matters because many "OpenAI-compatible" backends only implement part of the OpenAI surface.
233
235
 
@@ -271,6 +273,7 @@ This matters because many "OpenAI-compatible" backends only implement part of th
271
273
  - `queryGraphVault(rootDir, question, { traversal, budget })` runs deterministic local graph search, preferring semantic seed matches from `tasks.embeddingProvider` when configured and falling back to lexical search plus matching group patterns otherwise
272
274
  - `pathGraphVault(rootDir, from, to)` returns the shortest graph path between two targets
273
275
  - `explainGraphVault(rootDir, target)` returns node, community, neighbor, provenance, and group-pattern details
276
+ - `findGraphCycles(graph, { relations, limit, maxDepth })` returns deterministic directed cycles, commonly used for import or call-loop inspection
274
277
  - `listGraphHyperedges(rootDir, target?, limit?)` returns graph hyperedges globally or for a specific node/page target
275
278
  - `listGodNodes(rootDir, limit)` returns the most connected bridge-heavy graph nodes
276
279
  - `buildGraphShareArtifact(...)`, `renderGraphShareMarkdown(...)`, `renderGraphShareSvg(...)`, `renderGraphSharePreviewHtml(...)`, and `renderGraphShareBundleFiles(...)` produce the post-ready text, 1200x630 visual card, self-contained HTML preview, and portable share kit used by `wiki/graph/share-card.md`, `wiki/graph/share-card.svg`, `wiki/graph/share-kit/`, and the CLI `graph share` command
@@ -289,7 +292,9 @@ This matters because many "OpenAI-compatible" backends only implement part of th
289
292
  - large ingest and compile passes emit low-noise progress on TTYs, bound source-analysis concurrency, and use sparse graph co-occurrence projection so dense note sets do not create unbounded pairwise graph work
290
293
  - JSON state writes are atomic, and JSON parse failures include the exact state file path that failed to parse
291
294
  - `installGitHooks(rootDir)`, `uninstallGitHooks(rootDir)`, and `getGitHookStatus(rootDir)` manage local `post-commit` and `post-checkout` hook blocks for the nearest git repository
292
- - `installAgent(rootDir, agent, { hook })` writes agent instructions and returns the primary `target`, all touched `targets`, and optional merge warnings for agents such as Aider
295
+ - `installAgent(rootDir, agent, { hook, scope })` writes agent instructions and returns the primary `target`, all touched `targets`, and optional merge warnings for agents such as Aider and Kilo
296
+ - `getAgentInstallStatus(rootDir, agent, { scope })` reports expected install targets, presence, and managed-block status
297
+ - `addProviderConfig`, `listProviderConfigEntries`, `getProviderConfigEntry`, and `removeProviderConfig` manage named provider entries and task routing in `swarmvault.config.json`
293
298
  - `lintVault(rootDir, options)` runs structural lint, optional deep lint, optional contradiction-only filtering through `{ conflicts: true }`, and optional web-augmented evidence gathering
294
299
  - `listSchedules(rootDir)`, `runSchedule(rootDir, jobId)`, and `serveSchedules(rootDir)` manage recurring local jobs from config
295
300
  - compile, query, explore, lint, and watch also write canonical markdown session artifacts to `state/sessions/`
@@ -66,9 +66,11 @@ var agentTypeSchema = z.enum([
66
66
  "cortex",
67
67
  "crush",
68
68
  "deepagents",
69
+ "devin",
69
70
  "firebender",
70
71
  "iflow",
71
72
  "junie",
73
+ "kilo",
72
74
  "kilo-code",
73
75
  "kimi",
74
76
  "kode",
@@ -1669,6 +1671,8 @@ async function getResolvedPaths(rootDir) {
1669
1671
  }
1670
1672
 
1671
1673
  export {
1674
+ providerCapabilitySchema,
1675
+ providerTypeSchema,
1672
1676
  slugify,
1673
1677
  sha256,
1674
1678
  ensureDir,
@@ -22,7 +22,7 @@ import {
22
22
  uniqueBy,
23
23
  writeFileIfChanged,
24
24
  writeJsonFile
25
- } from "./chunk-NUWZUYE7.js";
25
+ } from "./chunk-NON6BVEI.js";
26
26
  import {
27
27
  estimateTokens
28
28
  } from "./chunk-NAIERP4C.js";
@@ -208,6 +208,7 @@ var agentFileKinds = {
208
208
  kiroSteering: ".kiro/steering/swarmvault.md",
209
209
  antigravityRules: ".agents/rules/swarmvault.md",
210
210
  antigravityWorkflow: ".agents/workflows/swarmvault.md",
211
+ devinRules: ".windsurf/rules/swarmvault.md",
211
212
  vscode: ".github/chatmodes/swarmvault.chatmode.md"
212
213
  };
213
214
  var legacyAntigravityFileKinds = {
@@ -226,6 +227,7 @@ var SKILL_BUNDLE_AGENTS = {
226
227
  cortex: ".snowflake/cortex/skills",
227
228
  crush: ".config/crush/skills",
228
229
  deepagents: ".deepagents/agent/skills",
230
+ devin: ".devin/skills",
229
231
  firebender: ".firebender/skills",
230
232
  iflow: ".iflow/skills",
231
233
  junie: ".junie/skills",
@@ -248,15 +250,53 @@ var SKILL_BUNDLE_AGENTS = {
248
250
  windsurf: ".codeium/windsurf/skills",
249
251
  zencoder: ".zencoder/skills"
250
252
  };
253
+ var PROJECT_SKILL_TARGETS = {
254
+ antigravity: [".agents/skills"],
255
+ amp: [".amp/skills"],
256
+ codex: [".agents/skills"],
257
+ copilot: [".copilot/skills"],
258
+ devin: [".devin/skills"],
259
+ gemini: [".gemini/skills"],
260
+ kimi: [".kimi/skills"],
261
+ opencode: [".opencode/skills"],
262
+ pi: [".pi/agent/skills"],
263
+ vscode: [".copilot/skills"]
264
+ };
265
+ var USER_SKILL_TARGETS = {
266
+ antigravity: path2.join(".gemini", "config", "skills"),
267
+ amp: path2.join(".amp", "skills"),
268
+ codex: path2.join(".codex", "skills"),
269
+ copilot: path2.join(".copilot", "skills"),
270
+ devin: path2.join(".config", "devin", "skills"),
271
+ gemini: path2.join(".gemini", "skills"),
272
+ kimi: path2.join(".kimi", "skills"),
273
+ kilo: path2.join(".config", "kilo", "skills"),
274
+ opencode: path2.join(".config", "opencode", "skills"),
275
+ pi: path2.join(".pi", "agent", "skills"),
276
+ vscode: path2.join(".copilot", "skills")
277
+ };
251
278
  function skillBundleTarget(rootDir, agent) {
252
279
  const relativeSkillsDir = SKILL_BUNDLE_AGENTS[agent];
253
280
  if (!relativeSkillsDir) return null;
254
281
  return path2.join(rootDir, relativeSkillsDir, "swarmvault", "SKILL.md");
255
282
  }
283
+ function skillBundlePath(baseDir, relativeSkillsDir) {
284
+ return path2.join(baseDir, relativeSkillsDir, "swarmvault", "SKILL.md");
285
+ }
286
+ function projectSkillTargets(rootDir, agent) {
287
+ return (PROJECT_SKILL_TARGETS[agent] ?? []).map((relativeSkillsDir) => skillBundlePath(rootDir, relativeSkillsDir));
288
+ }
289
+ function userSkillTarget(agent) {
290
+ const relativeSkillsDir = USER_SKILL_TARGETS[agent];
291
+ return relativeSkillsDir ? skillBundlePath(os.homedir(), relativeSkillsDir) : null;
292
+ }
256
293
  var hermesUserSkillRelative = path2.join(".hermes", "skills", "swarmvault", "SKILL.md");
257
294
  function hermesUserSkillPath() {
258
295
  return path2.join(os.homedir(), hermesUserSkillRelative);
259
296
  }
297
+ function kiloUserCommandPath() {
298
+ return path2.join(os.homedir(), ".config", "kilo", "command", "swarmvault.md");
299
+ }
260
300
  var SWARMVAULT_RULE_BULLETS = [
261
301
  "- Read `swarmvault.schema.md` before compile or query style work. It is the canonical schema path.",
262
302
  "- Treat `raw/` as immutable source input.",
@@ -357,6 +397,37 @@ function buildAntigravityWorkflowFile() {
357
397
  ""
358
398
  ].join("\n");
359
399
  }
400
+ function buildKiloCommandFile() {
401
+ return [
402
+ "# /swarmvault",
403
+ "",
404
+ "Use SwarmVault's graph-first workflow in the current project.",
405
+ "",
406
+ "1. If no vault exists, run `swarmvault init`.",
407
+ "2. Read `wiki/graph/report.md` before broad source search when it exists.",
408
+ "3. Prefer `swarmvault graph query`, `swarmvault graph path`, and `swarmvault graph explain` for structure questions.",
409
+ "4. Run `swarmvault compile` after adding or refreshing sources.",
410
+ ""
411
+ ].join("\n");
412
+ }
413
+ function buildKiloPluginFile() {
414
+ return [
415
+ "export default async function SwarmVaultPlugin({ project }) {",
416
+ " return {",
417
+ " name: 'swarmvault-graph-first',",
418
+ " async beforeToolUse(event) {",
419
+ " const toolName = event?.tool?.name ?? event?.toolName ?? '';",
420
+ " if (!['bash', 'shell', 'terminal', 'search', 'grep', 'glob'].includes(String(toolName).toLowerCase())) return;",
421
+ " const root = project?.root ?? process.cwd();",
422
+ " return {",
423
+ " message: `SwarmVault graph guidance: from ${root}, read wiki/graph/report.md first when it exists, or run swarmvault graph query/path/explain before broad search.`",
424
+ " };",
425
+ " }",
426
+ " };",
427
+ "}",
428
+ ""
429
+ ].join("\n");
430
+ }
360
431
  function buildVscodeChatmodeFile() {
361
432
  const frontmatter = YAML.stringify({
362
433
  description: "SwarmVault graph-first workflow for VS Code Copilot Chat.",
@@ -387,10 +458,20 @@ function buildCursorRule() {
387
458
  return ["---", frontmatter, "---", "", buildManagedBlock("cursor").trimEnd(), ""].join("\n");
388
459
  }
389
460
  function supportsAgentHook(agent) {
390
- return agent === "codex" || agent === "claude" || agent === "opencode" || agent === "gemini" || agent === "copilot";
461
+ return agent === "codex" || agent === "claude" || agent === "opencode" || agent === "gemini" || agent === "copilot" || agent === "kilo";
462
+ }
463
+ function installScope(agent, options = {}) {
464
+ if (options.scope) return options.scope;
465
+ return agent === "hermes" ? "user" : "project";
391
466
  }
392
- function primaryTargetPathForAgent(rootDir, agent) {
467
+ function primaryTargetPathForAgent(rootDir, agent, options = {}) {
468
+ if (installScope(agent, options) === "user") {
469
+ if (agent === "hermes") return hermesUserSkillPath();
470
+ const target = userSkillTarget(agent);
471
+ if (target) return target;
472
+ }
393
473
  switch (agent) {
474
+ case "kilo":
394
475
  case "codex":
395
476
  case "goose":
396
477
  case "pi":
@@ -435,6 +516,8 @@ function hookScriptPathForAgent(rootDir, agent) {
435
516
  return path2.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
436
517
  case "opencode":
437
518
  return path2.join(rootDir, ".opencode", "plugins", "swarmvault-graph-first.js");
519
+ case "kilo":
520
+ return path2.join(rootDir, ".kilo", "plugins", "swarmvault.js");
438
521
  case "gemini":
439
522
  return path2.join(rootDir, ".gemini", "hooks", "swarmvault-graph-first.js");
440
523
  case "copilot":
@@ -451,6 +534,10 @@ function hookConfigPathForAgent(rootDir, agent) {
451
534
  return path2.join(rootDir, ".claude", "settings.json");
452
535
  case "gemini":
453
536
  return path2.join(rootDir, ".gemini", "settings.json");
537
+ case "opencode":
538
+ return path2.join(rootDir, ".opencode", "opencode.json");
539
+ case "kilo":
540
+ return path2.join(rootDir, ".kilo", "kilo.json");
454
541
  case "copilot":
455
542
  return path2.join(rootDir, ".github", "hooks", "swarmvault-graph-first.json");
456
543
  default:
@@ -458,7 +545,20 @@ function hookConfigPathForAgent(rootDir, agent) {
458
545
  }
459
546
  }
460
547
  function targetsForAgent(rootDir, agent, options = {}) {
461
- const targets = [primaryTargetPathForAgent(rootDir, agent)];
548
+ const scope = installScope(agent, options);
549
+ const targets = [primaryTargetPathForAgent(rootDir, agent, options)];
550
+ if (scope === "user") {
551
+ if (agent === "kilo") {
552
+ targets.push(kiloUserCommandPath());
553
+ }
554
+ if (agent === "hermes") {
555
+ targets.push(path2.join(rootDir, agentFileKinds.agents));
556
+ }
557
+ return [...new Set(targets)];
558
+ }
559
+ if (options.scope === "project") {
560
+ targets.push(...projectSkillTargets(rootDir, agent));
561
+ }
462
562
  if (agent === "copilot") {
463
563
  targets.push(path2.join(rootDir, agentFileKinds.agents));
464
564
  }
@@ -477,6 +577,9 @@ function targetsForAgent(rootDir, agent, options = {}) {
477
577
  if (agent === "antigravity") {
478
578
  targets.push(path2.join(rootDir, agentFileKinds.antigravityWorkflow));
479
579
  }
580
+ if (agent === "devin") {
581
+ targets.push(path2.join(rootDir, agentFileKinds.devinRules));
582
+ }
480
583
  if (options.hook && supportsAgentHook(agent)) {
481
584
  const configPath = hookConfigPathForAgent(rootDir, agent);
482
585
  const scriptPath = hookScriptPathForAgent(rootDir, agent);
@@ -557,6 +660,85 @@ async function readJsonWithWarnings(filePath, fallback, label) {
557
660
  };
558
661
  }
559
662
  }
663
+ function stripJsonComments(source) {
664
+ let output = "";
665
+ let inString = false;
666
+ let escaped = false;
667
+ let inLineComment = false;
668
+ let inBlockComment = false;
669
+ for (let index = 0; index < source.length; index += 1) {
670
+ const current = source[index] ?? "";
671
+ const next = source[index + 1] ?? "";
672
+ if (inLineComment) {
673
+ if (current === "\n" || current === "\r") {
674
+ inLineComment = false;
675
+ output += current;
676
+ }
677
+ continue;
678
+ }
679
+ if (inBlockComment) {
680
+ if (current === "*" && next === "/") {
681
+ inBlockComment = false;
682
+ index += 1;
683
+ } else if (current === "\n" || current === "\r") {
684
+ output += current;
685
+ }
686
+ continue;
687
+ }
688
+ if (inString) {
689
+ output += current;
690
+ if (escaped) {
691
+ escaped = false;
692
+ } else if (current === "\\") {
693
+ escaped = true;
694
+ } else if (current === '"') {
695
+ inString = false;
696
+ }
697
+ continue;
698
+ }
699
+ if (current === '"') {
700
+ inString = true;
701
+ output += current;
702
+ continue;
703
+ }
704
+ if (current === "/" && next === "/") {
705
+ inLineComment = true;
706
+ index += 1;
707
+ continue;
708
+ }
709
+ if (current === "/" && next === "*") {
710
+ inBlockComment = true;
711
+ index += 1;
712
+ continue;
713
+ }
714
+ output += current;
715
+ }
716
+ return output;
717
+ }
718
+ async function readJsonOrJsoncWithWarnings(jsonPath, jsoncPath, fallback, label) {
719
+ if (await fileExists(jsonPath)) {
720
+ return readJsonWithWarnings(jsonPath, fallback, label);
721
+ }
722
+ if (!await fileExists(jsoncPath)) {
723
+ return { data: fallback, warnings: [] };
724
+ }
725
+ try {
726
+ const parsed = JSON.parse(stripJsonComments(await fs2.readFile(jsoncPath, "utf8")));
727
+ return { data: parsed, warnings: [] };
728
+ } catch {
729
+ return {
730
+ data: fallback,
731
+ warnings: [`Could not parse ${label}. Left the existing file unchanged.`]
732
+ };
733
+ }
734
+ }
735
+ function withPluginEntry(config, pluginEntry) {
736
+ const existing = Array.isArray(config.plugins) ? config.plugins.filter((entry) => typeof entry === "string") : [];
737
+ return {
738
+ ...config,
739
+ plugins: existing.includes(pluginEntry) ? existing : [...existing, pluginEntry]
740
+ };
741
+ }
560
742
  async function installClaudeHook(rootDir) {
561
743
  const settingsPath = path2.join(rootDir, ".claude", "settings.json");
562
744
  const scriptPath = path2.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
@@ -724,8 +906,33 @@ async function installCopilotHook(rootDir) {
724
906
  }
725
907
  async function installOpenCodeHook(rootDir) {
726
908
  const pluginPath = path2.join(rootDir, ".opencode", "plugins", "swarmvault-graph-first.js");
909
+ const configPath = path2.join(rootDir, ".opencode", "opencode.json");
727
910
  await writeOwnedFile(pluginPath, await readBuiltHook("opencode.js"));
728
- return { paths: [pluginPath], warnings: [] };
911
+ const { data: config, warnings } = await readJsonWithWarnings(configPath, {}, ".opencode/opencode.json");
912
+ if (warnings.length > 0 && await fileExists(configPath)) {
913
+ return { paths: [pluginPath, configPath], warnings };
914
+ }
915
+ await writeOwnedFile(configPath, `${JSON.stringify(withPluginEntry(config, "./plugins/swarmvault-graph-first.js"), null, 2)}
916
+ `);
917
+ return { paths: [pluginPath, configPath], warnings: [] };
918
+ }
919
+ async function installKiloHook(rootDir) {
920
+ const pluginPath = path2.join(rootDir, ".kilo", "plugins", "swarmvault.js");
921
+ const configPath = path2.join(rootDir, ".kilo", "kilo.json");
922
+ const jsoncPath = path2.join(rootDir, ".kilo", "kilo.jsonc");
923
+ await writeOwnedFile(pluginPath, buildKiloPluginFile());
924
+ const { data: config, warnings } = await readJsonOrJsoncWithWarnings(
925
+ configPath,
926
+ jsoncPath,
927
+ {},
928
+ ".kilo/kilo.json or .kilo/kilo.jsonc"
929
+ );
930
+ if (warnings.length > 0 && (await fileExists(configPath) || await fileExists(jsoncPath))) {
931
+ return { paths: [pluginPath, configPath], warnings };
932
+ }
933
+ await writeOwnedFile(configPath, `${JSON.stringify(withPluginEntry(config, "./plugins/swarmvault.js"), null, 2)}
934
+ `);
935
+ return { paths: [pluginPath, configPath], warnings: [] };
729
936
  }
730
937
  function stableKeyForAgent(rootDir, agent) {
731
938
  if (agent === "codex" || agent === "goose" || agent === "pi") {
@@ -735,9 +942,28 @@ function stableKeyForAgent(rootDir, agent) {
735
942
  }
736
943
  async function installAgent(rootDir, agent, options = {}) {
737
944
  await initWorkspace(rootDir);
738
- const target = primaryTargetPathForAgent(rootDir, agent);
945
+ const scope = installScope(agent, options);
946
+ const target = primaryTargetPathForAgent(rootDir, agent, options);
739
947
  const warnings = [];
948
+ if (scope === "user") {
949
+ if (agent === "hermes") {
950
+ await upsertManagedBlock(path2.join(rootDir, agentFileKinds.agents), buildManagedBlock("agents"));
951
+ await writeOwnedFile(hermesUserSkillPath(), buildStandaloneSkillFile());
952
+ } else {
953
+ const userTarget = userSkillTarget(agent);
954
+ if (!userTarget) {
955
+ throw new Error(`User-scope install is not supported for agent ${String(agent)}`);
956
+ }
957
+ await writeOwnedFile(userTarget, buildStandaloneSkillFile());
958
+ if (agent === "kilo") {
959
+ await writeOwnedFile(kiloUserCommandPath(), buildKiloCommandFile());
960
+ }
961
+ }
962
+ const targets2 = targetsForAgent(rootDir, agent, options);
963
+ return warnings.length > 0 ? { agent, target, targets: targets2, warnings } : { agent, target, targets: targets2 };
964
+ }
740
965
  switch (agent) {
966
+ case "kilo":
741
967
  case "codex":
742
968
  case "goose":
743
969
  case "pi":
@@ -798,6 +1024,14 @@ async function installAgent(rootDir, agent, options = {}) {
798
1024
  const aiderResult = await mergeAiderConfig(rootDir);
799
1025
  warnings.push(...aiderResult.warnings);
800
1026
  }
1027
+ if (options.scope === "project") {
1028
+ for (const skillTarget of projectSkillTargets(rootDir, agent)) {
1029
+ await writeOwnedFile(skillTarget, buildStandaloneSkillFile());
1030
+ }
1031
+ }
1032
+ if (agent === "devin") {
1033
+ await writeOwnedFile(path2.join(rootDir, agentFileKinds.devinRules), buildManagedBlock("devinRules"));
1034
+ }
801
1035
  if (options.hook && supportsAgentHook(agent)) {
802
1036
  if (agent === "codex") {
803
1037
  const result = await installCodexHook(rootDir);
@@ -811,6 +1045,10 @@ async function installAgent(rootDir, agent, options = {}) {
811
1045
  const result = await installOpenCodeHook(rootDir);
812
1046
  warnings.push(...result.warnings);
813
1047
  }
1048
+ if (agent === "kilo") {
1049
+ const result = await installKiloHook(rootDir);
1050
+ warnings.push(...result.warnings);
1051
+ }
814
1052
  if (agent === "gemini") {
815
1053
  const result = await installGeminiHook(rootDir);
816
1054
  warnings.push(...result.warnings);
@@ -823,6 +1061,24 @@ async function installAgent(rootDir, agent, options = {}) {
823
1061
  const targets = targetsForAgent(rootDir, agent, options);
824
1062
  return warnings.length > 0 ? { agent, target, targets, warnings } : { agent, target, targets };
825
1063
  }
1064
+ async function getAgentInstallStatus(rootDir, agent, options = {}) {
1065
+ const target = primaryTargetPathForAgent(rootDir, agent, options);
1066
+ const targets = targetsForAgent(rootDir, agent, options);
1067
+ const targetStatuses = await Promise.all(
1068
+ targets.map(async (targetPath) => ({
1069
+ path: targetPath,
1070
+ exists: await fileExists(targetPath)
1071
+ }))
1072
+ );
1073
+ return {
1074
+ agent,
1075
+ scope: installScope(agent, options),
1076
+ hook: options.hook ?? false,
1077
+ target,
1078
+ targets: targetStatuses,
1079
+ installed: targetStatuses.length > 0 && targetStatuses.every((entry) => entry.exists)
1080
+ };
1081
+ }
826
1082
  async function installConfiguredAgents(rootDir) {
827
1083
  const { config } = await initWorkspace(rootDir);
828
1084
  const dedupedAgents = /* @__PURE__ */ new Map();
@@ -15779,6 +16035,79 @@ function graphDiff(oldGraph, newGraph) {
15779
16035
  const summary = parts.length ? parts.join("; ") : "No changes";
15780
16036
  return { addedNodes, removedNodes, addedEdges, removedEdges, addedPages, removedPages, summary };
15781
16037
  }
16038
+ function findGraphCycles(graph, options = {}) {
16039
+ const relationFilter = options.relations?.length ? new Set(options.relations) : null;
16040
+ const limit = Math.max(1, Math.min(options.limit ?? 25, 250));
16041
+ const maxDepth = Math.max(2, Math.min(options.maxDepth ?? 25, 100));
16042
+ const nodes = nodeById(graph);
16043
+ const adjacency = /* @__PURE__ */ new Map();
16044
+ for (const edge of graph.edges) {
16045
+ if (relationFilter && !relationFilter.has(edge.relation)) {
16046
+ continue;
16047
+ }
16048
+ if (!nodes.has(edge.source) || !nodes.has(edge.target)) {
16049
+ continue;
16050
+ }
16051
+ const outgoing = adjacency.get(edge.source) ?? [];
16052
+ outgoing.push(edge);
16053
+ adjacency.set(edge.source, outgoing);
16054
+ }
16055
+ for (const [nodeId, edges] of adjacency.entries()) {
16056
+ adjacency.set(
16057
+ nodeId,
16058
+ edges.sort(
16059
+ (left, right) => left.target.localeCompare(right.target) || left.relation.localeCompare(right.relation) || left.id.localeCompare(right.id)
16060
+ )
16061
+ );
16062
+ }
16063
+ const cycles = [];
16064
+ const seenKeys = /* @__PURE__ */ new Set();
16065
+ const sortedNodeIds = [...nodes.keys()].sort((left, right) => left.localeCompare(right));
16066
+ const addCycle = (nodeIds, edges) => {
16067
+ const key = nodeIds.join(">");
16068
+ if (seenKeys.has(key)) {
16069
+ return;
16070
+ }
16071
+ seenKeys.add(key);
16072
+ cycles.push({
16073
+ nodeIds,
16074
+ labels: nodeIds.map((nodeId) => nodes.get(nodeId)?.label ?? nodeId),
16075
+ edgeIds: edges.map((edge) => edge.id),
16076
+ relations: edges.map((edge) => edge.relation)
16077
+ });
16078
+ };
16079
+ const visit = (startId, currentId, pathNodeIds, pathEdges, visited) => {
16080
+ if (cycles.length >= limit || pathNodeIds.length > maxDepth) {
16081
+ return;
16082
+ }
16083
+ for (const edge of adjacency.get(currentId) ?? []) {
16084
+ const nextId = edge.target;
16085
+ if (nextId === startId && pathNodeIds.length > 1) {
16086
+ addCycle([...pathNodeIds], [...pathEdges, edge]);
16087
+ if (cycles.length >= limit) return;
16088
+ continue;
16089
+ }
16090
+ if (visited.has(nextId) || nextId.localeCompare(startId) < 0) {
16091
+ continue;
16092
+ }
16093
+ visited.add(nextId);
16094
+ visit(startId, nextId, [...pathNodeIds, nextId], [...pathEdges, edge], visited);
16095
+ visited.delete(nextId);
16096
+ if (cycles.length >= limit) return;
16097
+ }
16098
+ };
16099
+ for (const startId of sortedNodeIds) {
16100
+ visit(startId, startId, [startId], [], /* @__PURE__ */ new Set([startId]));
16101
+ if (cycles.length >= limit) {
16102
+ break;
16103
+ }
16104
+ }
16105
+ cycles.sort(
16106
+ (left, right) => left.labels.join(" > ").localeCompare(right.labels.join(" > ")) || left.nodeIds.join(">").localeCompare(right.nodeIds.join(">"))
16107
+ );
16108
+ const summary = cycles.length ? `Found ${cycles.length} cycle${cycles.length === 1 ? "" : "s"}${cycles.length >= limit ? ` (limited to ${limit})` : ""}.` : "No cycles found.";
16109
+ return { cycles, limit, summary };
16110
+ }
15782
16111
  function blastRadius(graph, target, options) {
15783
16112
  const maxDepth = Math.max(1, Math.min(options?.maxDepth ?? 3, 10));
15784
16113
  const resolved = resolveNode(graph, target);
@@ -20150,7 +20479,7 @@ async function resolveImageGenerationProvider(rootDir) {
20150
20479
  if (!providerConfig) {
20151
20480
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
20152
20481
  }
20153
- const { createProvider: createProvider2 } = await import("./registry-ILDEBNCW.js");
20482
+ const { createProvider: createProvider2 } = await import("./registry-ZZ6NESFD.js");
20154
20483
  return createProvider2(preferredProviderId, providerConfig, rootDir);
20155
20484
  }
20156
20485
  async function generateOutputArtifacts(rootDir, input) {
@@ -26120,13 +26449,13 @@ async function buildContextPack(rootDir, options) {
26120
26449
  }
26121
26450
  }
26122
26451
  }
26123
- const memorySummaries = await import("./memory-PK55JUKG.js").then(({ listMemoryTasks: listMemoryTasks2 }) => listMemoryTasks2(rootDir)).catch(() => []);
26452
+ const memorySummaries = await import("./memory-4BDKH4Y2.js").then(({ listMemoryTasks: listMemoryTasks2 }) => listMemoryTasks2(rootDir)).catch(() => []);
26124
26453
  const memorySummaryIds = uniqueStrings5([
26125
26454
  ...options.memoryTaskId ? [options.memoryTaskId] : [],
26126
26455
  ...memorySummaries.slice(0, 20).map((summary) => summary.id)
26127
26456
  ]);
26128
26457
  if (memorySummaryIds.length) {
26129
- const { readMemoryTask: readMemoryTask2 } = await import("./memory-PK55JUKG.js");
26458
+ const { readMemoryTask: readMemoryTask2 } = await import("./memory-4BDKH4Y2.js");
26130
26459
  for (const taskId of memorySummaryIds) {
26131
26460
  const task = await readMemoryTask2(rootDir, taskId).catch(() => null);
26132
26461
  if (!task) {
@@ -26205,7 +26534,7 @@ async function buildContextPack(rootDir, options) {
26205
26534
  ]
26206
26535
  });
26207
26536
  if (options.memoryTaskId) {
26208
- const { updateMemoryTask: updateMemoryTask2 } = await import("./memory-PK55JUKG.js");
26537
+ const { updateMemoryTask: updateMemoryTask2 } = await import("./memory-4BDKH4Y2.js");
26209
26538
  await updateMemoryTask2(rootDir, options.memoryTaskId, { contextPackId: pack.id });
26210
26539
  }
26211
26540
  return {
@@ -26879,6 +27208,7 @@ function estimateMemoryTaskTokens(task) {
26879
27208
 
26880
27209
  export {
26881
27210
  installAgent,
27211
+ getAgentInstallStatus,
26882
27212
  installConfiguredAgents,
26883
27213
  DEFAULT_PROMOTION_CONFIG,
26884
27214
  evaluateCandidateForPromotion,
@@ -26920,6 +27250,7 @@ export {
26920
27250
  graphStats,
26921
27251
  validateGraphArtifact,
26922
27252
  graphDiff,
27253
+ findGraphCycles,
26923
27254
  blastRadius,
26924
27255
  LARGE_REPO_NODE_THRESHOLD,
26925
27256
  resolveLargeRepoDefaults,