skillwiki 0.3.0 → 0.4.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.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
 
10
10
  // src/cli.ts
11
11
  import { readFileSync as readFileSync9 } from "fs";
12
- import { join as join37 } from "path";
12
+ import { join as join38 } from "path";
13
13
  import { Command } from "commander";
14
14
 
15
15
  // ../shared/src/exit-codes.ts
@@ -1985,7 +1985,9 @@ async function runStale(input) {
1985
1985
  const meta = transcriptMeta.get(t.relPath);
1986
1986
  if (!meta) continue;
1987
1987
  if (CLAIMABLE_KINDS.has(meta.kind) && meta.project) {
1988
- unclaimedTranscripts.push({ path: t.relPath, reason: `${meta.kind} for ${meta.project} \u2014 no work item` });
1988
+ const projectSlug = extractSlug(meta.project);
1989
+ const hint = `skillwiki claim ${t.relPath} --project ${projectSlug}`;
1990
+ unclaimedTranscripts.push({ path: t.relPath, reason: `${meta.kind} for ${meta.project} \u2014 no work item`, hint });
1989
1991
  }
1990
1992
  }
1991
1993
  const doneWorkItems = [];
@@ -2084,7 +2086,8 @@ async function runStale(input) {
2084
2086
  const hintLines = [];
2085
2087
  if (stale.length > 0) hintLines.push(`stale_pages: ${stale.length}`, ...stale.map((p) => ` ${p.page}: ${p.reason}`));
2086
2088
  if (staleTranscripts.length > 0) hintLines.push(`stale_transcripts: ${staleTranscripts.length}`, ...staleTranscripts.map((t) => ` ${t.path}: ${t.reason}`));
2087
- if (unclaimedTranscripts.length > 0) hintLines.push(`unclaimed_transcripts: ${unclaimedTranscripts.length}`, ...unclaimedTranscripts.map((t) => ` ${t.path}: ${t.reason}`));
2089
+ if (unclaimedTranscripts.length > 0) hintLines.push(`unclaimed_transcripts: ${unclaimedTranscripts.length}`, ...unclaimedTranscripts.map((t) => ` ${t.path}: ${t.reason}${t.hint ? `
2090
+ hint: ${t.hint}` : ""}`));
2088
2091
  if (incompleteWorkItems.length > 0) hintLines.push(`incomplete_work_items: ${incompleteWorkItems.length}`, ...incompleteWorkItems.map((w) => ` ${w.path}: ${w.reason}`));
2089
2092
  if (doneWorkItems.length > 0) hintLines.push(`done_work_items: ${doneWorkItems.length}`, ...doneWorkItems.map((w) => ` ${w.path}: ${w.reason}`));
2090
2093
  if (archived.length > 0) hintLines.push(`archived: ${archived.length}`, ...archived.map((a) => ` ${a}`));
@@ -2100,6 +2103,104 @@ async function runStale(input) {
2100
2103
  }) };
2101
2104
  }
2102
2105
 
2106
+ // src/commands/claim.ts
2107
+ import { mkdir as mkdir7, writeFile as writeFile6, readFile as readFile11 } from "fs/promises";
2108
+ import { existsSync as existsSync2, statSync } from "fs";
2109
+ import { join as join14 } from "path";
2110
+ function extractDate(filename) {
2111
+ const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
2112
+ return m?.[1] ?? "";
2113
+ }
2114
+ function extractSlugFromFilename(filename) {
2115
+ return filename.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/^(task|bug|idea|note|observation)-/, "").replace(/\.md$/, "");
2116
+ }
2117
+ function extractProjectSlug(projectField) {
2118
+ return projectField.replace(/^\[\[/, "").replace(/\]\]$/, "").replace(/^"|"$/g, "");
2119
+ }
2120
+ async function runClaim(input) {
2121
+ if (!existsSync2(input.vault) || !statSync(input.vault).isDirectory()) {
2122
+ return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
2123
+ }
2124
+ const absTranscript = join14(input.vault, input.transcript);
2125
+ if (!existsSync2(absTranscript)) {
2126
+ return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
2127
+ }
2128
+ const content = await readFile11(absTranscript, "utf8");
2129
+ const fm = extractFrontmatter(content);
2130
+ let projectSlug = input.project;
2131
+ if (!projectSlug && fm.ok && typeof fm.data.project === "string") {
2132
+ projectSlug = extractProjectSlug(fm.data.project);
2133
+ }
2134
+ if (!projectSlug) {
2135
+ return {
2136
+ exitCode: ExitCode.SCHEME_REJECTED,
2137
+ result: err("SCHEME_REJECTED", { message: "No project specified. Use --project or set project in transcript frontmatter." })
2138
+ };
2139
+ }
2140
+ const projectDir = join14(input.vault, "projects", projectSlug);
2141
+ if (!existsSync2(projectDir) || !statSync(projectDir).isDirectory()) {
2142
+ return {
2143
+ exitCode: ExitCode.PROJECT_NOT_FOUND,
2144
+ result: err("PROJECT_NOT_FOUND", { project: projectSlug })
2145
+ };
2146
+ }
2147
+ const filename = input.transcript.split("/").pop();
2148
+ const date = extractDate(filename);
2149
+ if (!date) {
2150
+ return {
2151
+ exitCode: ExitCode.SCHEME_REJECTED,
2152
+ result: err("SCHEME_REJECTED", { message: `Cannot extract date from filename: ${filename}` })
2153
+ };
2154
+ }
2155
+ const workSlug = input.slug || extractSlugFromFilename(filename);
2156
+ const dirName = `${date}-${workSlug}`;
2157
+ const workDir = join14(projectDir, "work", dirName);
2158
+ const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
2159
+ const relSpecPath = `${relWorkDir}/spec.md`;
2160
+ if (existsSync2(workDir)) {
2161
+ return {
2162
+ exitCode: ExitCode.OK,
2163
+ result: ok({
2164
+ workItemPath: relWorkDir,
2165
+ specPath: relSpecPath,
2166
+ source: input.transcript,
2167
+ humanHint: `work item already exists: ${relWorkDir}`
2168
+ })
2169
+ };
2170
+ }
2171
+ await mkdir7(workDir, { recursive: true });
2172
+ const kind = fm.ok && typeof fm.data.kind === "string" ? fm.data.kind : "task";
2173
+ const specLines = [
2174
+ "---",
2175
+ `source: ${input.transcript}`,
2176
+ `status: planned`,
2177
+ `kind: ${kind}`,
2178
+ `created: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
2179
+ "---",
2180
+ "",
2181
+ `# ${workSlug}`,
2182
+ "",
2183
+ `Claimed from ${input.transcript}`,
2184
+ ""
2185
+ ];
2186
+ await writeFile6(join14(workDir, "spec.md"), specLines.join("\n"), "utf8");
2187
+ appendLastOp(input.vault, {
2188
+ operation: "claim",
2189
+ summary: `claimed ${input.transcript} \u2192 ${relWorkDir}`,
2190
+ files: [relSpecPath],
2191
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2192
+ });
2193
+ return {
2194
+ exitCode: ExitCode.OK,
2195
+ result: ok({
2196
+ workItemPath: relWorkDir,
2197
+ specPath: relSpecPath,
2198
+ source: input.transcript,
2199
+ humanHint: `claimed ${input.transcript} \u2192 ${relWorkDir}`
2200
+ })
2201
+ };
2202
+ }
2203
+
2103
2204
  // src/commands/pagesize.ts
