skillwiki 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  }
@@ -2466,9 +2567,20 @@ async function runLint(input) {
2466
2567
  workItemDirs.set(dir, pages);
2467
2568
  }
2468
2569
  for (const [dir, pages] of workItemDirs) {
2469
- const hasSpec = pages.some((p) => p.relPath.endsWith("/spec.md"));
2570
+ const specPage = pages.find((p) => p.relPath.endsWith("/spec.md"));
2470
2571
  const hasPlan = pages.some((p) => p.relPath.endsWith("/plan.md"));
2471
- if (hasSpec && !hasPlan) {
2572
+ let specStatus;
2573
+ let specStarted;
2574
+ if (specPage) {
2575
+ const text = await readPage(specPage);
2576
+ const fm = extractFrontmatter(text);
2577
+ if (fm.ok) {
2578
+ specStatus = fm.data.status;
2579
+ specStarted = fm.data.started;
2580
+ }
2581
+ }
2582
+ const isClosed = specStatus === "completed" || specStatus === "abandoned";
2583
+ if (specPage && !hasPlan && !isClosed) {
2472
2584
  const lastSegment = dir.split("/").pop();
2473
2585
  const dateMatch = lastSegment.match(/^(\d{4}-\d{2}-\d{2})/);
2474
2586
  if (dateMatch) {
@@ -2478,13 +2590,8 @@ async function runLint(input) {
2478
2590
  }
2479
2591
  }
2480
2592
  }
2481
- for (const page of pages) {
2482
- if (!page.relPath.endsWith("/spec.md")) continue;
2483
- const text = await readPage(page);
2484
- const fm = extractFrontmatter(text);
2485
- if (fm.ok && fm.data.status === "in-progress" && !fm.data.started) {
2486
- workItemHealth.push(`${page.relPath}: in-progress without started date`);
2487
- }
2593
+ if (specPage && specStatus === "in-progress" && !specStarted) {
2594
+ workItemHealth.push(`${specPage.relPath}: in-progress without started date`);
2488
2595
  }
2489
2596
  }
2490
2597
  if (workItemHealth.length > 0) buckets.work_item_health = workItemHealth;
@@ -2499,11 +2606,11 @@ async function runLint(input) {
2499
2606
  const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
2500
2607
  if (!slugMatch) continue;
2501
2608
  const slug = slugMatch[1];
2502
- const knowledgePath = join17(input.vault, "projects", slug, "knowledge.md");
2503
- if (!existsSync2(knowledgePath)) continue;
2609
+ const knowledgePath = join18(input.vault, "projects", slug, "knowledge.md");
2610
+ if (!existsSync3(knowledgePath)) continue;
2504
2611
  const pageRef = page.relPath.replace(/\.md$/, "");
2505
2612
  try {
2506
- const knowledgeContent = await readFile13(knowledgePath, "utf8");
2613
+ const knowledgeContent = await readFile14(knowledgePath, "utf8");
2507
2614
  if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
2508
2615
  orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
2509
2616
  }
@@ -2518,7 +2625,7 @@ async function runLint(input) {
2518
2625
  for (const relPath of legacyPages) {
2519
2626
  try {
2520
2627
  const absPath = `${input.vault}/${relPath}`;
2521
- const raw = await readFile13(absPath, "utf8");
2628
+ const raw = await readFile14(absPath, "utf8");
2522
2629
  const split = splitFrontmatter(raw);
2523
2630
  if (!split.ok) {
2524
2631
  unresolved.push(relPath);
@@ -2596,7 +2703,7 @@ async function runLint(input) {
2596
2703
  ${rawFm}
2597
2704
  ---
2598
2705
  ${newBody}`;
2599
- await writeFile7(absPath, newContent, "utf8");
2706
+ await writeFile8(absPath, newContent, "utf8");
2600
2707
  fixed.push(relPath);
2601
2708
  } catch {
2602
2709
  unresolved.push(relPath);
@@ -2613,7 +2720,7 @@ ${newBody}`;
2613
2720
  for (const relPath of noOverview) {
2614
2721
  try {
2615
2722
  const absPath = `${input.vault}/${relPath}`;
2616
- const raw = await readFile13(absPath, "utf8");
2723
+ const raw = await readFile14(absPath, "utf8");
2617
2724
  const split = splitFrontmatter(raw);
2618
2725
  if (!split.ok) {
2619
2726
  unresolved.push(relPath);
@@ -2634,7 +2741,7 @@ ${rawFm}
2634
2741
  ${overviewSection}
2635
2742
 
2636
2743
  ${trimmedBody}`;
2637
- await writeFile7(absPath, newContent, "utf8");
2744
+ await writeFile8(absPath, newContent, "utf8");
2638
2745
  fixed.push(relPath);
2639
2746
  } catch {
2640
2747
  unresolved.push(relPath);
@@ -2650,7 +2757,7 @@ ${trimmedBody}`;
2650
2757
  for (const relPath of missingTldrFlags) {
2651
2758
  try {
2652
2759
  const absPath = `${input.vault}/${relPath}`;
2653
- const raw = await readFile13(absPath, "utf8");
2760
+ const raw = await readFile14(absPath, "utf8");
2654
2761
  const split = splitFrontmatter(raw);
2655
2762
  if (!split.ok) {
2656
2763
  unresolved.push(relPath);
@@ -2668,7 +2775,7 @@ ${rawFm}
2668
2775
  - Pending summary.
2669
2776
 
2670
2777
  ${trimmedBody}`;
2671
- await writeFile7(absPath, newContent, "utf8");
2778
+ await writeFile8(absPath, newContent, "utf8");
2672
2779
  fixed.push(relPath);
2673
2780
  } catch {
2674
2781
  unresolved.push(relPath);
@@ -2686,7 +2793,7 @@ ${trimmedBody}`;
2686
2793
  for (const relPath of wikilinkCitationFlags) {
2687
2794
  try {
2688
2795
  const absPath = `${input.vault}/${relPath}`;
2689
- const raw = await readFile13(absPath, "utf8");
2796
+ const raw = await readFile14(absPath, "utf8");
2690
2797
  const split = splitFrontmatter(raw);
2691
2798
  if (!split.ok) {
2692
2799
  unresolved.push(relPath);
@@ -2750,7 +2857,7 @@ ${trimmedBody}`;
2750
2857
  ${rawFm}
2751
2858
  ---
2752
2859
  ${newBody}`;
2753
- await writeFile7(absPath, newContent, "utf8");
2860
+ await writeFile8(absPath, newContent, "utf8");
2754
2861
  wikilinkFixed.push(relPath);
2755
2862
  } catch {
2756
2863
  unresolved.push(relPath);
@@ -2807,14 +2914,14 @@ ${newBody}`;
2807
2914
  }
2808
2915
 
2809
2916
  // 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";
2917
+ import { readFile as readFile15 } from "fs/promises";
2918
+ import { existsSync as existsSync4 } from "fs";
2919
+ import { join as join19 } from "path";
2813
2920
  function validateKey(key) {
2814
2921
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
2815
2922
  }
2816
2923
  function configPath(home) {
2817
- return join18(home, ".skillwiki", ".env");
2924
+ return join19(home, ".skillwiki", ".env");
2818
2925
  }
2819
2926
  async function runConfigGet(input) {
2820
2927
  if (!validateKey(input.key)) {
@@ -2832,7 +2939,7 @@ async function runConfigSet(input) {
2832
2939
  try {
2833
2940
  let originalContent;
2834
2941
  try {
2835
- originalContent = await readFile14(filePath, "utf8");
2942
+ originalContent = await readFile15(filePath, "utf8");
2836
2943
  } catch {
2837
2944
  }
2838
2945
  const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
@@ -2864,17 +2971,17 @@ async function runConfigList(input) {
2864
2971
  }
2865
2972
  async function runConfigPath(input) {
2866
2973
  const filePath = configPath(input.home);
2867
- return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync3(filePath), humanHint: filePath }) };
2974
+ return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync4(filePath), humanHint: filePath }) };
2868
2975
  }
2869
2976
 
2870
2977
  // 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";
2978
+ import { existsSync as existsSync6, lstatSync, readlinkSync, readdirSync, statSync as statSync2 } from "fs";
2979
+ import { join as join22, resolve as resolve4 } from "path";
2873
2980
  import { execSync } from "child_process";
2874
2981
 
2875
2982
  // 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";
2983
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
2984
+ import { join as join20, dirname as dirname8 } from "path";
2878
2985
  import { spawn } from "child_process";
2879
2986
 
2880
2987
  // src/utils/update-consts.ts
@@ -2885,7 +2992,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
2885
2992
 
2886
2993
  // src/utils/auto-update.ts
2887
2994
  function cachePath(home) {
2888
- return join19(home, ".skillwiki", CACHE_FILENAME);
2995
+ return join20(home, ".skillwiki", CACHE_FILENAME);
2889
2996
  }
2890
2997
  function readCacheRaw(home) {
2891
2998
  try {
@@ -2923,7 +3030,7 @@ function triggerAutoUpdate(home, currentVersion) {
2923
3030
  const { isStale } = readCache(home);
2924
3031
  if (!isStale) return;
2925
3032
  const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
2926
- if (!existsSync4(bgScript)) return;
3033
+ if (!existsSync5(bgScript)) return;
2927
3034
  const child = spawn(process.execPath, [bgScript, home, currentVersion], {
2928
3035
  detached: true,
2929
3036
  stdio: "ignore"
@@ -2935,12 +3042,12 @@ function triggerAutoUpdate(home, currentVersion) {
2935
3042
 
2936
3043
  // src/utils/plugin-registry.ts
2937
3044
  import { readFileSync as readFileSync5 } from "fs";
2938
- import { join as join20 } from "path";
2939
- var REGISTRY_PATH = join20(".claude", "plugins", "installed_plugins.json");
3045
+ import { join as join21 } from "path";
3046
+ var REGISTRY_PATH = join21(".claude", "plugins", "installed_plugins.json");
2940
3047
  var PLUGIN_KEY = "skillwiki@llm-wiki";
2941
3048
  function readInstalledPlugins(home) {
2942
3049
  try {
2943
- const raw = readFileSync5(join20(home, REGISTRY_PATH), "utf8");
3050
+ const raw = readFileSync5(join21(home, REGISTRY_PATH), "utf8");
2944
3051
  return JSON.parse(raw);
2945
3052
  } catch {
2946
3053
  return null;
@@ -2983,13 +3090,13 @@ function detectCliChannels(argv, home) {
2983
3090
  }
2984
3091
  const plugin = findPlugin(home);
2985
3092
  if (plugin) {
2986
- const pluginBin = join21(plugin.installPath, "bin", "skillwiki");
2987
- if (existsSync5(pluginBin)) {
3093
+ const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
3094
+ if (existsSync6(pluginBin)) {
2988
3095
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
2989
3096
  }
2990
3097
  }
2991
- const installBin = join21(home, ".claude", "skills", "bin", "skillwiki");
2992
- if (existsSync5(installBin)) {
3098
+ const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
3099
+ if (existsSync6(installBin)) {
2993
3100
  channels.push({ name: "install", path: installBin, isDevLink: false });
2994
3101
  }
2995
3102
  return channels;
@@ -3041,7 +3148,7 @@ function checkCliChannels(argv, home) {
3041
3148
  }
3042
3149
  async function checkConfigFile(home) {
3043
3150
  const cfgPath = configPath(home);
3044
- if (!existsSync5(cfgPath)) {
3151
+ if (!existsSync6(cfgPath)) {
3045
3152
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
3046
3153
  }
3047
3154
  try {
@@ -3056,7 +3163,7 @@ function checkWikiPathExists(resolvedPath) {
3056
3163
  if (resolvedPath === void 0) {
3057
3164
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
3058
3165
  }
3059
- if (existsSync5(resolvedPath) && statSync(resolvedPath).isDirectory()) {
3166
+ if (existsSync6(resolvedPath) && statSync2(resolvedPath).isDirectory()) {
3060
3167
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
3061
3168
  }
3062
3169
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -3065,13 +3172,13 @@ function checkVaultStructure(resolvedPath) {
3065
3172
  if (resolvedPath === void 0) {
3066
3173
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
3067
3174
  }
3068
- if (!existsSync5(resolvedPath)) {
3175
+ if (!existsSync6(resolvedPath)) {
3069
3176
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
3070
3177
  }
3071
3178
  const missing = [];
3072
- if (!existsSync5(join21(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
3179
+ if (!existsSync6(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
3073
3180
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
3074
- if (!existsSync5(join21(resolvedPath, dir))) missing.push(dir + "/");
3181
+ if (!existsSync6(join22(resolvedPath, dir))) missing.push(dir + "/");
3075
3182
  }
3076
3183
  if (missing.length === 0) {
3077
3184
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -3079,8 +3186,8 @@ function checkVaultStructure(resolvedPath) {
3079
3186
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
3080
3187
  }
3081
3188
  function checkSkillsInstalled(home, cwd) {
3082
- const srcDir = cwd ? join21(cwd, "packages", "skills") : void 0;
3083
- if (srcDir && existsSync5(srcDir)) {
3189
+ const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
3190
+ if (srcDir && existsSync6(srcDir)) {
3084
3191
  const found = findSkillMd(srcDir);
3085
3192
  if (found.length > 0) {
3086
3193
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -3093,8 +3200,8 @@ function checkSkillsInstalled(home, cwd) {
3093
3200
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
3094
3201
  }
3095
3202
  }
3096
- const skillsDir = join21(home, ".claude", "skills");
3097
- if (existsSync5(skillsDir)) {
3203
+ const skillsDir = join22(home, ".claude", "skills");
3204
+ if (existsSync6(skillsDir)) {
3098
3205
  const found = findSkillMd(skillsDir);
3099
3206
  if (found.length > 0) {
3100
3207
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -3104,10 +3211,10 @@ function checkSkillsInstalled(home, cwd) {
3104
3211
  }
3105
3212
  function checkDuplicateSkills(home) {
3106
3213
  const plugin = findPlugin(home);
3107
- const skillsDir = join21(home, ".claude", "skills");
3214
+ const skillsDir = join22(home, ".claude", "skills");
3108
3215
  const agentSkillDirs = [
3109
- { label: "~/.codex/skills/", path: join21(home, ".codex", "skills") },
3110
- { label: "~/.agents/skills/", path: join21(home, ".agents", "skills") }
3216
+ { label: "~/.codex/skills/", path: join22(home, ".codex", "skills") },
3217
+ { label: "~/.agents/skills/", path: join22(home, ".agents", "skills") }
3111
3218
  ];
3112
3219
  if (!plugin) {
3113
3220
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -3184,8 +3291,8 @@ async function checkProfiles(home) {
3184
3291
  }
3185
3292
  async function checkProjectLocalOverride(cwd) {
3186
3293
  const dir = cwd ?? process.cwd();
3187
- const envPath = join21(dir, ".skillwiki", ".env");
3188
- if (existsSync5(envPath)) {
3294
+ const envPath = join22(dir, ".skillwiki", ".env");
3295
+ if (existsSync6(envPath)) {
3189
3296
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
3190
3297
  }
3191
3298
  return check("pass", "project_local", "Project-local config", "None");
@@ -3194,7 +3301,7 @@ function checkVaultGitRemote(resolvedPath) {
3194
3301
  if (resolvedPath === void 0) {
3195
3302
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
3196
3303
  }
3197
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3304
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3198
3305
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
3199
3306
  }
3200
3307
  try {
@@ -3217,9 +3324,9 @@ function checkObsidianTemplates(resolvedPath) {
3217
3324
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
3218
3325
  }
3219
3326
  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");
3327
+ if (!existsSync6(join22(resolvedPath, "_Templates"))) missing.push("_Templates/");
3328
+ if (!existsSync6(join22(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
3329
+ if (!existsSync6(join22(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
3223
3330
  if (missing.length === 0) {
3224
3331
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
3225
3332
  }
@@ -3229,8 +3336,8 @@ function checkDotStoreClean(resolvedPath) {
3229
3336
  if (resolvedPath === void 0) {
3230
3337
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
3231
3338
  }
3232
- const rawDir = join21(resolvedPath, "raw");
3233
- if (!existsSync5(rawDir)) {
3339
+ const rawDir = join22(resolvedPath, "raw");
3340
+ if (!existsSync6(rawDir)) {
3234
3341
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
3235
3342
  }
3236
3343
  const found = [];
@@ -3245,7 +3352,7 @@ function checkDotStoreClean(resolvedPath) {
3245
3352
  if (entry.name === ".DS_Store") {
3246
3353
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
3247
3354
  } else if (entry.isDirectory()) {
3248
- walk2(join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3355
+ walk2(join22(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3249
3356
  }
3250
3357
  }
3251
3358
  })(rawDir, "");
@@ -3258,7 +3365,7 @@ function checkSyncLastPush(resolvedPath) {
3258
3365
  if (resolvedPath === void 0) {
3259
3366
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
3260
3367
  }
3261
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3368
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3262
3369
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
3263
3370
  }
3264
3371
  let timestamp;
@@ -3300,9 +3407,9 @@ function findSkillMd(dir) {
3300
3407
  }
3301
3408
  for (const entry of entries) {
3302
3409
  if (entry.isFile() && entry.name === "SKILL.md") {
3303
- results.push(join21(dir, entry.name));
3410
+ results.push(join22(dir, entry.name));
3304
3411
  } else if (entry.isDirectory()) {
3305
- results.push(...findSkillMd(join21(dir, entry.name)));
3412
+ results.push(...findSkillMd(join22(dir, entry.name)));
3306
3413
  }
3307
3414
  }
3308
3415
  return results;
@@ -3316,7 +3423,7 @@ function findSkillNames(dir) {
3316
3423
  return results;
3317
3424
  }
3318
3425
  for (const entry of entries) {
3319
- if (entry.isDirectory() && existsSync5(join21(dir, entry.name, "SKILL.md"))) {
3426
+ if (entry.isDirectory() && existsSync6(join22(dir, entry.name, "SKILL.md"))) {
3320
3427
  results.push(entry.name);
3321
3428
  }
3322
3429
  }
@@ -3369,8 +3476,8 @@ async function runDoctor(input) {
3369
3476
  }
3370
3477
 
3371
3478
  // 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";
3479
+ import { rename as rename4, mkdir as mkdir8, readFile as readFile16, writeFile as writeFile9 } from "fs/promises";
3480
+ import { join as join23, dirname as dirname9 } from "path";
3374
3481
  async function runArchive(input) {
3375
3482
  const scan = await scanVault(input.vault);
3376
3483
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -3386,25 +3493,25 @@ async function runArchive(input) {
3386
3493
  }
3387
3494
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
3388
3495
  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 });
3496
+ const archivePath = join23("_archive", relPath).replace(/\\/g, "/");
3497
+ await mkdir8(dirname9(join23(input.vault, archivePath)), { recursive: true });
3391
3498
  let indexUpdated = false;
3392
3499
  if (!isRaw) {
3393
- const indexPath = join22(input.vault, "index.md");
3500
+ const indexPath = join23(input.vault, "index.md");
3394
3501
  try {
3395
- const idx = await readFile15(indexPath, "utf8");
3502
+ const idx = await readFile16(indexPath, "utf8");
3396
3503
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
3397
3504
  const originalLines = idx.split("\n");
3398
3505
  const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
3399
3506
  if (filtered.length !== originalLines.length) {
3400
- await writeFile8(indexPath, filtered.join("\n"), "utf8");
3507
+ await writeFile9(indexPath, filtered.join("\n"), "utf8");
3401
3508
  indexUpdated = true;
3402
3509
  }
3403
3510
  } catch (e) {
3404
3511
  if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
3405
3512
  }
3406
3513
  }
3407
- await rename4(join22(input.vault, relPath), join22(input.vault, archivePath));
3514
+ await rename4(join23(input.vault, relPath), join23(input.vault, archivePath));
3408
3515
  appendLastOp(input.vault, {
3409
3516
  operation: "archive",
3410
3517
  summary: `moved ${relPath} to ${archivePath}`,
@@ -3416,7 +3523,7 @@ async function runArchive(input) {
3416
3523
 
3417
3524
  // src/commands/drift.ts
3418
3525
  import { createHash as createHash2 } from "crypto";
3419
- import { writeFile as writeFile9 } from "fs/promises";
3526
+ import { writeFile as writeFile10 } from "fs/promises";
3420
3527
 
3421
3528
  // src/utils/fetch.ts
3422
3529
  async function controlledFetch(url, opts) {
@@ -3503,7 +3610,7 @@ async function runDrift(input) {
3503
3610
  ${newFm}
3504
3611
  ---
3505
3612
  ${body}`;
3506
- await writeFile9(raw.absPath, newText, "utf8");
3613
+ await writeFile10(raw.absPath, newText, "utf8");
3507
3614
  results.push({
3508
3615
  raw_path: raw.relPath,
3509
3616
  source_url: sourceUrl,
@@ -3546,7 +3653,7 @@ ${body}`;
3546
3653
  }
3547
3654
 
3548
3655
  // src/commands/migrate-citations.ts
3549
- import { writeFile as writeFile10 } from "fs/promises";
3656
+ import { writeFile as writeFile11 } from "fs/promises";
3550
3657
  var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
3551
3658
  function moveMarkersToParagraphEnd(body) {
3552
3659
  const lines = body.split("\n");
@@ -3669,7 +3776,7 @@ ${migratedBody}${newFooter}`;
3669
3776
  continue;
3670
3777
  }
3671
3778
  if (!input.dryRun) {
3672
- await writeFile10(page.absPath, newText, "utf8");
3779
+ await writeFile11(page.absPath, newText, "utf8");
3673
3780
  }
3674
3781
  migrated.push(page.relPath);
3675
3782
  }
@@ -3699,7 +3806,7 @@ ${migratedBody}${newFooter}`;
3699
3806
  }
3700
3807
 
3701
3808
  // src/commands/frontmatter-fix.ts
3702
- import { writeFile as writeFile11 } from "fs/promises";
3809
+ import { writeFile as writeFile12 } from "fs/promises";
3703
3810
  function isoToday() {
3704
3811
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3705
3812
  }
@@ -3741,7 +3848,7 @@ ${newBody}`;
3741
3848
  continue;
3742
3849
  }
3743
3850
  if (!input.dryRun) {
3744
- await writeFile11(page.absPath, newText, "utf8");
3851
+ await writeFile12(page.absPath, newText, "utf8");
3745
3852
  }
3746
3853
  fixed.push(page.relPath);
3747
3854
  }
@@ -3774,14 +3881,14 @@ ${newBody}`;
3774
3881
  // src/commands/update.ts
3775
3882
  import { execSync as execSync2 } from "child_process";
3776
3883
  import { readFileSync as readFileSync6 } from "fs";
3777
- import { join as join23 } from "path";
3884
+ import { join as join24 } from "path";
3778
3885
  function resolveGlobalSkillsRoot() {
3779
3886
  try {
3780
3887
  const globalRoot = execSync2("npm root -g", {
3781
3888
  encoding: "utf8",
3782
3889
  timeout: 5e3
3783
3890
  }).trim();
3784
- return join23(globalRoot, "skillwiki", "skills");
3891
+ return join24(globalRoot, "skillwiki", "skills");
3785
3892
  } catch {
3786
3893
  return null;
3787
3894
  }
@@ -3807,7 +3914,7 @@ async function runUpdate(input) {
3807
3914
  );
3808
3915
  const currentVersion = pkg2.version;
3809
3916
  const tag = input.distTag ?? "latest";
3810
- const target = join23(input.home, ".claude", "skills");
3917
+ const target = join24(input.home, ".claude", "skills");
3811
3918
  let latest;
3812
3919
  try {
3813
3920
  latest = execSync2(`npm view skillwiki@${tag} version`, {
@@ -3877,16 +3984,16 @@ async function runUpdate(input) {
3877
3984
 
3878
3985
  // src/commands/self-update.ts
3879
3986
  import { execSync as execSync3 } from "child_process";
3880
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
3881
- import { join as join24 } from "path";
3987
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
3988
+ import { join as join25 } from "path";
3882
3989
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3883
3990
  async function runSelfUpdate(input) {
3884
3991
  const currentVersion = JSON.parse(
3885
3992
  readFileSync7(new URL("../../package.json", import.meta.url), "utf8")
3886
3993
  ).version;
3887
3994
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3888
- const localPkgPath = join24(sourceRoot, "packages", "cli", "package.json");
3889
- const hasLocalSource = existsSync6(localPkgPath);
3995
+ const localPkgPath = join25(sourceRoot, "packages", "cli", "package.json");
3996
+ const hasLocalSource = existsSync7(localPkgPath);
3890
3997
  if (input.check) {
3891
3998
  let availableVersion = null;
3892
3999
  let source;
@@ -4017,10 +4124,10 @@ async function runSelfUpdate(input) {
4017
4124
  }
4018
4125
 
4019
4126
  // 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";
4127
+ import { readdir as readdir5, stat as stat6, readFile as readFile17 } from "fs/promises";
4128
+ import { join as join26 } from "path";
4022
4129
  async function runTranscripts(input) {
4023
- const dir = join25(input.vault, "raw", "transcripts");
4130
+ const dir = join26(input.vault, "raw", "transcripts");
4024
4131
  let entries;
4025
4132
  try {
4026
4133
  entries = await readdir5(dir, { withFileTypes: true });
@@ -4030,8 +4137,8 @@ async function runTranscripts(input) {
4030
4137
  const transcripts = [];
4031
4138
  for (const entry of entries) {
4032
4139
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4033
- const filePath = join25(dir, entry.name);
4034
- const content = await readFile16(filePath, "utf8");
4140
+ const filePath = join26(dir, entry.name);
4141
+ const content = await readFile17(filePath, "utf8");
4035
4142
  const fm = extractFrontmatter(content);
4036
4143
  if (!fm.ok) continue;
4037
4144
  const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
@@ -4048,12 +4155,12 @@ async function runTranscripts(input) {
4048
4155
  }
4049
4156
 
4050
4157
  // 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";
4158
+ import { readdir as readdir6, readFile as readFile18, writeFile as writeFile13, mkdir as mkdir9 } from "fs/promises";
4159
+ import { join as join27, dirname as dirname10 } from "path";
4053
4160
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
4054
4161
  async function runProjectIndex(input) {
4055
4162
  const slug = input.slug;
4056
- const projectDir = join26(input.vault, "projects", slug);
4163
+ const projectDir = join27(input.vault, "projects", slug);
4057
4164
  try {
4058
4165
  await readdir6(projectDir);
4059
4166
  } catch {
@@ -4064,15 +4171,15 @@ async function runProjectIndex(input) {
4064
4171
  }
4065
4172
  const wikilinkPattern = `[[${slug}]]`;
4066
4173
  const entries = [];
4067
- const compoundDir = join26(input.vault, "projects", slug, "compound");
4174
+ const compoundDir = join27(input.vault, "projects", slug, "compound");
4068
4175
  try {
4069
4176
  const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
4070
4177
  for (const entry of compoundFiles) {
4071
4178
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4072
- const filePath = join26(compoundDir, entry.name);
4179
+ const filePath = join27(compoundDir, entry.name);
4073
4180
  let text;
4074
4181
  try {
4075
- text = await readFile17(filePath, "utf8");
4182
+ text = await readFile18(filePath, "utf8");
4076
4183
  } catch {
4077
4184
  continue;
4078
4185
  }
@@ -4089,16 +4196,16 @@ async function runProjectIndex(input) {
4089
4196
  for (const dir of LAYER2_DIRS) {
4090
4197
  let files;
4091
4198
  try {
4092
- files = await readdir6(join26(input.vault, dir), { withFileTypes: true });
4199
+ files = await readdir6(join27(input.vault, dir), { withFileTypes: true });
4093
4200
  } catch {
4094
4201
  continue;
4095
4202
  }
4096
4203
  for (const entry of files) {
4097
4204
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4098
- const filePath = join26(input.vault, dir, entry.name);
4205
+ const filePath = join27(input.vault, dir, entry.name);
4099
4206
  let text;
4100
4207
  try {
4101
- text = await readFile17(filePath, "utf8");
4208
+ text = await readFile18(filePath, "utf8");
4102
4209
  } catch {
4103
4210
  continue;
4104
4211
  }
@@ -4119,11 +4226,11 @@ async function runProjectIndex(input) {
4119
4226
  const tb = typeOrder[b.type] ?? 99;
4120
4227
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
4121
4228
  });
4122
- const indexPath = join26(projectDir, "knowledge.md");
4229
+ const indexPath = join27(projectDir, "knowledge.md");
4123
4230
  let existing = false;
4124
4231
  let stale = false;
4125
4232
  try {
4126
- const existingText = await readFile17(indexPath, "utf8");
4233
+ const existingText = await readFile18(indexPath, "utf8");
4127
4234
  existing = true;
4128
4235
  const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
4129
4236
  const existingPages = new Set(existingEntries.map((l) => {
@@ -4163,8 +4270,8 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
4163
4270
  }
4164
4271
  if (input.apply) {
4165
4272
  try {
4166
- await mkdir8(dirname10(indexPath), { recursive: true });
4167
- await writeFile12(indexPath, body, "utf8");
4273
+ await mkdir9(dirname10(indexPath), { recursive: true });
4274
+ await writeFile13(indexPath, body, "utf8");
4168
4275
  } catch (e) {
4169
4276
  return {
4170
4277
  exitCode: ExitCode.WRITE_FAILED,
@@ -4192,10 +4299,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
4192
4299
  }
4193
4300
 
4194
4301
  // 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";
4302
+ import { writeFile as writeFile14, mkdir as mkdir10, readdir as readdir7, unlink as unlink2 } from "fs/promises";
4303
+ import { join as join28 } from "path";
4304
+ import { existsSync as existsSync8 } from "fs";
4305
+ import { readFile as readFile19 } from "fs/promises";
4199
4306
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
4200
4307
  var FIELD_RE = {
4201
4308
  improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
@@ -4293,17 +4400,17 @@ function extractRetroFields(date, cycleName, block) {
4293
4400
  };
4294
4401
  }
4295
4402
  async function runCompound(input) {
4296
- const logPath = join27(input.vault, "log.md");
4403
+ const logPath = join28(input.vault, "log.md");
4297
4404
  let logText;
4298
4405
  try {
4299
- logText = await readFile18(logPath, "utf8");
4406
+ logText = await readFile19(logPath, "utf8");
4300
4407
  } catch {
4301
4408
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
4302
4409
  }
4303
4410
  const entries = parseRetroEntries(logText);
4304
4411
  const promoted = [];
4305
4412
  const skipped = [];
4306
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4413
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4307
4414
  for (const entry of entries) {
4308
4415
  const generalizeValue = entry.generalize.trim();
4309
4416
  if (!/^yes/i.test(generalizeValue)) {
@@ -4311,8 +4418,8 @@ async function runCompound(input) {
4311
4418
  continue;
4312
4419
  }
4313
4420
  const slug = slugify(entry.cycleName);
4314
- const compoundPath = join27(compoundDir, `${slug}.md`);
4315
- if (existsSync7(compoundPath)) {
4421
+ const compoundPath = join28(compoundDir, `${slug}.md`);
4422
+ if (existsSync8(compoundPath)) {
4316
4423
  skipped.push(entry.date);
4317
4424
  continue;
4318
4425
  }
@@ -4350,10 +4457,10 @@ async function runCompound(input) {
4350
4457
  ].join("\n");
4351
4458
  const content = frontmatter + "\n" + body;
4352
4459
  if (!input.dryRun) {
4353
- if (!existsSync7(compoundDir)) {
4354
- await mkdir9(compoundDir, { recursive: true });
4460
+ if (!existsSync8(compoundDir)) {
4461
+ await mkdir10(compoundDir, { recursive: true });
4355
4462
  }
4356
- await writeFile13(compoundPath, content, "utf8");
4463
+ await writeFile14(compoundPath, content, "utf8");
4357
4464
  }
4358
4465
  promoted.push(`${slug}.md`);
4359
4466
  }
@@ -4372,16 +4479,16 @@ async function runCompound(input) {
4372
4479
  };
4373
4480
  }
4374
4481
  async function runCompoundDelete(input) {
4375
- const projectDir = join27(input.vault, "projects", input.project);
4376
- if (!existsSync7(projectDir)) {
4482
+ const projectDir = join28(input.vault, "projects", input.project);
4483
+ if (!existsSync8(projectDir)) {
4377
4484
  return {
4378
4485
  exitCode: ExitCode.PROJECT_NOT_FOUND,
4379
4486
  result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
4380
4487
  };
4381
4488
  }
4382
4489
  const entryName = input.entry.replace(/\.md$/, "");
4383
- const compoundPath = join27(projectDir, "compound", `${entryName}.md`);
4384
- if (!existsSync7(compoundPath)) {
4490
+ const compoundPath = join28(projectDir, "compound", `${entryName}.md`);
4491
+ if (!existsSync8(compoundPath)) {
4385
4492
  return {
4386
4493
  exitCode: ExitCode.FILE_NOT_FOUND,
4387
4494
  result: err("FILE_NOT_FOUND", { path: compoundPath })
@@ -4414,8 +4521,8 @@ knowledge.md regenerated`
4414
4521
  };
4415
4522
  }
4416
4523
  async function runCompoundList(input) {
4417
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4418
- if (!existsSync7(compoundDir)) {
4524
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4525
+ if (!existsSync8(compoundDir)) {
4419
4526
  return {
4420
4527
  exitCode: ExitCode.OK,
4421
4528
  result: ok({
@@ -4445,10 +4552,10 @@ could not read compound directory`
4445
4552
  const entries = [];
4446
4553
  for (const dirent of dirents) {
4447
4554
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
4448
- const filePath = join27(compoundDir, dirent.name);
4555
+ const filePath = join28(compoundDir, dirent.name);
4449
4556
  let text;
4450
4557
  try {
4451
- text = await readFile18(filePath, "utf8");
4558
+ text = await readFile19(filePath, "utf8");
4452
4559
  } catch {
4453
4560
  continue;
4454
4561
  }
@@ -4477,9 +4584,9 @@ no compound entries found`;
4477
4584
  }
4478
4585
 
4479
4586
  // 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";
4587
+ import { mkdir as mkdir11, writeFile as writeFile15 } from "fs/promises";
4588
+ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
4589
+ import { join as join29 } from "path";
4483
4590
  import { createHash as createHash3 } from "crypto";
4484
4591
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
4485
4592
  function slugify2(text) {
@@ -4502,15 +4609,15 @@ async function runObserve(input) {
4502
4609
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
4503
4610
  };
4504
4611
  }
4505
- if (!existsSync8(input.vault) || !statSync2(input.vault).isDirectory()) {
4612
+ if (!existsSync9(input.vault) || !statSync3(input.vault).isDirectory()) {
4506
4613
  return {
4507
4614
  exitCode: ExitCode.VAULT_PATH_INVALID,
4508
4615
  result: err("VAULT_PATH_INVALID", { path: input.vault })
4509
4616
  };
4510
4617
  }
4511
- const transcriptsDir = join28(input.vault, "raw", "transcripts");
4618
+ const transcriptsDir = join29(input.vault, "raw", "transcripts");
4512
4619
  try {
4513
- await mkdir10(transcriptsDir, { recursive: true });
4620
+ await mkdir11(transcriptsDir, { recursive: true });
4514
4621
  } catch {
4515
4622
  return {
4516
4623
  exitCode: ExitCode.VAULT_PATH_INVALID,
@@ -4520,7 +4627,7 @@ async function runObserve(input) {
4520
4627
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4521
4628
  const slug = slugify2(input.text);
4522
4629
  const fileName = `${today}-observation-${slug}.md`;
4523
- const filePath = join28(transcriptsDir, fileName);
4630
+ const filePath = join29(transcriptsDir, fileName);
4524
4631
  const body = `
4525
4632
  ${input.text.trim()}
4526
4633
  `;
@@ -4538,7 +4645,7 @@ ${input.text.trim()}
4538
4645
  frontmatterLines.push("---");
4539
4646
  const content = frontmatterLines.join("\n") + body;
4540
4647
  try {
4541
- await writeFile14(filePath, content, "utf8");
4648
+ await writeFile15(filePath, content, "utf8");
4542
4649
  } catch (e) {
4543
4650
  return {
4544
4651
  exitCode: ExitCode.WRITE_FAILED,
@@ -4560,8 +4667,8 @@ ${input.text.trim()}
4560
4667
  }
4561
4668
 
4562
4669
  // 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";
4670
+ import { readFile as readFile20, writeFile as writeFile16, mkdir as mkdir12 } from "fs/promises";
4671
+ import { join as join30 } from "path";
4565
4672
  import { createHash as createHash4 } from "crypto";
4566
4673
  var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
4567
4674
  var TYPE_DIR = {
@@ -4720,7 +4827,7 @@ async function runIngest(input) {
4720
4827
  sourceContent = fetchResult.data.body;
4721
4828
  } else {
4722
4829
  try {
4723
- sourceContent = await readFile19(input.source, "utf8");
4830
+ sourceContent = await readFile20(input.source, "utf8");
4724
4831
  } catch {
4725
4832
  return {
4726
4833
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -4735,8 +4842,8 @@ async function runIngest(input) {
4735
4842
  const rawRelPath = `raw/articles/${slug}.md`;
4736
4843
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
4737
4844
  const typedRelPath = `${typedDir}/${slug}.md`;
4738
- const rawAbsPath = join29(input.vault, rawRelPath);
4739
- const typedAbsPath = join29(input.vault, typedRelPath);
4845
+ const rawAbsPath = join30(input.vault, rawRelPath);
4846
+ const typedAbsPath = join30(input.vault, typedRelPath);
4740
4847
  const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
4741
4848
  const typedContent = buildTypedContent(
4742
4849
  input.title,
@@ -4799,8 +4906,8 @@ async function runIngest(input) {
4799
4906
  };
4800
4907
  }
4801
4908
  try {
4802
- await mkdir11(join29(input.vault, "raw", "articles"), { recursive: true });
4803
- await writeFile15(rawAbsPath, rawContent, "utf8");
4909
+ await mkdir12(join30(input.vault, "raw", "articles"), { recursive: true });
4910
+ await writeFile16(rawAbsPath, rawContent, "utf8");
4804
4911
  } catch (e) {
4805
4912
  return {
4806
4913
  exitCode: ExitCode.WRITE_FAILED,
@@ -4808,8 +4915,8 @@ async function runIngest(input) {
4808
4915
  };
4809
4916
  }
4810
4917
  try {
4811
- await mkdir11(join29(input.vault, typedDir), { recursive: true });
4812
- await writeFile15(typedAbsPath, typedContent, "utf8");
4918
+ await mkdir12(join30(input.vault, typedDir), { recursive: true });
4919
+ await writeFile16(typedAbsPath, typedContent, "utf8");
4813
4920
  } catch (e) {
4814
4921
  return {
4815
4922
  exitCode: ExitCode.WRITE_FAILED,
@@ -4840,7 +4947,7 @@ async function runIngest(input) {
4840
4947
  }
4841
4948
 
4842
4949
  // src/commands/tag-sync.ts
4843
- import { writeFile as writeFile16 } from "fs/promises";
4950
+ import { writeFile as writeFile17 } from "fs/promises";
4844
4951
  var ENUM_MIRRORS = {
4845
4952
  provenance: ["research", "project", "mixed"],
4846
4953
  confidence: ["high", "medium", "low"]
@@ -4955,7 +5062,7 @@ ${newFm}
4955
5062
  ---
4956
5063
  ${body}`;
4957
5064
  if (!input.dryRun) {
4958
- await writeFile16(page.absPath, newText, "utf8");
5065
+ await writeFile17(page.absPath, newText, "utf8");
4959
5066
  }
4960
5067
  synced.push(page.relPath);
4961
5068
  }
@@ -4984,11 +5091,11 @@ ${body}`;
4984
5091
  }
4985
5092
 
4986
5093
  // src/commands/sync.ts
4987
- import { existsSync as existsSync9 } from "fs";
4988
- import { join as join30 } from "path";
5094
+ import { existsSync as existsSync10 } from "fs";
5095
+ import { join as join31 } from "path";
4989
5096
  function runSyncStatus(input) {
4990
5097
  const vault = input.vault;
4991
- if (!existsSync9(join30(vault, ".git"))) {
5098
+ if (!existsSync10(join31(vault, ".git"))) {
4992
5099
  return {
4993
5100
  exitCode: ExitCode.VAULT_PATH_INVALID,
4994
5101
  result: ok({
@@ -5057,7 +5164,7 @@ function runSyncStatus(input) {
5057
5164
  }
5058
5165
  async function runSyncPush(input) {
5059
5166
  const vault = input.vault;
5060
- if (!existsSync9(join30(vault, ".git"))) {
5167
+ if (!existsSync10(join31(vault, ".git"))) {
5061
5168
  return {
5062
5169
  exitCode: ExitCode.VAULT_PATH_INVALID,
5063
5170
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5142,7 +5249,7 @@ async function runSyncPush(input) {
5142
5249
  }
5143
5250
  async function runSyncPull(input) {
5144
5251
  const vault = input.vault;
5145
- if (!existsSync9(join30(vault, ".git"))) {
5252
+ if (!existsSync10(join31(vault, ".git"))) {
5146
5253
  return {
5147
5254
  exitCode: ExitCode.VAULT_PATH_INVALID,
5148
5255
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5217,8 +5324,8 @@ async function runSyncPull(input) {
5217
5324
  }
5218
5325
 
5219
5326
  // 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";
5327
+ import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
5328
+ import { join as join32, relative as relative3, dirname as dirname11 } from "path";
5222
5329
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
5223
5330
 
5224
5331
  // src/utils/s3-client.ts
@@ -5242,7 +5349,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
5242
5349
  function* walkMarkdown(dir, base) {
5243
5350
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
5244
5351
  if (SKIP_DIRS.has(entry.name)) continue;
5245
- const full = join31(dir, entry.name);
5352
+ const full = join32(dir, entry.name);
5246
5353
  if (entry.isDirectory()) {
5247
5354
  yield* walkMarkdown(full, base);
5248
5355
  } else if (entry.name.endsWith(".md")) {
@@ -5265,8 +5372,8 @@ async function runBackupSync(input) {
5265
5372
  let failed = 0;
5266
5373
  const files = [...walkMarkdown(input.vault, input.vault)];
5267
5374
  for (const relPath of files) {
5268
- const absPath = join31(input.vault, relPath);
5269
- const localStat = statSync3(absPath);
5375
+ const absPath = join32(input.vault, relPath);
5376
+ const localStat = statSync4(absPath);
5270
5377
  let needsUpload = true;
5271
5378
  try {
5272
5379
  const head = await client.send(new HeadObjectCommand({ Bucket: input.bucket, Key: relPath }));
@@ -5341,9 +5448,9 @@ async function runBackupRestore(input) {
5341
5448
  const objects = list.Contents ?? [];
5342
5449
  for (const obj of objects) {
5343
5450
  if (!obj.Key) continue;
5344
- const localPath = join31(target, obj.Key);
5451
+ const localPath = join32(target, obj.Key);
5345
5452
  try {
5346
- const localStat = statSync3(localPath);
5453
+ const localStat = statSync4(localPath);
5347
5454
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
5348
5455
  conflicts++;
5349
5456
  continue;
@@ -5387,11 +5494,11 @@ async function runBackupRestore(input) {
5387
5494
  }
5388
5495
 
5389
5496
  // 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";
5497
+ import { existsSync as existsSync11, statSync as statSync5 } from "fs";
5498
+ import { readFile as readFile21 } from "fs/promises";
5499
+ import { join as join33 } from "path";
5393
5500
  async function runStatus(input) {
5394
- if (!existsSync10(input.vault)) {
5501
+ if (!existsSync11(input.vault)) {
5395
5502
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
5396
5503
  }
5397
5504
  const scan = await scanVault(input.vault);
@@ -5416,7 +5523,7 @@ async function runStatus(input) {
5416
5523
  const compound = scan.data.compound.length;
5417
5524
  let schemaVersion = "v1";
5418
5525
  try {
5419
- const schemaContent = await readFile20(join32(input.vault, "SCHEMA.md"), "utf8");
5526
+ const schemaContent = await readFile21(join33(input.vault, "SCHEMA.md"), "utf8");
5420
5527
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
5421
5528
  if (versionMatch) schemaVersion = versionMatch[1];
5422
5529
  } catch {
@@ -5432,7 +5539,7 @@ async function runStatus(input) {
5432
5539
  let maxTime = 0;
5433
5540
  for (const page of allPages) {
5434
5541
  try {
5435
- const st = statSync4(page.absPath);
5542
+ const st = statSync5(page.absPath);
5436
5543
  if (st.mtimeMs > maxTime) {
5437
5544
  maxTime = st.mtimeMs;
5438
5545
  lastModified = st.mtime.toISOString();
@@ -5476,8 +5583,8 @@ async function runStatus(input) {
5476
5583
  }
5477
5584
 
5478
5585
  // 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";
5586
+ import { mkdir as mkdir13, writeFile as writeFile18, stat as stat7 } from "fs/promises";
5587
+ import { join as join34 } from "path";
5481
5588
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5482
5589
  var EXAMPLE_PAGES = {
5483
5590
  "entities/example-project.md": `---
@@ -5546,30 +5653,30 @@ Real sources are immutable after ingestion \u2014 never edit them.
5546
5653
  `;
5547
5654
  async function runSeed(input) {
5548
5655
  try {
5549
- await stat7(join33(input.vault, "SCHEMA.md"));
5656
+ await stat7(join34(input.vault, "SCHEMA.md"));
5550
5657
  } catch {
5551
5658
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
5552
5659
  }
5553
5660
  const created = [];
5554
5661
  const skipped = [];
5555
5662
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
5556
- const absPath = join33(input.vault, relPath);
5663
+ const absPath = join34(input.vault, relPath);
5557
5664
  try {
5558
5665
  await stat7(absPath);
5559
5666
  skipped.push(relPath);
5560
5667
  } catch {
5561
- await mkdir12(join33(absPath, ".."), { recursive: true });
5562
- await writeFile17(absPath, content, "utf8");
5668
+ await mkdir13(join34(absPath, ".."), { recursive: true });
5669
+ await writeFile18(absPath, content, "utf8");
5563
5670
  created.push(relPath);
5564
5671
  }
5565
5672
  }
5566
- const rawPath = join33(input.vault, "raw", "articles", "example-source.md");
5673
+ const rawPath = join34(input.vault, "raw", "articles", "example-source.md");
5567
5674
  try {
5568
5675
  await stat7(rawPath);
5569
5676
  skipped.push("raw/articles/example-source.md");
5570
5677
  } catch {
5571
- await mkdir12(join33(rawPath, ".."), { recursive: true });
5572
- await writeFile17(rawPath, EXAMPLE_RAW, "utf8");
5678
+ await mkdir13(join34(rawPath, ".."), { recursive: true });
5679
+ await writeFile18(rawPath, EXAMPLE_RAW, "utf8");
5573
5680
  created.push("raw/articles/example-source.md");
5574
5681
  }
5575
5682
  if (created.length > 0) {
@@ -5591,9 +5698,9 @@ async function runSeed(input) {
5591
5698
  }
5592
5699
 
5593
5700
  // 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";
5701
+ import { readFile as readFile22, writeFile as writeFile19 } from "fs/promises";
5702
+ import { existsSync as existsSync12 } from "fs";
5703
+ import { join as join35 } from "path";
5597
5704
  var NODE_WIDTH = 240;
5598
5705
  var NODE_HEIGHT = 60;
5599
5706
  var COLUMN_SPACING = 400;
@@ -5671,8 +5778,8 @@ function buildCanvasEdges(adjacency) {
5671
5778
  return edges;
5672
5779
  }
5673
5780
  async function runCanvasGenerate(input) {
5674
- const graphPath = input.graphPath ?? join34(input.vault, ".skillwiki", "graph.json");
5675
- if (!existsSync11(graphPath)) {
5781
+ const graphPath = input.graphPath ?? join35(input.vault, ".skillwiki", "graph.json");
5782
+ if (!existsSync12(graphPath)) {
5676
5783
  return {
5677
5784
  exitCode: ExitCode.FILE_NOT_FOUND,
5678
5785
  result: err("FILE_NOT_FOUND", {
@@ -5683,7 +5790,7 @@ async function runCanvasGenerate(input) {
5683
5790
  }
5684
5791
  let raw;
5685
5792
  try {
5686
- raw = await readFile21(graphPath, "utf8");
5793
+ raw = await readFile22(graphPath, "utf8");
5687
5794
  } catch (e) {
5688
5795
  return {
5689
5796
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -5709,9 +5816,9 @@ async function runCanvasGenerate(input) {
5709
5816
  const nodes = buildCanvasNodes(paths);
5710
5817
  const edges = buildCanvasEdges(graph.adjacency);
5711
5818
  const canvas = { nodes, edges };
5712
- const outPath = join34(input.vault, "vault-graph.canvas");
5819
+ const outPath = join35(input.vault, "vault-graph.canvas");
5713
5820
  try {
5714
- await writeFile18(outPath, JSON.stringify(canvas, null, 2));
5821
+ await writeFile19(outPath, JSON.stringify(canvas, null, 2));
5715
5822
  } catch (e) {
5716
5823
  return {
5717
5824
  exitCode: ExitCode.WRITE_FAILED,
@@ -5731,8 +5838,8 @@ written: ${outPath}`
5731
5838
  }
5732
5839
 
5733
5840
  // src/commands/query.ts
5734
- import { readFile as readFile22, stat as stat8 } from "fs/promises";
5735
- import { join as join35 } from "path";
5841
+ import { readFile as readFile23, stat as stat8 } from "fs/promises";
5842
+ import { join as join36 } from "path";
5736
5843
  var W_KEYWORD = 2;
5737
5844
  var W_SOURCE_OVERLAP = 4;
5738
5845
  var W_WIKILINK = 3;
@@ -5853,7 +5960,7 @@ function computeKeywordScore(terms, title, tags, body) {
5853
5960
  return score;
5854
5961
  }
5855
5962
  async function loadOrBuildGraph(vault) {
5856
- const graphPath = join35(vault, ".skillwiki", "graph.json");
5963
+ const graphPath = join36(vault, ".skillwiki", "graph.json");
5857
5964
  let needsBuild = false;
5858
5965
  try {
5859
5966
  const fileStat = await stat8(graphPath);
@@ -5867,7 +5974,7 @@ async function loadOrBuildGraph(vault) {
5867
5974
  if (buildResult.exitCode !== 0) return null;
5868
5975
  }
5869
5976
  try {
5870
- const raw = await readFile22(graphPath, "utf8");
5977
+ const raw = await readFile23(graphPath, "utf8");
5871
5978
  return JSON.parse(raw);
5872
5979
  } catch {
5873
5980
  return null;
@@ -5875,14 +5982,14 @@ async function loadOrBuildGraph(vault) {
5875
5982
  }
5876
5983
 
5877
5984
  // src/utils/auto-commit.ts
5878
- import { existsSync as existsSync12 } from "fs";
5879
- import { join as join36 } from "path";
5985
+ import { existsSync as existsSync13 } from "fs";
5986
+ import { join as join37 } from "path";
5880
5987
  async function postCommit(vault, exitCode) {
5881
5988
  if (exitCode !== 0) return;
5882
5989
  const home = process.env.HOME ?? "";
5883
5990
  const dotenv = await parseDotenvFile(configPath(home));
5884
5991
  if (dotenv["AUTO_COMMIT"] === "false") return;
5885
- if (!existsSync12(join36(vault, ".git"))) return;
5992
+ if (!existsSync13(join37(vault, ".git"))) return;
5886
5993
  const lastOps = readLastOp(vault);
5887
5994
  if (lastOps.length === 0) return;
5888
5995
  const porcelain = git(vault, ["status", "--porcelain"]);
@@ -5933,7 +6040,7 @@ program.command("validate <file>").description("validate vault page frontmatter
5933
6040
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
5934
6041
  });
5935
6042
  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");
6043
+ const out = opts.out ?? join38(vault, ".skillwiki", "graph.json");
5937
6044
  emit(await runGraphBuild({ vault, out }), vault);
5938
6045
  });
5939
6046
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -6044,6 +6151,11 @@ program.command("stale [vault]").description("identify stale transcripts and inc
6044
6151
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6045
6152
  else emit(await runStale({ vault: v.vault, days: opts.days, archive: !!opts.archive, forceScan: !!opts.forceScan }), v.vault);
6046
6153
  });
6154
+ 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) => {
6155
+ const v = await resolveVaultArg(vault, opts.wiki);
6156
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6157
+ else emit(await runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug }), v.vault);
6158
+ });
6047
6159
  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
6160
  const v = await resolveVaultArg(vault, opts.wiki);
6049
6161
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });