opencode-swarm 7.88.4 → 7.89.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.
@@ -14,51 +14,194 @@ import {
14
14
 
15
15
  // src/hooks/knowledge-store.ts
16
16
  var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
17
- import { existsSync } from "fs";
17
+ import { existsSync as existsSync2 } from "fs";
18
18
  import { appendFile, mkdir, readFile } from "fs/promises";
19
+ import * as os2 from "os";
20
+ import * as path2 from "path";
21
+
22
+ // src/hooks/knowledge-link.ts
23
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "fs";
19
24
  import * as os from "os";
20
25
  import * as path from "path";
21
- var KNOWLEDGE_JSONL_CACHE_NAMESPACE = "knowledge-jsonl:normalized:v1";
22
- function getPlatformConfigDir() {
26
+ var LINK_POINTER_FILENAME = "link.json";
27
+ var MAX_LINK_ID_LENGTH = 64;
28
+ var WINDOWS_RESERVED_NAMES = new Set([
29
+ "con",
30
+ "prn",
31
+ "aux",
32
+ "nul",
33
+ "com0",
34
+ "com1",
35
+ "com2",
36
+ "com3",
37
+ "com4",
38
+ "com5",
39
+ "com6",
40
+ "com7",
41
+ "com8",
42
+ "com9",
43
+ "lpt0",
44
+ "lpt1",
45
+ "lpt2",
46
+ "lpt3",
47
+ "lpt4",
48
+ "lpt5",
49
+ "lpt6",
50
+ "lpt7",
51
+ "lpt8",
52
+ "lpt9"
53
+ ]);
54
+ var CACHE_TTL_MS = 2000;
55
+ var MAX_CACHE_ENTRIES = 500;
56
+ function resolveDataDir() {
23
57
  const platform = process.platform;
24
58
  const home = process.env.HOME || os.homedir();
25
59
  if (platform === "win32") {
26
- return path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "opencode-swarm", "config");
60
+ return path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "opencode-swarm", "Data");
27
61
  } else if (platform === "darwin") {
28
62
  return path.join(home, "Library", "Application Support", "opencode-swarm");
63
+ }
64
+ return path.join(process.env.XDG_DATA_HOME || path.join(home, ".local", "share"), "opencode-swarm");
65
+ }
66
+ function resolveLinkBaseDir() {
67
+ return path.join(resolveDataDir(), "links");
68
+ }
69
+ function resolveLinkDir(linkId) {
70
+ return path.join(resolveLinkBaseDir(), linkId);
71
+ }
72
+ function sanitizeLinkId(name) {
73
+ if (typeof name !== "string")
74
+ return null;
75
+ const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, MAX_LINK_ID_LENGTH).replace(/[-.]+$/g, "");
76
+ if (cleaned.length === 0)
77
+ return null;
78
+ const base = cleaned.split(".")[0];
79
+ if (WINDOWS_RESERVED_NAMES.has(base))
80
+ return null;
81
+ return cleaned;
82
+ }
83
+ function resolveLinkPointerPath(directory) {
84
+ return path.join(directory, ".swarm", LINK_POINTER_FILENAME);
85
+ }
86
+ function readLinkPointer(directory) {
87
+ const pointerPath = resolveLinkPointerPath(directory);
88
+ if (!existsSync(pointerPath))
89
+ return null;
90
+ try {
91
+ const raw = JSON.parse(readFileSync(pointerPath, "utf-8"));
92
+ if (!raw || typeof raw !== "object")
93
+ return null;
94
+ const obj = raw;
95
+ const linkId = obj.linkId;
96
+ if (typeof linkId !== "string" || linkId.length === 0)
97
+ return null;
98
+ const safeId = sanitizeLinkId(linkId);
99
+ if (!safeId)
100
+ return null;
101
+ return {
102
+ version: 1,
103
+ linkId: safeId,
104
+ name: typeof obj.name === "string" ? obj.name : undefined,
105
+ createdAt: typeof obj.createdAt === "string" ? obj.createdAt : new Date(0).toISOString(),
106
+ source: obj.source === "auto" ? "auto" : "manual"
107
+ };
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+ async function writeLinkPointer(directory, pointer) {
113
+ const swarmDir = path.join(directory, ".swarm");
114
+ mkdirSync(swarmDir, { recursive: true });
115
+ const pointerPath = resolveLinkPointerPath(directory);
116
+ await atomicWriteFile(pointerPath, JSON.stringify(pointer, null, 2));
117
+ invalidateKnowledgeStoreDirCache(directory);
118
+ }
119
+ async function removeLinkPointer(directory) {
120
+ const pointerPath = resolveLinkPointerPath(directory);
121
+ try {
122
+ rmSync(pointerPath, { force: true });
123
+ } finally {
124
+ invalidateKnowledgeStoreDirCache(directory);
125
+ }
126
+ }
127
+ var _resolutionCache = new Map;
128
+ function resolveKnowledgeStoreDir(directory) {
129
+ const localSwarm = path.join(directory, ".swarm");
130
+ const now = Date.now();
131
+ const cached = _resolutionCache.get(directory);
132
+ if (cached && now < cached.expires) {
133
+ return cached.linkDir ?? localSwarm;
134
+ }
135
+ let linkDir = null;
136
+ try {
137
+ const pointer = readLinkPointer(directory);
138
+ if (pointer) {
139
+ linkDir = path.resolve(resolveLinkDir(pointer.linkId));
140
+ }
141
+ } catch {
142
+ linkDir = null;
143
+ }
144
+ if (!_resolutionCache.has(directory) && _resolutionCache.size >= MAX_CACHE_ENTRIES) {
145
+ const oldest = _resolutionCache.keys().next().value;
146
+ if (oldest !== undefined)
147
+ _resolutionCache.delete(oldest);
148
+ }
149
+ _resolutionCache.set(directory, { linkDir, expires: now + CACHE_TTL_MS });
150
+ return linkDir ?? localSwarm;
151
+ }
152
+ function invalidateKnowledgeStoreDirCache(directory) {
153
+ if (directory === undefined) {
154
+ _resolutionCache.clear();
155
+ return;
156
+ }
157
+ _resolutionCache.delete(directory);
158
+ }
159
+ function isLinked(directory) {
160
+ return readLinkPointer(directory) !== null;
161
+ }
162
+
163
+ // src/hooks/knowledge-store.ts
164
+ var KNOWLEDGE_JSONL_CACHE_NAMESPACE = "knowledge-jsonl:normalized:v1";
165
+ function getPlatformConfigDir() {
166
+ const platform = process.platform;
167
+ const home = process.env.HOME || os2.homedir();
168
+ if (platform === "win32") {
169
+ return path2.join(process.env.LOCALAPPDATA || path2.join(home, "AppData", "Local"), "opencode-swarm", "config");
170
+ } else if (platform === "darwin") {
171
+ return path2.join(home, "Library", "Application Support", "opencode-swarm");
29
172
  } else {
30
- return path.join(process.env.XDG_CONFIG_HOME || path.join(home, ".config"), "opencode-swarm");
173
+ return path2.join(process.env.XDG_CONFIG_HOME || path2.join(home, ".config"), "opencode-swarm");
31
174
  }
32
175
  }
33
176
  function resolveSwarmKnowledgePath(directory) {
34
- return path.join(directory, ".swarm", "knowledge.jsonl");
177
+ return path2.join(resolveKnowledgeStoreDir(directory), "knowledge.jsonl");
35
178
  }
36
179
  function resolveSwarmRejectedPath(directory) {
37
- return path.join(directory, ".swarm", "knowledge-rejected.jsonl");
180
+ return path2.join(resolveKnowledgeStoreDir(directory), "knowledge-rejected.jsonl");
38
181
  }
39
182
  function resolveSwarmRetractionsPath(directory) {
40
- return path.join(directory, ".swarm", "knowledge-retractions.jsonl");
183
+ return path2.join(resolveKnowledgeStoreDir(directory), "knowledge-retractions.jsonl");
41
184
  }
42
185
  function resolveHiveKnowledgePath() {
43
186
  const platform = process.platform;
44
- const home = process.env.HOME || os.homedir();
187
+ const home = process.env.HOME || os2.homedir();
45
188
  let dataDir;
46
189
  if (platform === "win32") {
47
- dataDir = path.join(process.env.LOCALAPPDATA || path.join(home, "AppData", "Local"), "opencode-swarm", "Data");
190
+ dataDir = path2.join(process.env.LOCALAPPDATA || path2.join(home, "AppData", "Local"), "opencode-swarm", "Data");
48
191
  } else if (platform === "darwin") {
49
- dataDir = path.join(home, "Library", "Application Support", "opencode-swarm");
192
+ dataDir = path2.join(home, "Library", "Application Support", "opencode-swarm");
50
193
  } else {
51
- dataDir = path.join(process.env.XDG_DATA_HOME || path.join(home, ".local", "share"), "opencode-swarm");
194
+ dataDir = path2.join(process.env.XDG_DATA_HOME || path2.join(home, ".local", "share"), "opencode-swarm");
52
195
  }
53
- return path.join(dataDir, "shared-learnings.jsonl");
196
+ return path2.join(dataDir, "shared-learnings.jsonl");
54
197
  }
55
198
  function resolveHiveRejectedPath() {
56
199
  const hivePath = resolveHiveKnowledgePath();
57
- return path.join(path.dirname(hivePath), "shared-learnings-rejected.jsonl");
200
+ return path2.join(path2.dirname(hivePath), "shared-learnings-rejected.jsonl");
58
201
  }
59
202
  function resolveHiveEventsPath() {
60
203
  const hivePath = resolveHiveKnowledgePath();
61
- return path.join(path.dirname(hivePath), "shared-knowledge-events.jsonl");
204
+ return path2.join(path2.dirname(hivePath), "shared-knowledge-events.jsonl");
62
205
  }
63
206
  function parseKnowledgeContent(content, max) {
64
207
  const results = [];
@@ -78,16 +221,16 @@ function parseKnowledgeContent(content, max) {
78
221
  return results;
79
222
  }
80
223
  async function readKnowledge(filePath, maxEntries) {
81
- const resolvedPath = path.resolve(filePath);
224
+ const resolvedPath = path2.resolve(filePath);
82
225
  const cap = maxEntries !== undefined && maxEntries > 0 ? maxEntries : undefined;
83
226
  if (cap !== undefined) {
84
- if (!existsSync(resolvedPath))
227
+ if (!existsSync2(resolvedPath))
85
228
  return [];
86
229
  const content = await readFile(resolvedPath, "utf-8");
87
230
  return parseKnowledgeContent(content, cap);
88
231
  }
89
232
  const entries = await readCachedParsedFile(resolvedPath, KNOWLEDGE_JSONL_CACHE_NAMESPACE, async () => {
90
- if (!existsSync(resolvedPath))
233
+ if (!existsSync2(resolvedPath))
91
234
  return null;
92
235
  return await readFile(resolvedPath, "utf-8");
93
236
  }, (content) => parseKnowledgeContent(content, Infinity));
@@ -169,7 +312,7 @@ async function appendRetractionRecord(directory, record) {
169
312
  await appendKnowledge(resolveSwarmRetractionsPath(directory), record);
170
313
  }
171
314
  async function appendKnowledge(filePath, entry) {
172
- const dir = path.dirname(filePath);
315
+ const dir = path2.dirname(filePath);
173
316
  await mkdir(dir, { recursive: true });
174
317
  let release = null;
175
318
  try {
@@ -188,7 +331,7 @@ async function appendKnowledge(filePath, entry) {
188
331
  }
189
332
  }
190
333
  async function rewriteKnowledge(filePath, entries) {
191
- const dir = path.dirname(filePath);
334
+ const dir = path2.dirname(filePath);
192
335
  await mkdir(dir, { recursive: true });
193
336
  let release = null;
194
337
  try {
@@ -209,7 +352,7 @@ async function rewriteKnowledge(filePath, entries) {
209
352
  }
210
353
  }
211
354
  async function transactFile(filePath, read, write, mutate) {
212
- const dir = path.dirname(filePath);
355
+ const dir = path2.dirname(filePath);
213
356
  try {
214
357
  await mkdir(dir, { recursive: true });
215
358
  } catch {
@@ -474,7 +617,7 @@ async function applyConfidenceDeltas(filePath, deltas) {
474
617
  }
475
618
  let release = null;
476
619
  try {
477
- const dir = path.dirname(filePath);
620
+ const dir = path2.dirname(filePath);
478
621
  await mkdir(dir, { recursive: true });
479
622
  release = await import_proper_lockfile.default.lock(dir, {
480
623
  retries: { retries: 5, minTimeout: 100, maxTimeout: 500 },
@@ -537,4 +680,4 @@ var _internals = {
537
680
  bumpKnowledgeConfidenceBatch
538
681
  };
539
682
 
540
- export { getPlatformConfigDir, resolveSwarmKnowledgePath, resolveSwarmRejectedPath, resolveSwarmRetractionsPath, resolveHiveKnowledgePath, resolveHiveRejectedPath, resolveHiveEventsPath, readKnowledge, normalizeEntry, readRejectedLessons, readRetractionRecords, appendRetractionRecord, appendKnowledge, rewriteKnowledge, transactFile, transactKnowledge, appendKnowledgeWithCapEnforcement, enforceKnowledgeCap, sweepAgedEntries, sweepStaleTodos, appendRejectedLesson, normalize, wordBigrams, jaccardBigram, findNearDuplicate, computeConfidence, OUTCOME_SIGNAL_SMOOTHING, computeOutcomeSignal, inferTags, bumpKnowledgeConfidenceBatch, _internals };
683
+ export { resolveLinkDir, sanitizeLinkId, readLinkPointer, writeLinkPointer, removeLinkPointer, resolveKnowledgeStoreDir, isLinked, getPlatformConfigDir, resolveSwarmKnowledgePath, resolveSwarmRejectedPath, resolveSwarmRetractionsPath, resolveHiveKnowledgePath, resolveHiveRejectedPath, resolveHiveEventsPath, readKnowledge, normalizeEntry, readRejectedLessons, readRetractionRecords, appendRetractionRecord, appendKnowledge, rewriteKnowledge, transactFile, transactKnowledge, appendKnowledgeWithCapEnforcement, enforceKnowledgeCap, sweepAgedEntries, sweepStaleTodos, appendRejectedLesson, normalize, wordBigrams, jaccardBigram, findNearDuplicate, computeConfidence, OUTCOME_SIGNAL_SMOOTHING, computeOutcomeSignal, inferTags, bumpKnowledgeConfidenceBatch, _internals };
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  import {
3
3
  handleGuardrailExplain
4
- } from "./index-d4hpgf63.js";
4
+ } from "./index-6k31ysgd.js";
5
5
  import {
6
6
  handleGuardrailLog
7
7
  } from "./index-gg589mfw.js";
@@ -51,6 +51,7 @@ import {
51
51
  handleKnowledgeRetryHardeningCommand,
52
52
  handleKnowledgeUnactionableCommand,
53
53
  handleLearningCommand,
54
+ handleLinkCommand,
54
55
  handleMemoryCommand,
55
56
  handleMemoryExportCommand,
56
57
  handleMemoryImportCommand,
@@ -73,14 +74,15 @@ import {
73
74
  handleStatusCommand,
74
75
  handleSyncPlanCommand,
75
76
  handleTurboCommand,
77
+ handleUnlinkCommand,
76
78
  handleWriteRetroCommand,
77
79
  normalizeSwarmCommandInput,
78
80
  resolveCommand
79
- } from "./index-hs2knbfq.js";
81
+ } from "./index-9w07ye9b.js";
80
82
  import"./index-6tnmt41c.js";
81
83
  import"./index-bm4f0nme.js";
82
- import"./index-rh53rrpt.js";
83
- import"./index-e8pk68cc.js";
84
+ import"./index-1ccnwh54.js";
85
+ import"./index-axwxkbdd.js";
84
86
  import"./index-bywt2171.js";
85
87
  import {
86
88
  AGENT_TOOL_MAP,
@@ -337,6 +339,7 @@ export {
337
339
  resolveCommand,
338
340
  normalizeSwarmCommandInput,
339
341
  handleWriteRetroCommand,
342
+ handleUnlinkCommand,
340
343
  handleTurboCommand,
341
344
  handleSyncPlanCommand,
342
345
  handleStatusCommand,
@@ -359,6 +362,7 @@ export {
359
362
  handleMemoryImportCommand,
360
363
  handleMemoryExportCommand,
361
364
  handleMemoryCommand,
365
+ handleLinkCommand,
362
366
  handleLearningCommand,
363
367
  handleKnowledgeUnactionableCommand,
364
368
  handleKnowledgeRetryHardeningCommand,
package/dist/cli/index.js CHANGED
@@ -7,11 +7,11 @@ import {
7
7
  getPluginLockFilePaths,
8
8
  package_default,
9
9
  resolveCommand
10
- } from "./index-hs2knbfq.js";
10
+ } from "./index-9w07ye9b.js";
11
11
  import"./index-6tnmt41c.js";
12
12
  import"./index-bm4f0nme.js";
13
- import"./index-rh53rrpt.js";
14
- import"./index-e8pk68cc.js";
13
+ import"./index-1ccnwh54.js";
14
+ import"./index-axwxkbdd.js";
15
15
  import"./index-bywt2171.js";
16
16
  import {
17
17
  DEFAULT_AGENT_CONFIGS
@@ -31,7 +31,7 @@ import {
31
31
  transactFile,
32
32
  transactKnowledge,
33
33
  wordBigrams
34
- } from "./index-e8pk68cc.js";
34
+ } from "./index-axwxkbdd.js";
35
35
  import"./index-fjwwrwr5.js";
36
36
  import"./index-jtqkh8jf.js";
37
37
  import"./index-bcp79s17.js";
@@ -22,8 +22,8 @@ import {
22
22
  retireSkill,
23
23
  sanitizeSlug,
24
24
  selectCandidateEntries
25
- } from "./index-rh53rrpt.js";
26
- import"./index-e8pk68cc.js";
25
+ } from "./index-1ccnwh54.js";
26
+ import"./index-axwxkbdd.js";
27
27
  import"./index-fjwwrwr5.js";
28
28
  import"./index-jtqkh8jf.js";
29
29
  import"./index-zgwm4ryv.js";
@@ -32,6 +32,7 @@ export { handleHandoffCommand } from './handoff';
32
32
  export { handleHistoryCommand } from './history';
33
33
  export { handleKnowledgeListCommand, handleKnowledgeMigrateCommand, handleKnowledgeQuarantineCommand, handleKnowledgeRestoreCommand, handleKnowledgeRetryHardeningCommand, handleKnowledgeUnactionableCommand, } from './knowledge';
34
34
  export { handleLearningCommand } from './learning';
35
+ export { handleLinkCommand } from './link';
35
36
  export { handleMemoryCommand, handleMemoryExportCommand, handleMemoryImportCommand, handleMemoryMigrateCommand, handleMemoryStatusCommand, } from './memory';
36
37
  export { handlePlanCommand } from './plan';
37
38
  export { handlePreflightCommand } from './preflight';
@@ -51,6 +52,7 @@ export { handleStatusCommand } from './status';
51
52
  export { handleSyncPlanCommand } from './sync-plan';
52
53
  export { classifySwarmCommandChatFallbackUse, classifySwarmCommandToolUse, SWARM_COMMAND_TOOL_ALLOWLIST, SWARM_COMMAND_TOOL_COMMANDS, } from './tool-policy.js';
53
54
  export { handleTurboCommand } from './turbo';
55
+ export { handleUnlinkCommand } from './unlink';
54
56
  export { handleWriteRetroCommand } from './write-retro';
55
57
  export declare function buildHelpText(): string;
56
58
  /**
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Handles the `/swarm link` command.
3
+ *
4
+ * Ties this worktree's swarm knowledge store to a shared "link" store so that
5
+ * several swarms working on the same project (typically separate git worktrees)
6
+ * — or on deliberately "similar" projects — pool their lessons instead of each
7
+ * keeping an isolated `.swarm/knowledge.jsonl`.
8
+ *
9
+ * Usage:
10
+ * - /swarm link — link using the project hash (ties all worktrees
11
+ * of the same repo to one shared store).
12
+ * - /swarm link <name> — link using an explicit shared name (use the same
13
+ * name in each worktree/repo to tie them together).
14
+ * - /swarm link status — show the current link state for this worktree.
15
+ *
16
+ * On link, this worktree's existing local lessons are merged (deduplicated) into
17
+ * the shared store so nothing already learned is lost.
18
+ */
19
+ export declare function handleLinkCommand(directory: string, args: string[]): Promise<string>;
@@ -609,6 +609,29 @@ export declare const COMMAND_REGISTRY: {
609
609
  readonly category: "config";
610
610
  readonly toolPolicy: "none";
611
611
  };
612
+ readonly link: {
613
+ readonly handler: (ctx: CommandContext) => Promise<string>;
614
+ readonly description: "Tie this worktree to a shared swarm knowledge store [name]";
615
+ readonly details: "Links the current worktree to a shared knowledge store so multiple swarms working on the same project (e.g. separate git worktrees) pool their lessons instead of each keeping an isolated .swarm/knowledge.jsonl. With no name, ties all worktrees of the same repo via the project hash; with a name, ties any worktrees/repos that use the same name. Existing local lessons are merged (deduped) into the shared store. Use `/swarm link status` to inspect.";
616
+ readonly args: "[<name> | status]";
617
+ readonly category: "utility";
618
+ readonly toolPolicy: "none";
619
+ };
620
+ readonly 'link status': {
621
+ readonly handler: (ctx: CommandContext) => Promise<string>;
622
+ readonly description: "Show whether this worktree shares knowledge via a link";
623
+ readonly subcommandOf: "link";
624
+ readonly category: "utility";
625
+ readonly toolPolicy: "none";
626
+ };
627
+ readonly unlink: {
628
+ readonly handler: (ctx: CommandContext) => Promise<string>;
629
+ readonly description: "Stop sharing swarm knowledge for this worktree [--no-copy]";
630
+ readonly details: "Unlinks the current worktree from its shared knowledge store and returns it to a local .swarm/knowledge.jsonl. By default the shared lessons are copied back into the local store (deduped) so nothing is lost; pass --no-copy to skip the copy-back.";
631
+ readonly args: "[--no-copy]";
632
+ readonly category: "utility";
633
+ readonly toolPolicy: "none";
634
+ };
612
635
  readonly promote: {
613
636
  readonly handler: (ctx: CommandContext) => Promise<string>;
614
637
  readonly description: "Manually promote lesson to hive knowledge";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Handles the `/swarm unlink` command.
3
+ *
4
+ * Stops sharing this worktree's swarm knowledge with its link store and returns
5
+ * it to a local `.swarm/knowledge.jsonl`. By default the shared lessons are
6
+ * copied back into the local store (deduplicated) so the worktree keeps the
7
+ * pooled knowledge it had access to; pass `--no-copy` to skip the copy-back.
8
+ *
9
+ * Usage:
10
+ * - /swarm unlink — unlink and copy shared lessons back to local.
11
+ * - /swarm unlink --no-copy — unlink without copying shared lessons back.
12
+ */
13
+ export declare function handleUnlinkCommand(directory: string, args: string[]): Promise<string>;
@@ -146,12 +146,12 @@ export type KnowledgeEventInput = KnowledgeEvent extends infer T ? T extends Kno
146
146
  } : never : never;
147
147
  /** Receipt event verbs that reference a single knowledge_id. */
148
148
  export declare const RECEIPT_EVENT_TYPES: ReadonlySet<string>;
149
- /** Returns `.swarm/knowledge-events.jsonl` for the given project directory. */
149
+ /** Returns the knowledge-events.jsonl path (link-aware via resolveKnowledgeStoreDir). */
150
150
  export declare function resolveKnowledgeEventsPath(directory: string): string;
151
- /** Returns `.swarm/knowledge-counter-baseline.json` for folded event counters. */
151
+ /** Returns the knowledge-counter-baseline.json path (link-aware). */
152
152
  export declare function resolveKnowledgeCounterBaselinePath(directory: string): string;
153
153
  export declare function resolveHiveEventsPath(): string;
154
- /** Returns `.swarm/knowledge-application.jsonl` for legacy v2 audit records. */
154
+ /** Returns the knowledge-application.jsonl path for legacy v2 audit records (link-aware). */
155
155
  export declare function resolveLegacyApplicationLogPath(directory: string): string;
156
156
  /** Generate a fresh trace id. One per retrieval; receipts reference it. */
157
157
  export declare function newTraceId(): string;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Knowledge "link" resolution for the opencode-swarm knowledge system.
3
+ *
4
+ * Problem: the swarm knowledge tier is rooted at `<directory>/.swarm/`, where
5
+ * `directory` is `ctx.directory`. When several swarms run in separate git
6
+ * worktrees of the same repository, each worktree has its own `.swarm/` and
7
+ * therefore its own isolated knowledge store — lessons one swarm learns are
8
+ * invisible to the others. The hive tier is the opposite extreme: global to
9
+ * EVERY project on the machine.
10
+ *
11
+ * A "link" ties multiple worktrees (or several deliberately "similar" repos)
12
+ * to one shared knowledge store that sits between the per-worktree swarm tier
13
+ * and the global hive tier. Membership is declared by an opt-in pointer file at
14
+ * `<directory>/.swarm/link.json`. When that pointer is active, the swarm
15
+ * knowledge *family* (store, events, rejected, retractions, counters,
16
+ * quarantine, unactionable) redirects from `<directory>/.swarm` to a shared
17
+ * link directory in the platform data dir — co-located with the hive store.
18
+ *
19
+ * Intentionally NOT redirected (stays per-worktree): `.knowledge-shown.json`
20
+ * (phase-keyed, session-local outcome bookkeeping), `plan.json`, evidence, and
21
+ * session state. Only id-keyed / append-only knowledge data is pooled.
22
+ *
23
+ * This module is self-contained (node builtins + the atomic-write helper only)
24
+ * so it can be imported by knowledge-store.ts, knowledge-events.ts, and
25
+ * knowledge-validator.ts without coupling to the heavily test-mocked store
26
+ * module and without import cycles.
27
+ */
28
+ /** On-disk pointer at `<directory>/.swarm/link.json`. */
29
+ export interface LinkPointer {
30
+ version: 1;
31
+ /** Path-safe identifier of the shared store (projectHash or sanitized name). */
32
+ linkId: string;
33
+ /** Human-friendly name when the link was created from an explicit name. */
34
+ name?: string;
35
+ /** ISO 8601 creation timestamp. */
36
+ createdAt: string;
37
+ /** How the link was established. */
38
+ source: 'manual' | 'auto';
39
+ }
40
+ export declare const LINK_POINTER_FILENAME = "link.json";
41
+ /** Root directory under which all shared link stores live: `<dataDir>/links`. */
42
+ export declare function resolveLinkBaseDir(): string;
43
+ /** Directory of the shared knowledge family for a given link id. */
44
+ export declare function resolveLinkDir(linkId: string): string;
45
+ /**
46
+ * Coerce an arbitrary user-supplied name into a single, path-safe directory
47
+ * segment. Returns null when nothing usable remains (caller falls back to the
48
+ * project hash). Lowercased for case-insensitive stability across worktrees.
49
+ */
50
+ export declare function sanitizeLinkId(name: string): string | null;
51
+ /** Read and validate the link pointer for a worktree. Null if absent/invalid. */
52
+ export declare function readLinkPointer(directory: string): LinkPointer | null;
53
+ /** Write the link pointer atomically and invalidate the resolution cache. */
54
+ export declare function writeLinkPointer(directory: string, pointer: LinkPointer): Promise<void>;
55
+ /** Remove the link pointer (idempotent) and invalidate the resolution cache. */
56
+ export declare function removeLinkPointer(directory: string): Promise<void>;
57
+ /**
58
+ * Resolve the directory that holds the swarm knowledge family for `directory`.
59
+ *
60
+ * Returns the shared link directory when an active pointer is present, otherwise
61
+ * the local `<directory>/.swarm`. Fail-open: any read/parse error degrades to the
62
+ * local directory, so a corrupt pointer never strands knowledge. Synchronous and
63
+ * cached so the hot retrieval path pays at most one tiny file read per TTL window.
64
+ *
65
+ * NOTE: when unlinked, the return value is byte-identical to the legacy
66
+ * `path.join(directory, '.swarm')`, so existing callers/tests are unaffected.
67
+ */
68
+ export declare function resolveKnowledgeStoreDir(directory: string): string;
69
+ /** Drop cached resolution(s). Pass a directory to invalidate one, omit for all. */
70
+ export declare function invalidateKnowledgeStoreDirCache(directory?: string): void;
71
+ /** True when the worktree currently redirects to a shared link store. */
72
+ export declare function isLinked(directory: string): boolean;
73
+ export declare const _internals: {
74
+ resolveKnowledgeStoreDir: typeof resolveKnowledgeStoreDir;
75
+ readLinkPointer: typeof readLinkPointer;
76
+ writeLinkPointer: typeof writeLinkPointer;
77
+ removeLinkPointer: typeof removeLinkPointer;
78
+ invalidateKnowledgeStoreDirCache: typeof invalidateKnowledgeStoreDirCache;
79
+ resolveLinkDir: typeof resolveLinkDir;
80
+ resolveLinkBaseDir: typeof resolveLinkBaseDir;
81
+ sanitizeLinkId: typeof sanitizeLinkId;
82
+ };
@@ -68,7 +68,7 @@ export interface ActionabilityResult {
68
68
  * quarantined rather than activated.
69
69
  */
70
70
  export declare function validateActionability(entry: Pick<KnowledgeEntryBase, 'forbidden_actions' | 'required_actions' | 'verification_checks' | 'verification_predicate' | 'applies_to_tools' | 'applies_to_agents'>): ActionabilityResult;
71
- /** Returns `.swarm/knowledge-unactionable.jsonl` for the given directory. */
71
+ /** Returns the knowledge-unactionable.jsonl path for the given directory (link-aware). */
72
72
  export declare function resolveUnactionablePath(directory: string): string;
73
73
  /** One quarantined-unactionable record. */
74
74
  export interface UnactionableRecord extends KnowledgeEntryBase {