2104
2205
  async function runPagesize(input) {
2105
2206
  const scan = await scanVault(input.vault);
@@ -2117,19 +2218,19 @@ async function runPagesize(input) {
2117
2218
  }
2118
2219
 
2119
2220
  // src/commands/log-rotate.ts
2120
- import { readFile as readFile11, rename as rename3, writeFile as writeFile6, stat as stat5 } from "fs/promises";
2121
- import { join as join14 } from "path";
2221
+ import { readFile as readFile12, rename as rename3, writeFile as writeFile7, stat as stat5 } from "fs/promises";
2222
+ import { join as join15 } from "path";
2122
2223
  var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
2123
2224
  async function runLogRotate(input) {
2124
2225
  try {
2125
- await stat5(join14(input.vault, "SCHEMA.md"));
2226
+ await stat5(join15(input.vault, "SCHEMA.md"));
2126
2227
  } catch {
2127
2228
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
2128
2229
  }
2129
- const logPath = join14(input.vault, "log.md");
2230
+ const logPath = join15(input.vault, "log.md");
2130
2231
  let logText;
2131
2232
  try {
2132
- logText = await readFile11(logPath, "utf8");
2233
+ logText = await readFile12(logPath, "utf8");
2133
2234
  } catch {
2134
2235
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
2135
2236
  }
@@ -2146,7 +2247,7 @@ async function runLogRotate(input) {
2146
2247
  }
2147
2248
  const newestYear = matches[matches.length - 1][1];
2148
2249
  const rotatedName = `log-${newestYear}.md`;
2149
- const rotatedPath = join14(input.vault, rotatedName);
2250
+ const rotatedPath = join15(input.vault, rotatedName);
2150
2251
  try {
2151
2252
  await rename3(logPath, rotatedPath);
2152
2253
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -2158,7 +2259,7 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
2158
2259
 
2159
2260
  - Previous log moved to ${rotatedName}
2160
2261
  `;
2161
- await writeFile6(logPath, fresh, "utf8");
2262
+ await writeFile7(logPath, fresh, "utf8");
2162
2263
  } catch (e) {
2163
2264
  return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
2164
2265
  }
@@ -2172,9 +2273,9 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
2172
2273
  }
2173
2274
 
2174
2275
  // src/commands/lint.ts
2175
- import { existsSync as existsSync2 } from "fs";
2176
- import { readFile as readFile13, writeFile as writeFile7 } from "fs/promises";
2177
- import { join as join17 } from "path";
2276
+ import { existsSync as existsSync3 } from "fs";
2277
+ import { readFile as readFile14, writeFile as writeFile8 } from "fs/promises";
2278
+ import { join as join18 } from "path";
2178
2279
 
2179
2280
  // src/commands/topic-map-check.ts
2180
2281
  var DEFAULT_THRESHOLD = 200;
@@ -2196,13 +2297,13 @@ async function runTopicMapCheck(input) {
2196
2297
  }
2197
2298
 
2198
2299
  // src/commands/index-link-format.ts
2199
- import { readFile as readFile12 } from "fs/promises";
2200
- import { join as join15 } from "path";
2300
+ import { readFile as readFile13 } from "fs/promises";
2301
+ import { join as join16 } from "path";
2201
2302
  var MD_LINK_RE = /\[[^\[\]]+\]\([^)]+\.md\)/;
2202
2303
  async function runIndexLinkFormat(input) {
2203
2304
  let text = "";
2204
2305
  try {
2205
- text = await readFile12(join15(input.vault, "index.md"), "utf8");
2306
+ text = await readFile13(join16(input.vault, "index.md"), "utf8");
2206
2307
  } catch {
2207
2308
  }
2208
2309
  const markdown_links = [];
@@ -2216,7 +2317,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
2216
2317
 
2217
2318
  // src/commands/dedup.ts
2218
2319
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
2219
- import { join as join16 } from "path";
2320
+ import { join as join17 } from "path";
2220
2321
  async function runDedup(input) {
2221
2322
  const scan = await scanVault(input.vault);
2222
2323
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -2244,7 +2345,7 @@ async function runDedup(input) {
2244
2345
  }
2245
2346
  }
2246
2347
  for (const page of scan.data.typedKnowledge) {
2247
- const text = readFileSync3(join16(input.vault, page.relPath), "utf-8");
2348
+ const text = readFileSync3(join17(input.vault, page.relPath), "utf-8");
2248
2349
  let updated = text;
2249
2350
  let changed = false;
2250
2351
  for (const [oldPath, newPath] of replacements) {
@@ -2262,12 +2363,12 @@ async function runDedup(input) {
2262
2363
  }
2263
2364
  }
2264
2365
  if (changed) {
2265
- writeFileSync2(join16(input.vault, page.relPath), updated);
2366
+ writeFileSync2(join17(input.vault, page.relPath), updated);
2266
2367
  rewired.push(page.relPath);
2267
2368
  }
2268
2369
  }
2269
2370
  for (const [oldPath] of replacements) {
2270
- const fullPath = join16(input.vault, oldPath);
2371
+ const fullPath = join17(input.vault, oldPath);
2271
2372
  try {
2272
2373
  unlinkSync2(fullPath);
2273
2374
  removed.push(oldPath);
@@ -2414,7 +2515,7 @@ async function runLint(input) {
2414
2515
  let rawPath = entry.replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
2415
2516
  rawPath = rawPath.replace(/^\^\[/, "").replace(/\]$/, "");
2416
2517
  if (!rawPath.startsWith("raw/") && !rawPath.startsWith("_archive/raw/")) continue;
2417
- if (!existsSync2(join17(input.vault, rawPath)) && !existsSync2(join17(input.vault, rawPath + ".md")) && !rawPath.startsWith("_archive/") && !existsSync2(join17(input.vault, "_archive", rawPath)) && !existsSync2(join17(input.vault, "_archive", rawPath + ".md"))) {
2518
+ if (!existsSync3(join18(input.vault, rawPath)) && !existsSync3(join18(input.vault, rawPath + ".md")) && !rawPath.startsWith("_archive/") && !existsSync3(join18(input.vault, "_archive", rawPath)) && !existsSync3(join18(input.vault, "_archive", rawPath + ".md"))) {
2418
2519
  brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
2419
2520
  }
2420
2521
  }
@@ -2499,11 +2600,11 @@ async function runLint(input) {
2499
2600
  const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
2500
2601
  if (!slugMatch) continue;
2501
2602
  const slug = slugMatch[1];
2502
- const knowledgePath = join17(input.vault, "projects", slug, "knowledge.md");
2503
- if (!existsSync2(knowledgePath)) continue;
2603
+ const knowledgePath = join18(input.vault, "projects", slug, "knowledge.md");
2604
+ if (!existsSync3(knowledgePath)) continue;
2504
2605
  const pageRef = page.relPath.replace(/\.md$/, "");
2505
2606
  try {
2506
- const knowledgeContent = await readFile13(knowledgePath, "utf8");
2607
+ const knowledgeContent = await readFile14(knowledgePath, "utf8");
2507
2608
  if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
2508
2609
  orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
2509
2610
  }
@@ -2518,7 +2619,7 @@ async function runLint(input) {
2518
2619
  for (const relPath of legacyPages) {
2519
2620
  try {
2520
2621
  const absPath = `${input.vault}/${relPath}`;
2521
- const raw = await readFile13(absPath, "utf8");
2622
+ const raw = await readFile14(absPath, "utf8");
2522
2623
  const split = splitFrontmatter(raw);
2523
2624
  if (!split.ok) {
2524
2625
  unresolved.push(relPath);
@@ -2596,7 +2697,7 @@ async function runLint(input) {
2596
2697
  ${rawFm}
2597
2698
  ---
2598
2699
  ${newBody}`;
2599
- await writeFile7(absPath, newContent, "utf8");
2700
+ await writeFile8(absPath, newContent, "utf8");
2600
2701
  fixed.push(relPath);
2601
2702
  } catch {
2602
2703
  unresolved.push(relPath);
@@ -2613,7 +2714,7 @@ ${newBody}`;
2613
2714
  for (const relPath of noOverview) {
2614
2715
  try {
2615
2716
  const absPath = `${input.vault}/${relPath}`;
2616
- const raw = await readFile13(absPath, "utf8");
2717
+ const raw = await readFile14(absPath, "utf8");
2617
2718
  const split = splitFrontmatter(raw);
2618
2719
  if (!split.ok) {
2619
2720
  unresolved.push(relPath);
@@ -2634,7 +2735,7 @@ ${rawFm}
2634
2735
  ${overviewSection}
2635
2736
 
2636
2737
  ${trimmedBody}`;
2637
- await writeFile7(absPath, newContent, "utf8");
2738
+ await writeFile8(absPath, newContent, "utf8");
2638
2739
  fixed.push(relPath);
2639
2740
  } catch {
2640
2741
  unresolved.push(relPath);
@@ -2650,7 +2751,7 @@ ${trimmedBody}`;
2650
2751
  for (const relPath of missingTldrFlags) {
2651
2752
  try {
2652
2753
  const absPath = `${input.vault}/${relPath}`;
2653
- const raw = await readFile13(absPath, "utf8");
2754
+ const raw = await readFile14(absPath, "utf8");
2654
2755
  const split = splitFrontmatter(raw);
2655
2756
  if (!split.ok) {
2656
2757
  unresolved.push(relPath);
@@ -2668,7 +2769,7 @@ ${rawFm}
2668
2769
  - Pending summary.
2669
2770
 
2670
2771
  ${trimmedBody}`;
2671
- await writeFile7(absPath, newContent, "utf8");
2772
+ await writeFile8(absPath, newContent, "utf8");
2672
2773
  fixed.push(relPath);
2673
2774
  } catch {
2674
2775
  unresolved.push(relPath);
@@ -2686,7 +2787,7 @@ ${trimmedBody}`;
2686
2787
  for (const relPath of wikilinkCitationFlags) {
2687
2788
  try {
2688
2789
  const absPath = `${input.vault}/${relPath}`;
2689
- const raw = await readFile13(absPath, "utf8");
2790
+ const raw = await readFile14(absPath, "utf8");
2690
2791
  const split = splitFrontmatter(raw);
2691
2792
  if (!split.ok) {
2692
2793
  unresolved.push(relPath);
@@ -2750,7 +2851,7 @@ ${trimmedBody}`;
2750
2851
  ${rawFm}
2751
2852
  ---
2752
2853
  ${newBody}`;
2753
- await writeFile7(absPath, newContent, "utf8");
2854
+ await writeFile8(absPath, newContent, "utf8");
2754
2855
  wikilinkFixed.push(relPath);
2755
2856
  } catch {
2756
2857
  unresolved.push(relPath);
@@ -2807,14 +2908,14 @@ ${newBody}`;
2807
2908
  }
2808
2909
 
2809
2910
  // src/commands/config.ts
2810
- import { readFile as readFile14 } from "fs/promises";
2811
- import { existsSync as existsSync3 } from "fs";
2812
- import { join as join18 } from "path";
2911
+ import { readFile as readFile15 } from "fs/promises";
2912
+ import { existsSync as existsSync4 } from "fs";
2913
+ import { join as join19 } from "path";
2813
2914
  function validateKey(key) {
2814
2915
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
2815
2916
  }
2816
2917
  function configPath(home) {
2817
- return join18(home, ".skillwiki", ".env");
2918
+ return join19(home, ".skillwiki", ".env");
2818
2919
  }
2819
2920
  async function runConfigGet(input) {
2820
2921
  if (!validateKey(input.key)) {
@@ -2832,7 +2933,7 @@ async function runConfigSet(input) {
2832
2933
  try {
2833
2934
  let originalContent;
2834
2935
  try {
2835
- originalContent = await readFile14(filePath, "utf8");
2936
+ originalContent = await readFile15(filePath, "utf8");
2836
2937
  } catch {
2837
2938
  }
2838
2939
  const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
@@ -2864,17 +2965,17 @@ async function runConfigList(input) {
2864
2965
  }
2865
2966
  async function runConfigPath(input) {
2866
2967
  const filePath = configPath(input.home);
2867
- return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync3(filePath), humanHint: filePath }) };
2968
+ return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync4(filePath), humanHint: filePath }) };
2868
2969
  }
2869
2970
 
2870
2971
  // src/commands/doctor.ts
2871
- import { existsSync as existsSync5, lstatSync, readlinkSync, readdirSync, statSync } from "fs";
2872
- import { join as join21, resolve as resolve4 } from "path";
2972
+ import { existsSync as existsSync6, lstatSync, readlinkSync, readdirSync, statSync as statSync2 } from "fs";
2973
+ import { join as join22, resolve as resolve4 } from "path";
2873
2974
  import { execSync } from "child_process";
2874
2975
 
2875
2976
  // src/utils/auto-update.ts
2876
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2877
- import { join as join19, dirname as dirname8 } from "path";
2977
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2978
+ import { join as join20, dirname as dirname8 } from "path";
2878
2979
  import { spawn } from "child_process";
2879
2980
 
2880
2981
  // src/utils/update-consts.ts
@@ -2885,7 +2986,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
2885
2986
 
2886
2987
  // src/utils/auto-update.ts
2887
2988
  function cachePath(home) {
2888
- return join19(home, ".skillwiki", CACHE_FILENAME);
2989
+ return join20(home, ".skillwiki", CACHE_FILENAME);
2889
2990
  }
2890
2991
  function readCacheRaw(home) {
2891
2992
  try {
@@ -2923,7 +3024,7 @@ function triggerAutoUpdate(home, currentVersion) {
2923
3024
  const { isStale } = readCache(home);
2924
3025
  if (!isStale) return;
2925
3026
  const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
2926
- if (!existsSync4(bgScript)) return;
3027
+ if (!existsSync5(bgScript)) return;
2927
3028
  const child = spawn(process.execPath, [bgScript, home, currentVersion], {
2928
3029
  detached: true,
2929
3030
  stdio: "ignore"
@@ -2935,12 +3036,12 @@ function triggerAutoUpdate(home, currentVersion) {
2935
3036
 
2936
3037
  // src/utils/plugin-registry.ts
2937
3038
  import { readFileSync as readFileSync5 } from "fs";
2938
- import { join as join20 } from "path";
2939
- var REGISTRY_PATH = join20(".claude", "plugins", "installed_plugins.json");
3039
+ import { join as join21 } from "path";
3040
+ var REGISTRY_PATH = join21(".claude", "plugins", "installed_plugins.json");
2940
3041
  var PLUGIN_KEY = "skillwiki@llm-wiki";
2941
3042
  function readInstalledPlugins(home) {
2942
3043
  try {
2943
- const raw = readFileSync5(join20(home, REGISTRY_PATH), "utf8");
3044
+ const raw = readFileSync5(join21(home, REGISTRY_PATH), "utf8");
2944
3045
  return JSON.parse(raw);
2945
3046
  } catch {
2946
3047
  return null;
@@ -2983,13 +3084,13 @@ function detectCliChannels(argv, home) {
2983
3084
  }
2984
3085
  const plugin = findPlugin(home);
2985
3086
  if (plugin) {
2986
- const pluginBin = join21(plugin.installPath, "bin", "skillwiki");
2987
- if (existsSync5(pluginBin)) {
3087
+ const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
3088
+ if (existsSync6(pluginBin)) {
2988
3089
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
2989
3090
  }
2990
3091
  }
2991
- const installBin = join21(home, ".claude", "skills", "bin", "skillwiki");
2992
- if (existsSync5(installBin)) {
3092
+ const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
3093
+ if (existsSync6(installBin)) {
2993
3094
  channels.push({ name: "install", path: installBin, isDevLink: false });
2994
3095
  }
2995
3096
  return channels;
@@ -3041,7 +3142,7 @@ function checkCliChannels(argv, home) {
3041
3142
  }
3042
3143
  async function checkConfigFile(home) {
3043
3144
  const cfgPath = configPath(home);
3044
- if (!existsSync5(cfgPath)) {
3145
+ if (!existsSync6(cfgPath)) {
3045
3146
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
3046
3147
  }
3047
3148
  try {
@@ -3056,7 +3157,7 @@ function checkWikiPathExists(resolvedPath) {
3056
3157
  if (resolvedPath === void 0) {
3057
3158
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
3058
3159
  }
3059
- if (existsSync5(resolvedPath) && statSync(resolvedPath).isDirectory()) {
3160
+ if (existsSync6(resolvedPath) && statSync2(resolvedPath).isDirectory()) {
3060
3161
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
3061
3162
  }
3062
3163
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -3065,13 +3166,13 @@ function checkVaultStructure(resolvedPath) {
3065
3166
  if (resolvedPath === void 0) {
3066
3167
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
3067
3168
  }
3068
- if (!existsSync5(resolvedPath)) {
3169
+ if (!existsSync6(resolvedPath)) {
3069
3170
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
3070
3171
  }
3071
3172
  const missing = [];
3072
- if (!existsSync5(join21(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
3173
+ if (!existsSync6(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
3073
3174
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
3074
- if (!existsSync5(join21(resolvedPath, dir))) missing.push(dir + "/");
3175
+ if (!existsSync6(join22(resolvedPath, dir))) missing.push(dir + "/");
3075
3176
  }
3076
3177
  if (missing.length === 0) {
3077
3178
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -3079,8 +3180,8 @@ function checkVaultStructure(resolvedPath) {
3079
3180
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
3080
3181
  }
3081
3182
  function checkSkillsInstalled(home, cwd) {
3082
- const srcDir = cwd ? join21(cwd, "packages", "skills") : void 0;
3083
- if (srcDir && existsSync5(srcDir)) {
3183
+ const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
3184
+ if (srcDir && existsSync6(srcDir)) {
3084
3185
  const found = findSkillMd(srcDir);
3085
3186
  if (found.length > 0) {
3086
3187
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -3093,8 +3194,8 @@ function checkSkillsInstalled(home, cwd) {
3093
3194
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
3094
3195
  }
3095
3196
  }
3096
- const skillsDir = join21(home, ".claude", "skills");
3097
- if (existsSync5(skillsDir)) {
3197
+ const skillsDir = join22(home, ".claude", "skills");
3198
+ if (existsSync6(skillsDir)) {
3098
3199
  const found = findSkillMd(skillsDir);
3099
3200
  if (found.length > 0) {
3100
3201
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -3104,10 +3205,10 @@ function checkSkillsInstalled(home, cwd) {
3104
3205
  }
3105
3206
  function checkDuplicateSkills(home) {
3106
3207
  const plugin = findPlugin(home);
3107
- const skillsDir = join21(home, ".claude", "skills");
3208
+ const skillsDir = join22(home, ".claude", "skills");
3108
3209
  const agentSkillDirs = [
3109
- { label: "~/.codex/skills/", path: join21(home, ".codex", "skills") },
3110
- { label: "~/.agents/skills/", path: join21(home, ".agents", "skills") }
3210
+ { label: "~/.codex/skills/", path: join22(home, ".codex", "skills") },
3211
+ { label: "~/.agents/skills/", path: join22(home, ".agents", "skills") }
3111
3212
  ];
3112
3213
  if (!plugin) {
3113
3214
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -3184,8 +3285,8 @@ async function checkProfiles(home) {
3184
3285
  }
3185
3286
  async function checkProjectLocalOverride(cwd) {
3186
3287
  const dir = cwd ?? process.cwd();
3187
- const envPath = join21(dir, ".skillwiki", ".env");
3188
- if (existsSync5(envPath)) {
3288
+ const envPath = join22(dir, ".skillwiki", ".env");
3289
+ if (existsSync6(envPath)) {
3189
3290
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
3190
3291
  }
3191
3292
  return check("pass", "project_local", "Project-local config", "None");
@@ -3194,7 +3295,7 @@ function checkVaultGitRemote(resolvedPath) {
3194
3295
  if (resolvedPath === void 0) {
3195
3296
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
3196
3297
  }
3197
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3298
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3198
3299
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
3199
3300
  }
3200
3301
  try {
@@ -3217,9 +3318,9 @@ function checkObsidianTemplates(resolvedPath) {
3217
3318
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
3218
3319
  }
3219
3320
  const missing = [];
3220
- if (!existsSync5(join21(resolvedPath, "_Templates"))) missing.push("_Templates/");
3221
- if (!existsSync5(join21(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
3222
- if (!existsSync5(join21(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
3321
+ if (!existsSync6(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
3322
+ if (!existsSync6(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
3323
+ if (!existsSync6(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
3223
3324
  if (missing.length === 0) {
3224
3325
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
3225
3326
  }
@@ -3229,8 +3330,8 @@ function checkDotStoreClean(resolvedPath) {
3229
3330
  if (resolvedPath === void 0) {
3230
3331
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
3231
3332
  }
3232
- const rawDir = join21(resolvedPath, "raw");
3233
- if (!existsSync5(rawDir)) {
3333
+ const rawDir = join22(resolvedPath, "raw");
3334
+ if (!existsSync6(rawDir)) {
3234
3335
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
3235
3336
  }
3236
3337
  const found = [];
@@ -3245,7 +3346,7 @@ function checkDotStoreClean(resolvedPath) {
3245
3346
  if (entry.name === ".DS_Store") {
3246
3347
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
3247
3348
  } else if (entry.isDirectory()) {
3248
- walk2(join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3349
+ walk2(join22(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3249
3350
  }
3250
3351
  }
3251
3352
  })(rawDir, "");
@@ -3258,7 +3359,7 @@ function checkSyncLastPush(resolvedPath) {
3258
3359
  if (resolvedPath === void 0) {
3259
3360
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
3260
3361
  }
3261
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3362
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3262
3363
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
3263
3364
  }
3264
3365
  let timestamp;
@@ -3300,9 +3401,9 @@ function findSkillMd(dir) {
3300
3401
  }
3301
3402
  for (const entry of entries) {
3302
3403
  if (entry.isFile() && entry.name === "SKILL.md") {
3303
- results.push(join21(dir, entry.name));
3404
+ results.push(join22(dir, entry.name));
3304
3405
  } else if (entry.isDirectory()) {
3305
- results.push(...findSkillMd(join21(dir, entry.name)));
3406
+ results.push(...findSkillMd(join22(dir, entry.name)));
3306
3407
  }
3307
3408
  }
3308
3409
  return results;
@@ -3316,7 +3417,7 @@ function findSkillNames(dir) {
3316
3417
  return results;
3317
3418
  }
3318
3419
  for (const entry of entries) {
3319
- if (entry.isDirectory() && existsSync5(join21(dir, entry.name, "SKILL.md"))) {
3420
+ if (entry.isDirectory() && existsSync6(join22(dir, entry.name, "SKILL.md"))) {
3320
3421
  results.push(entry.name);
3321
3422
  }
3322
3423
  }
@@ -3369,8 +3470,8 @@ async function runDoctor(input) {
3369
3470
  }
3370
3471
 
3371
3472
  // src/commands/archive.ts
3372
- import { rename as rename4, mkdir as mkdir7, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
3373
- import { join as join22, dirname as dirname9 } from "path";
3473
+ import { rename as rename4, mkdir as mkdir8, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
3474
+ import { join as join23, dirname as dirname9 } from "path";
3374
3475
  async function runArchive(input) {
3375
3476
  const scan = await scanVault(input.vault);
3376
3477
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -3386,25 +3487,25 @@ async function runArchive(input) {
3386
3487
  }
3387
3488
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
3388
3489
  if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
3389
- const archivePath = join22("_archive", relPath).replace(/\\/g, "/");
3390
- await mkdir7(dirname9(join22(input.vault, archivePath)), { recursive: true });
3490
+ const archivePath = join23("_archive", relPath).replace(/\\/g, "/");
3491
+ await mkdir8(dirname9(join23(input.vault, archivePath)), { recursive: true });
3391
3492
  let indexUpdated = false;
3392
3493
  if (!isRaw) {
3393
- const indexPath = join22(input.vault, "index.md");
3494
+ const indexPath = join23(input.vault, "index.md");
3394
3495
  try {
3395
- const idx = await readFile15(indexPath, "utf8");
3496
+ const idx = await readFile16(indexPath, "utf8");
3396
3497
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
3397
3498
  const originalLines = idx.split("\n");
3398
3499
  const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
3399
3500
  if (filtered.length !== originalLines.length) {
3400
- await writeFile8(indexPath, filtered.join("\n"), "utf8");
3501
+ await writeFile9(indexPath, filtered.join("\n"), "utf8");
3401
3502
  indexUpdated = true;
3402
3503
  }
3403
3504
  } catch (e) {
3404
3505
  if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
3405
3506
  }
3406
3507
  }
3407
- await rename4(join22(input.vault, relPath), join22(input.vault, archivePath));
3508
+ await rename4(join23(input.vault, relPath), join23(input.vault, archivePath));
3408
3509
  appendLastOp(input.vault, {
3409
3510
  operation: "archive",
3410
3511
  summary: `moved ${relPath} to ${archivePath}`,
@@ -3416,7 +3517,7 @@ async function runArchive(input) {
3416
3517
 
3417
3518
  // src/commands/drift.ts
3418
3519
  import { createHash as createHash2 } from "crypto";
3419
- import { writeFile as writeFile9 } from "fs/promises";
3520
+ import { writeFile as writeFile10 } from "fs/promises";
3420
3521
 
3421
3522
  // src/utils/fetch.ts
3422
3523
  async function controlledFetch(url, opts) {
@@ -3503,7 +3604,7 @@ async function runDrift(input) {
3503
3604
  ${newFm}
3504
3605
  ---
3505
3606
  ${body}`;
3506
- await writeFile9(raw.absPath, newText, "utf8");
3607
+ await writeFile10(raw.absPath, newText, "utf8");
3507
3608
  results.push({
3508
3609
  raw_path: raw.relPath,
3509
3610
  source_url: sourceUrl,
@@ -3546,7 +3647,7 @@ ${body}`;
3546
3647
  }
3547
3648
 
3548
3649
  // src/commands/migrate-citations.ts
3549
- import { writeFile as writeFile10 } from "fs/promises";
3650
+ import { writeFile as writeFile11 } from "fs/promises";
3550
3651
  var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
3551
3652
  function moveMarkersToParagraphEnd(body) {
3552
3653
  const lines = body.split("\n");
@@ -3669,7 +3770,7 @@ ${migratedBody}${newFooter}`;
3669
3770
  continue;
3670
3771
  }
3671
3772
  if (!input.dryRun) {
3672
- await writeFile10(page.absPath, newText, "utf8");
3773
+ await writeFile11(page.absPath, newText, "utf8");
3673
3774
  }
3674
3775
  migrated.push(page.relPath);
3675
3776
  }
@@ -3699,7 +3800,7 @@ ${migratedBody}${newFooter}`;
3699
3800
  }
3700
3801
 
3701
3802
  // src/commands/frontmatter-fix.ts
3702
- import { writeFile as writeFile11 } from "fs/promises";
3803
+ import { writeFile as writeFile12 } from "fs/promises";
3703
3804
  function isoToday() {
3704
3805
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3705
3806
  }
@@ -3741,7 +3842,7 @@ ${newBody}`;
3741
3842
  continue;
3742
3843
  }
3743
3844
  if (!input.dryRun) {
3744
- await writeFile11(page.absPath, newText, "utf8");
3845
+ await writeFile12(page.absPath, newText, "utf8");
3745
3846
  }
3746
3847
  fixed.push(page.relPath);
3747
3848
  }
@@ -3774,14 +3875,14 @@ ${newBody}`;
3774
3875
  // src/commands/update.ts
3775
3876
  import { execSync as execSync2 } from "child_process";
3776
3877
  import { readFileSync as readFileSync6 } from "fs";
3777
- import { join as join23 } from "path";
3878
+ import { join as join24 } from "path";
3778
3879
  function resolveGlobalSkillsRoot() {
3779
3880
  try {
3780
3881
  const globalRoot = execSync2("npm root -g", {
3781
3882
  encoding: "utf8",
3782
3883
  timeout: 5e3
3783
3884
  }).trim();
3784
- return join23(globalRoot, "skillwiki", "skills");
3885
+ return join24(globalRoot, "skillwiki", "skills");
3785
3886
  } catch {
3786
3887
  return null;
3787
3888
  }
@@ -3807,7 +3908,7 @@ async function runUpdate(input) {
3807
3908
  );
3808
3909
  const currentVersion = pkg2.version;
3809
3910
  const tag = input.distTag ?? "latest";
3810
- const target = join23(input.home, ".claude", "skills");
3911
+ const target = join24(input.home, ".claude", "skills");
3811
3912
  let latest;
3812
3913
  try {
3813
3914
  latest = execSync2(`npm view skillwiki@${tag} version`, {
@@ -3877,16 +3978,16 @@ async function runUpdate(input) {
3877
3978
 
3878
3979
  // src/commands/self-update.ts
3879
3980
  import { execSync as execSync3 } from "child_process";
3880
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
3881
- import { join as join24 } from "path";
3981
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
3982
+ import { join as join25 } from "path";
3882
3983
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3883
3984
  async function runSelfUpdate(input) {
3884
3985
  const currentVersion = JSON.parse(
3885
3986
  readFileSync7(new URL("../../package.json", import.meta.url), "utf8")
3886
3987
  ).version;
3887
3988
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3888
- const localPkgPath = join24(sourceRoot, "packages", "cli", "package.json");
3889
- const hasLocalSource = existsSync6(localPkgPath);
3989
+ const localPkgPath = join25(sourceRoot, "packages", "cli", "package.json");
3990
+ const hasLocalSource = existsSync7(localPkgPath);
3890
3991
  if (input.check) {
3891
3992
  let availableVersion = null;
3892
3993
  let source;
@@ -4017,10 +4118,10 @@ async function runSelfUpdate(input) {
4017
4118
  }
4018
4119
 
4019
4120
  // src/commands/transcripts.ts
4020
- import { readdir as readdir5, stat as stat6, readFile as readFile16 } from "fs/promises";
4021
- import { join as join25 } from "path";
4121
+ import { readdir as readdir5, stat as stat6, readFile as readFile17 } from "fs/promises";
4122
+ import { join as join26 } from "path";
4022
4123
  async function runTranscripts(input) {
4023
- const dir = join25(input.vault, "raw", "transcripts");
4124
+ const dir = join26(input.vault, "raw", "transcripts");
4024
4125
  let entries;
4025
4126
  try {
4026
4127
  entries = await readdir5(dir, { withFileTypes: true });
@@ -4030,8 +4131,8 @@ async function runTranscripts(input) {
4030
4131
  const transcripts = [];
4031
4132
  for (const entry of entries) {
4032
4133
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4033
- const filePath = join25(dir, entry.name);
4034
- const content = await readFile16(filePath, "utf8");
4134
+ const filePath = join26(dir, entry.name);
4135
+ const content = await readFile17(filePath, "utf8");
4035
4136
  const fm = extractFrontmatter(content);
4036
4137
  if (!fm.ok) continue;
4037
4138
  const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
@@ -4048,12 +4149,12 @@ async function runTranscripts(input) {
4048
4149
  }
4049
4150
 
4050
4151
  // src/commands/project-index.ts
4051
- import { readdir as readdir6, readFile as readFile17, writeFile as writeFile12, mkdir as mkdir8 } from "fs/promises";
4052
- import { join as join26, dirname as dirname10 } from "path";
4152
+ import { readdir as readdir6, readFile as readFile18, writeFile as writeFile13, mkdir as mkdir9 } from "fs/promises";
4153
+ import { join as join27, dirname as dirname10 } from "path";
4053
4154
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
4054
4155
  async function runProjectIndex(input) {
4055
4156
  const slug = input.slug;
4056
- const projectDir = join26(input.vault, "projects", slug);
4157
+ const projectDir = join27(input.vault, "projects", slug);
4057
4158
  try {
4058
4159
  await readdir6(projectDir);
4059
4160
  } catch {
@@ -4064,15 +4165,15 @@ async function runProjectIndex(input) {
4064
4165
  }
4065
4166
  const wikilinkPattern = `[[${slug}]]`;
4066
4167
  const entries = [];
4067
- const compoundDir = join26(input.vault, "projects", slug, "compound");
4168
+ const compoundDir = join27(input.vault, "projects", slug, "compound");
4068
4169
  try {
4069
4170
  const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
4070
4171
  for (const entry of compoundFiles) {
4071
4172
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4072
- const filePath = join26(compoundDir, entry.name);
4173
+ const filePath = join27(compoundDir, entry.name);
4073
4174
  let text;
4074
4175
  try {
4075
- text = await readFile17(filePath, "utf8");
4176
+ text = await readFile18(filePath, "utf8");
4076
4177
  } catch {
4077
4178
  continue;
4078
4179
  }
@@ -4089,16 +4190,16 @@ async function runProjectIndex(input) {
4089
4190
  for (const dir of LAYER2_DIRS) {
4090
4191
  let files;
4091
4192
  try {
4092
- files = await readdir6(join26(input.vault, dir), { withFileTypes: true });
4193
+ files = await readdir6(join27(input.vault, dir), { withFileTypes: true });
4093
4194
  } catch {
4094
4195
  continue;
4095
4196
  }
4096
4197
  for (const entry of files) {
4097
4198
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4098
- const filePath = join26(input.vault, dir, entry.name);
4199
+ const filePath = join27(input.vault, dir, entry.name);
4099
4200
  let text;
4100
4201
  try {
4101
- text = await readFile17(filePath, "utf8");
4202
+ text = await readFile18(filePath, "utf8");
4102
4203
  } catch {
4103
4204
  continue;
4104
4205
  }
@@ -4119,11 +4220,11 @@ async function runProjectIndex(input) {
4119
4220
  const tb = typeOrder[b.type] ?? 99;
4120
4221
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
4121
4222
  });
4122
- const indexPath = join26(projectDir, "knowledge.md");
4223
+ const indexPath = join27(projectDir, "knowledge.md");
4123
4224
  let existing = false;
4124
4225
  let stale = false;
4125
4226
  try {
4126
- const existingText = await readFile17(indexPath, "utf8");
4227
+ const existingText = await readFile18(indexPath, "utf8");
4127
4228
  existing = true;
4128
4229
  const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
4129
4230
  const existingPages = new Set(existingEntries.map((l) => {
@@ -4163,8 +4264,8 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
4163
4264
  }
4164
4265
  if (input.apply) {
4165
4266
  try {
4166
- await mkdir8(dirname10(indexPath), { recursive: true });
4167
- await writeFile12(indexPath, body, "utf8");
4267
+ await mkdir9(dirname10(indexPath), { recursive: true });
4268
+ await writeFile13(indexPath, body, "utf8");
4168
4269
  } catch (e) {
4169
4270
  return {
4170
4271
  exitCode: ExitCode.WRITE_FAILED,
@@ -4192,10 +4293,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
4192
4293
  }
4193
4294
 
4194
4295
  // src/commands/compound.ts
4195
- import { writeFile as writeFile13, mkdir as mkdir9, readdir as readdir7, unlink as unlink2 } from "fs/promises";
4196
- import { join as join27 } from "path";
4197
- import { existsSync as existsSync7 } from "fs";
4198
- import { readFile as readFile18 } from "fs/promises";
4296
+ import { writeFile as writeFile14, mkdir as mkdir10, readdir as readdir7, unlink as unlink2 } from "fs/promises";
4297
+ import { join as join28 } from "path";
4298
+ import { existsSync as existsSync8 } from "fs";
4299
+ import { readFile as readFile19 } from "fs/promises";
4199
4300
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
4200
4301
  var FIELD_RE = {
4201
4302
  improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
@@ -4293,17 +4394,17 @@ function extractRetroFields(date, cycleName, block) {
4293
4394
  };
4294
4395
  }
4295
4396
  async function runCompound(input) {
4296
- const logPath = join27(input.vault, "log.md");
4397
+ const logPath = join28(input.vault, "log.md");
4297
4398
  let logText;
4298
4399
  try {
4299
- logText = await readFile18(logPath, "utf8");
4400
+ logText = await readFile19(logPath, "utf8");
4300
4401
  } catch {
4301
4402
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
4302
4403
  }
4303
4404
  const entries = parseRetroEntries(logText);
4304
4405
  const promoted = [];
4305
4406
  const skipped = [];
4306
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4407
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4307
4408
  for (const entry of entries) {
4308
4409
  const generalizeValue = entry.generalize.trim();
4309
4410
  if (!/^yes/i.test(generalizeValue)) {
@@ -4311,8 +4412,8 @@ async function runCompound(input) {
4311
4412
  continue;
4312
4413
  }
4313
4414
  const slug = slugify(entry.cycleName);
4314
- const compoundPath = join27(compoundDir, `${slug}.md`);
4315
- if (existsSync7(compoundPath)) {
4415
+ const compoundPath = join28(compoundDir, `${slug}.md`);
4416
+ if (existsSync8(compoundPath)) {
4316
4417
  skipped.push(entry.date);
4317
4418
  continue;
4318
4419
  }
@@ -4350,10 +4451,10 @@ async function runCompound(input) {
4350
4451
  ].join("\n");
4351
4452
  const content = frontmatter + "\n" + body;
4352
4453
  if (!input.dryRun) {
4353
- if (!existsSync7(compoundDir)) {
4354
- await mkdir9(compoundDir, { recursive: true });
4454
+ if (!existsSync8(compoundDir)) {
4455
+ await mkdir10(compoundDir, { recursive: true });
4355
4456
  }
4356
- await writeFile13(compoundPath, content, "utf8");
4457
+ await writeFile14(compoundPath, content, "utf8");
4357
4458
  }
4358
4459
  promoted.push(`${slug}.md`);
4359
4460
  }
@@ -4372,16 +4473,16 @@ async function runCompound(input) {
4372
4473
  };
4373
4474
  }
4374
4475
  async function runCompoundDelete(input) {
4375
- const projectDir = join27(input.vault, "projects", input.project);
4376
- if (!existsSync7(projectDir)) {
4476
+ const projectDir = join28(input.vault, "projects", input.project);
4477
+ if (!existsSync8(projectDir)) {
4377
4478
  return {
4378
4479
  exitCode: ExitCode.PROJECT_NOT_FOUND,
4379
4480
  result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
4380
4481
  };
4381
4482
  }
4382
4483
  const entryName = input.entry.replace(/\.md$/, "");
4383
- const compoundPath = join27(projectDir, "compound", `${entryName}.md`);
4384
- if (!existsSync7(compoundPath)) {
4484
+ const compoundPath = join28(projectDir, "compound", `${entryName}.md`);
4485
+ if (!existsSync8(compoundPath)) {
4385
4486
  return {
4386
4487
  exitCode: ExitCode.FILE_NOT_FOUND,
4387
4488
  result: err("FILE_NOT_FOUND", { path: compoundPath })
@@ -4414,8 +4515,8 @@ knowledge.md regenerated`
4414
4515
  };
4415
4516
  }
4416
4517
  async function runCompoundList(input) {
4417
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4418
- if (!existsSync7(compoundDir)) {
4518
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4519
+ if (!existsSync8(compoundDir)) {
4419
4520
  return {
4420
4521
  exitCode: ExitCode.OK,
4421
4522
  result: ok({
@@ -4445,10 +4546,10 @@ could not read compound directory`
4445
4546
  const entries = [];
4446
4547
  for (const dirent of dirents) {
4447
4548
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
4448
- const filePath = join27(compoundDir, dirent.name);
4549
+ const filePath = join28(compoundDir, dirent.name);
4449
4550
  let text;
4450
4551
  try {
4451
- text = await readFile18(filePath, "utf8");
4552
+ text = await readFile19(filePath, "utf8");
4452
4553
  } catch {
4453
4554
  continue;
4454
4555
  }
@@ -4477,9 +4578,9 @@ no compound entries found`;
4477
4578
  }
4478
4579
 
4479
4580
  // src/commands/observe.ts
4480
- import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
4481
- import { existsSync as existsSync8, statSync as statSync2 } from "fs";
4482
- import { join as join28 } from "path";
4581
+ import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
4582
+ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
4583
+ import { join as join29 } from "path";
4483
4584
  import { createHash as createHash3 } from "crypto";
4484
4585
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
4485
4586
  function slugify2(text) {
@@ -4502,15 +4603,15 @@ async function runObserve(input) {
4502
4603
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
4503
4604
  };
4504
4605
  }
4505
- if (!existsSync8(input.vault) || !statSync2(input.vault).isDirectory()) {
4606
+ if (!existsSync9(input.vault) || !statSync3(input.vault).isDirectory()) {
4506
4607
  return {
4507
4608
  exitCode: ExitCode.VAULT_PATH_INVALID,
4508
4609
  result: err("VAULT_PATH_INVALID", { path: input.vault })
4509
4610
  };
4510
4611
  }
4511
- const transcriptsDir = join28(input.vault, "raw", "transcripts");
4612
+ const transcriptsDir = join29(input.vault, "raw", "transcripts");
4512
4613
  try {
4513
- await mkdir10(transcriptsDir, { recursive: true });
4614
+ await mkdir11(transcriptsDir, { recursive: true });
4514
4615
  } catch {
4515
4616
  return {
4516
4617
  exitCode: ExitCode.VAULT_PATH_INVALID,
@@ -4520,7 +4621,7 @@ async function runObserve(input) {
4520
4621
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4521
4622
  const slug = slugify2(input.text);
4522
4623
  const fileName = `${today}-observation-${slug}.md`;
4523
- const filePath = join28(transcriptsDir, fileName);
4624
+ const filePath = join29(transcriptsDir, fileName);
4524
4625
  const body = `
4525
4626
  ${input.text.trim()}
4526
4627
  `;
@@ -4538,7 +4639,7 @@ ${input.text.trim()}
4538
4639
  frontmatterLines.push("---");
4539
4640
  const content = frontmatterLines.join("\n") + body;
4540
4641
  try {
4541
- await writeFile14(filePath, content, "utf8");
4642
+ await writeFile15(filePath, content, "utf8");
4542
4643
  } catch (e) {
4543
4644
  return {
4544
4645
  exitCode: ExitCode.WRITE_FAILED,
@@ -4560,8 +4661,8 @@ ${input.text.trim()}
4560
4661
  }
4561
4662
 
4562
4663
  // src/commands/ingest.ts
4563
- import { readFile as readFile19, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
4564
- import { join as join29 } from "path";
4664
+ import { readFile as readFile20, writeFile as writeFile16, mkdir as mkdir12 } from "fs/promises";
4665
+ import { join as join30 } from "path";
4565
4666
  import { createHash as createHash4 } from "crypto";
4566
4667
  var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
4567
4668
  var TYPE_DIR = {
@@ -4720,7 +4821,7 @@ async function runIngest(input) {
4720
4821
  sourceContent = fetchResult.data.body;
4721
4822
  } else {
4722
4823
  try {
4723
- sourceContent = await readFile19(input.source, "utf8");
4824
+ sourceContent = await readFile20(input.source, "utf8");
4724
4825
  } catch {
4725
4826
  return {
4726
4827
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -4735,8 +4836,8 @@ async function runIngest(input) {
4735
4836
  const rawRelPath = `raw/articles/${slug}.md`;
4736
4837
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
4737
4838
  const typedRelPath = `${typedDir}/${slug}.md`;
4738
- const rawAbsPath = join29(input.vault, rawRelPath);
4739
- const typedAbsPath = join29(input.vault, typedRelPath);
4839
+ const rawAbsPath = join30(input.vault, rawRelPath);
4840
+ const typedAbsPath = join30(input.vault, typedRelPath);
4740
4841
  const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
4741
4842
  const typedContent = buildTypedContent(
4742
4843
  input.title,
@@ -4799,8 +4900,8 @@ async function runIngest(input) {
4799
4900
  };
4800
4901
  }
4801
4902
  try {
4802
- await mkdir11(join29(input.vault, "raw", "articles"), { recursive: true });
4803
- await writeFile15(rawAbsPath, rawContent, "utf8");
4903
+ await mkdir12(join30(input.vault, "raw", "articles"), { recursive: true });
4904
+ await writeFile16(rawAbsPath, rawContent, "utf8");
4804
4905
  } catch (e) {
4805
4906
  return {
4806
4907
  exitCode: ExitCode.WRITE_FAILED,
@@ -4808,8 +4909,8 @@ async function runIngest(input) {
4808
4909
  };
4809
4910
  }
4810
4911
  try {
4811
- await mkdir11(join29(input.vault, typedDir), { recursive: true });
4812
- await writeFile15(typedAbsPath, typedContent, "utf8");
4912
+ await mkdir12(join30(input.vault, typedDir), { recursive: true });
4913
+ await writeFile16(typedAbsPath, typedContent, "utf8");
4813
4914
  } catch (e) {
4814
4915
  return {
4815
4916
  exitCode: ExitCode.WRITE_FAILED,
@@ -4840,7 +4941,7 @@ async function runIngest(input) {
4840
4941
  }
4841
4942
 
4842
4943
  // src/commands/tag-sync.ts
4843
- import { writeFile as writeFile16 } from "fs/promises";
4944
+ import { writeFile as writeFile17 } from "fs/promises";
4844
4945
  var ENUM_MIRRORS = {
4845
4946
  provenance: ["research", "project", "mixed"],
4846
4947
  confidence: ["high", "medium", "low"]
@@ -4955,7 +5056,7 @@ ${newFm}
4955
5056
  ---
4956
5057
  ${body}`;
4957
5058
  if (!input.dryRun) {
4958
- await writeFile16(page.absPath, newText, "utf8");
5059
+ await writeFile17(page.absPath, newText, "utf8");
4959
5060
  }
4960
5061
  synced.push(page.relPath);
4961
5062
  }
@@ -4984,11 +5085,11 @@ ${body}`;
4984
5085
  }
4985
5086
 
4986
5087
  // src/commands/sync.ts
4987
- import { existsSync as existsSync9 } from "fs";
4988
- import { join as join30 } from "path";
5088
+ import { existsSync as existsSync10 } from "fs";
5089
+ import { join as join31 } from "path";
4989
5090
  function runSyncStatus(input) {
4990
5091
  const vault = input.vault;
4991
- if (!existsSync9(join30(vault, ".git"))) {
5092
+ if (!existsSync10(join31(vault, ".git"))) {
4992
5093
  return {
4993
5094
  exitCode: ExitCode.VAULT_PATH_INVALID,
4994
5095
  result: ok({
@@ -5057,7 +5158,7 @@ function runSyncStatus(input) {
5057
5158
  }
5058
5159
  async function runSyncPush(input) {
5059
5160
  const vault = input.vault;
5060
- if (!existsSync9(join30(vault, ".git"))) {
5161
+ if (!existsSync10(join31(vault, ".git"))) {
5061
5162
  return {
5062
5163
  exitCode: ExitCode.VAULT_PATH_INVALID,
5063
5164
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5142,7 +5243,7 @@ async function runSyncPush(input) {
5142
5243
  }
5143
5244
  async function runSyncPull(input) {
5144
5245
  const vault = input.vault;
5145
- if (!existsSync9(join30(vault, ".git"))) {
5246
+ if (!existsSync10(join31(vault, ".git"))) {
5146
5247
  return {
5147
5248
  exitCode: ExitCode.VAULT_PATH_INVALID,
5148
5249
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5217,8 +5318,8 @@ async function runSyncPull(input) {
5217
5318
  }
5218
5319
 
5219
5320
  // src/commands/backup.ts
5220
- import { statSync as statSync3, readdirSync as readdirSync2, readFileSync as readFileSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
5221
- import { join as join31, relative as relative3, dirname as dirname11 } from "path";
5321
+ import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
5322
+ import { join as join32, relative as relative3, dirname as dirname11 } from "path";
5222
5323
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
5223
5324
 
5224
5325
  // src/utils/s3-client.ts
@@ -5242,7 +5343,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
5242
5343
  function* walkMarkdown(dir, base) {
5243
5344
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
5244
5345
  if (SKIP_DIRS.has(entry.name)) continue;
5245
- const full = join31(dir, entry.name);
5346
+ const full = join32(dir, entry.name);
5246
5347
  if (entry.isDirectory()) {
5247
5348
  yield* walkMarkdown(full, base);
5248
5349
  } else if (entry.name.endsWith(".md")) {
@@ -5265,8 +5366,8 @@ async function runBackupSync(input) {
5265
5366
  let failed = 0;
5266
5367
  const files = [...walkMarkdown(input.vault, input.vault)];
5267
5368
  for (const relPath of files) {
5268
- const absPath = join31(input.vault, relPath);
5269
- const localStat = statSync3(absPath);
5369
+ const absPath = join32(input.vault, relPath);
5370
+ const localStat = statSync4(absPath);
5270
5371
  let needsUpload = true;
5271
5372
  try {
5272
5373
  const head = await client.send(new HeadObjectCommand({ Bucket: input.bucket, Key: relPath }));
@@ -5341,9 +5442,9 @@ async function runBackupRestore(input) {
5341
5442
  const objects = list.Contents ?? [];
5342
5443
  for (const obj of objects) {
5343
5444
  if (!obj.Key) continue;
5344
- const localPath = join31(target, obj.Key);
5445
+ const localPath = join32(target, obj.Key);
5345
5446
  try {
5346
- const localStat = statSync3(localPath);
5447
+ const localStat = statSync4(localPath);
5347
5448
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
5348
5449
  conflicts++;
5349
5450
  continue;
@@ -5387,11 +5488,11 @@ async function runBackupRestore(input) {
5387
5488
  }
5388
5489
 
5389
5490
  // src/commands/status.ts
5390
- import { existsSync as existsSync10, statSync as statSync4 } from "fs";
5391
- import { readFile as readFile20 } from "fs/promises";
5392
- import { join as join32 } from "path";
5491
+ import { existsSync as existsSync11, statSync as statSync5 } from "fs";
5492
+ import { readFile as readFile21 } from "fs/promises";
5493
+ import { join as join33 } from "path";
5393
5494
  async function runStatus(input) {
5394
- if (!existsSync10(input.vault)) {
5495
+ if (!existsSync11(input.vault)) {
5395
5496
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
5396
5497
  }
5397
5498
  const scan = await scanVault(input.vault);
@@ -5416,7 +5517,7 @@ async function runStatus(input) {
5416
5517
  const compound = scan.data.compound.length;
5417
5518
  let schemaVersion = "v1";
5418
5519
  try {
5419
- const schemaContent = await readFile20(join32(input.vault, "SCHEMA.md"), "utf8");
5520
+ const schemaContent = await readFile21(join33(input.vault, "SCHEMA.md"), "utf8");
5420
5521
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
5421
5522
  if (versionMatch) schemaVersion = versionMatch[1];
5422
5523
  } catch {
@@ -5432,7 +5533,7 @@ async function runStatus(input) {
5432
5533
  let maxTime = 0;
5433
5534
  for (const page of allPages) {
5434
5535
  try {
5435
- const st = statSync4(page.absPath);
5536
+ const st = statSync5(page.absPath);
5436
5537
  if (st.mtimeMs > maxTime) {
5437
5538
  maxTime = st.mtimeMs;
5438
5539
  lastModified = st.mtime.toISOString();
@@ -5476,8 +5577,8 @@ async function runStatus(input) {
5476
5577
  }
5477
5578
 
5478
5579
  // src/commands/seed.ts
5479
- import { mkdir as mkdir12, writeFile as writeFile17, stat as stat7 } from "fs/promises";
5480
- import { join as join33 } from "path";
5580
+ import { mkdir as mkdir13, writeFile as writeFile18, stat as stat7 } from "fs/promises";
5581
+ import { join as join34 } from "path";
5481
5582
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5482
5583
  var EXAMPLE_PAGES = {
5483
5584
  "entities/example-project.md": `---
@@ -5546,30 +5647,30 @@ Real sources are immutable after ingestion \u2014 never edit them.
5546
5647
  `;
5547
5648
  async function runSeed(input) {
5548
5649
  try {
5549
- await stat7(join33(input.vault, "SCHEMA.md"));
5650
+ await stat7(join34(input.vault, "SCHEMA.md"));
5550
5651
  } catch {
5551
5652
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
5552
5653
  }
5553
5654
  const created = [];
5554
5655
  const skipped = [];
5555
5656
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
5556
- const absPath = join33(input.vault, relPath);
5657
+ const absPath = join34(input.vault, relPath);
5557
5658
  try {
5558
5659
  await stat7(absPath);
5559
5660
  skipped.push(relPath);
5560
5661
  } catch {
5561
- await mkdir12(join33(absPath, ".."), { recursive: true });
5562
- await writeFile17(absPath, content, "utf8");
5662
+ await mkdir13(join34(absPath, ".."), { recursive: true });
5663
+ await writeFile18(absPath, content, "utf8");
5563
5664
  created.push(relPath);
5564
5665
  }
5565
5666
  }
5566
- const rawPath = join33(input.vault, "raw", "articles", "example-source.md");
5667
+ const rawPath = join34(input.vault, "raw", "articles", "example-source.md");
5567
5668
  try {
5568
5669
  await stat7(rawPath);
5569
5670
  skipped.push("raw/articles/example-source.md");
5570
5671
  } catch {
5571
- await mkdir12(join33(rawPath, ".."), { recursive: true });
5572
- await writeFile17(rawPath, EXAMPLE_RAW, "utf8");
5672
+ await mkdir13(join34(rawPath, ".."), { recursive: true });
5673
+ await writeFile18(rawPath, EXAMPLE_RAW, "utf8");
5573
5674
  created.push("raw/articles/example-source.md");
5574
5675
  }
5575
5676
  if (created.length > 0) {
@@ -5591,9 +5692,9 @@ async function runSeed(input) {
5591
5692
  }
5592
5693
 
5593
5694
  // src/commands/canvas.ts
5594
- import { readFile as readFile21, writeFile as writeFile18 } from "fs/promises";
5595
- import { existsSync as existsSync11 } from "fs";
5596
- import { join as join34 } from "path";
5695
+ import { readFile as readFile22, writeFile as writeFile19 } from "fs/promises";
5696
+ import { existsSync as existsSync12 } from "fs";
5697
+ import { join as join35 } from "path";
5597
5698
  var NODE_WIDTH = 240;
5598
5699
  var NODE_HEIGHT = 60;
5599
5700
  var COLUMN_SPACING = 400;
@@ -5671,8 +5772,8 @@ function buildCanvasEdges(adjacency) {
5671
5772
  return edges;
5672
5773
  }
5673
5774
  async function runCanvasGenerate(input) {
5674
- const graphPath = input.graphPath ?? join34(input.vault, ".skillwiki", "graph.json");
5675
- if (!existsSync11(graphPath)) {
5775
+ const graphPath = input.graphPath ?? join35(input.vault, ".skillwiki", "graph.json");
5776
+ if (!existsSync12(graphPath)) {
5676
5777
  return {
5677
5778
  exitCode: ExitCode.FILE_NOT_FOUND,
5678
5779
  result: err("FILE_NOT_FOUND", {
@@ -5683,7 +5784,7 @@ async function runCanvasGenerate(input) {
5683
5784
  }
5684
5785
  let raw;
5685
5786
  try {
5686
- raw = await readFile21(graphPath, "utf8");
5787
+ raw = await readFile22(graphPath, "utf8");
5687
5788
  } catch (e) {
5688
5789
  return {
5689
5790
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -5709,9 +5810,9 @@ async function runCanvasGenerate(input) {
5709
5810
  const nodes = buildCanvasNodes(paths);
5710
5811
  const edges = buildCanvasEdges(graph.adjacency);
5711
5812
  const canvas = { nodes, edges };
5712
- const outPath = join34(input.vault, "vault-graph.canvas");
5813
+ const outPath = join35(input.vault, "vault-graph.canvas");
5713
5814
  try {
5714
- await writeFile18(outPath, JSON.stringify(canvas, null, 2));
5815
+ await writeFile19(outPath, JSON.stringify(canvas, null, 2));
5715
5816
  } catch (e) {
5716
5817
  return {
5717
5818
  exitCode: ExitCode.WRITE_FAILED,
@@ -5731,8 +5832,8 @@ written: ${outPath}`
5731
5832
  }
5732
5833
 
5733
5834
  // src/commands/query.ts
5734
- import { readFile as readFile22, stat as stat8 } from "fs/promises";
5735
- import { join as join35 } from "path";
5835
+ import { readFile as readFile23, stat as stat8 } from "fs/promises";
5836
+ import { join as join36 } from "path";
5736
5837
  var W_KEYWORD = 2;
5737
5838
  var W_SOURCE_OVERLAP = 4;
5738
5839
  var W_WIKILINK = 3;
@@ -5853,7 +5954,7 @@ function computeKeywordScore(terms, title, tags, body) {
5853
5954
  return score;
5854
5955
  }
5855
5956
  async function loadOrBuildGraph(vault) {
5856
- const graphPath = join35(vault, ".skillwiki", "graph.json");
5957
+ const graphPath = join36(vault, ".skillwiki", "graph.json");
5857
5958
  let needsBuild = false;
5858
5959
  try {
5859
5960
  const fileStat = await stat8(graphPath);
@@ -5867,7 +5968,7 @@ async function loadOrBuildGraph(vault) {
5867
5968
  if (buildResult.exitCode !== 0) return null;
5868
5969
  }
5869
5970
  try {
5870
- const raw = await readFile22(graphPath, "utf8");
5971
+ const raw = await readFile23(graphPath, "utf8");
5871
5972
  return JSON.parse(raw);
5872
5973
  } catch {
5873
5974
  return null;
@@ -5875,14 +5976,14 @@ async function loadOrBuildGraph(vault) {
5875
5976
  }
5876
5977
 
5877
5978
  // src/utils/auto-commit.ts
5878
- import { existsSync as existsSync12 } from "fs";
5879
- import { join as join36 } from "path";
5979
+ import { existsSync as existsSync13 } from "fs";
5980
+ import { join as join37 } from "path";
5880
5981
  async function postCommit(vault, exitCode) {
5881
5982
  if (exitCode !== 0) return;
5882
5983
  const home = process.env.HOME ?? "";
5883
5984
  const dotenv = await parseDotenvFile(configPath(home));
5884
5985
  if (dotenv["AUTO_COMMIT"] === "false") return;
5885
- if (!existsSync12(join36(vault, ".git"))) return;
5986
+ if (!existsSync13(join37(vault, ".git"))) return;
5886
5987
  const lastOps = readLastOp(vault);
5887
5988
  if (lastOps.length === 0) return;
5888
5989
  const porcelain = git(vault, ["status", "--porcelain"]);
@@ -5933,7 +6034,7 @@ program.command("validate <file>").description("validate vault page frontmatter
5933
6034
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
5934
6035
  });
5935
6036
  program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5936
- const out = opts.out ?? join37(vault, ".skillwiki", "graph.json");
6037
+ const out = opts.out ?? join38(vault, ".skillwiki", "graph.json");
5937
6038
  emit(await runGraphBuild({ vault, out }), vault);
5938
6039
  });
5939
6040
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -6044,6 +6145,11 @@ program.command("stale [vault]").description("identify stale transcripts and inc
6044
6145
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6045
6146
  else emit(await runStale({ vault: v.vault, days: opts.days, archive: !!opts.archive, forceScan: !!opts.forceScan }), v.vault);
6046
6147
  });
6148
+ program.command("claim <transcript> [vault]").description("claim an unclaimed transcript by creating a work item with source: link").option("--project <slug>", "project slug (overrides transcript frontmatter)").option("--slug <slug>", "work-item slug (defaults to transcript filename without date/kind prefix)").option("--wiki <name>", "wiki profile name").action(async (transcript, vault, opts) => {
6149
+ const v = await resolveVaultArg(vault, opts.wiki);
6150
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6151
+ else emit(await runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug }), v.vault);
6152
+ });
6047
6153
  program.command("pagesize [vault]").description("report page sizes and flag oversized pages").option("--lines <n>", "max body lines", (s) => parseInt(s, 10), 200).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6048
6154
  const v = await resolveVaultArg(vault, opts.wiki);
6049
6155
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });