skillwiki 0.2.6 → 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
@@ -1845,6 +1845,7 @@ async function runStale(input) {
1845
1845
  const incompleteWorkItems = [];
1846
1846
  const archived = [];
1847
1847
  const workDirs = /* @__PURE__ */ new Map();
1848
+ const workDirsBySlug = /* @__PURE__ */ new Map();
1848
1849
  const projectsDir = join13(input.vault, "projects");
1849
1850
  let projectSlugs = [];
1850
1851
  try {
@@ -1859,6 +1860,7 @@ async function runStale(input) {
1859
1860
  } catch {
1860
1861
  continue;
1861
1862
  }
1863
+ const slugDirs = /* @__PURE__ */ new Map();
1862
1864
  for (const e of entries) {
1863
1865
  if (!e.isDirectory()) continue;
1864
1866
  const relDir = `projects/${slug}/work/${e.name}`;
@@ -1869,6 +1871,7 @@ async function runStale(input) {
1869
1871
  files = await readdir4(absDir);
1870
1872
  } catch {
1871
1873
  workDirs.set(relDir, "");
1874
+ slugDirs.set(e.name, "");
1872
1875
  continue;
1873
1876
  }
1874
1877
  for (const f of files) {
@@ -1883,18 +1886,110 @@ async function runStale(input) {
1883
1886
  }
1884
1887
  }
1885
1888
  workDirs.set(relDir, status);
1889
+ slugDirs.set(e.name, status);
1886
1890
  }
1891
+ workDirsBySlug.set(slug, slugDirs);
1892
+ }
1893
+ function extractSlug(projectField) {
1894
+ return projectField.replace(/^\[\[/, "").replace(/\]\]$/, "").replace(/^"|"$/g, "");
1887
1895
  }
1896
+ const KIND_FROM_FILENAME = /^(?:\d{4}-\d{2}-\d{2})-(task|bug|idea|note|observation)-.+\.md$/;
1897
+ const LOOP_CYCLE_PATTERN = /loop-cycle-/;
1888
1898
  const transcripts = scan.data.raw.filter((p) => p.relPath.startsWith("raw/transcripts/") && p.relPath.endsWith(".md"));
1899
+ const claimedPaths = /* @__PURE__ */ new Set();
1900
+ const transcriptMeta = /* @__PURE__ */ new Map();
1901
+ for (const t of transcripts) {
1902
+ try {
1903
+ const content = await readFile10(join13(input.vault, t.relPath), "utf8");
1904
+ const fm = extractFrontmatter(content);
1905
+ let kind = fm.ok && typeof fm.data.kind === "string" ? fm.data.kind : "";
1906
+ let project = fm.ok && typeof fm.data.project === "string" ? fm.data.project : "";
1907
+ let inferred = false;
1908
+ if (input.forceScan && !kind) {
1909
+ const basename = t.relPath.split("/").pop();
1910
+ if (!LOOP_CYCLE_PATTERN.test(basename)) {
1911
+ const m = basename.match(KIND_FROM_FILENAME);
1912
+ if (m) {
1913
+ kind = m[1];
1914
+ inferred = true;
1915
+ }
1916
+ }
1917
+ }
1918
+ if (input.forceScan && !project && kind) {
1919
+ const bodyStart = content.indexOf("---", 4);
1920
+ if (bodyStart > 0) {
1921
+ const body = content.slice(bodyStart);
1922
+ const wikilink2 = body.match(/\[\[([a-z0-9-]+)\]\]/);
1923
+ if (wikilink2) {
1924
+ const candidate = wikilink2[1];
1925
+ if (workDirsBySlug.has(candidate)) {
1926
+ project = `[[${candidate}]]`;
1927
+ inferred = true;
1928
+ }
1929
+ }
1930
+ }
1931
+ }
1932
+ transcriptMeta.set(t.relPath, { kind, project, slug: extractSlug(project), inferred });
1933
+ } catch {
1934
+ }
1935
+ }
1889
1936
  for (const t of transcripts) {
1890
1937
  const datePrefix = t.relPath.split("/").pop().slice(0, 10);
1891
- for (const [dir, status] of workDirs) {
1892
- if (dir.split("/").pop().startsWith(datePrefix) && (status === "done" || status === "invalid")) {
1893
- staleTranscripts.push({ path: t.relPath, reason: `work item ${dir} is ${status}` });
1894
- break;
1938
+ const meta = transcriptMeta.get(t.relPath);
1939
+ const slug = meta?.slug || "";
1940
+ if (slug && workDirsBySlug.has(slug)) {
1941
+ const slugDirs = workDirsBySlug.get(slug);
1942
+ const tSlug = t.relPath.split("/").pop().replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/\.md$/, "").replace(/^(task|bug|idea|note|observation)-/, "");
1943
+ for (const [dirName, status] of slugDirs) {
1944
+ if (!dirName.startsWith(datePrefix)) continue;
1945
+ const wSlug = dirName.replace(/^\d{4}-\d{2}-\d{2}-/, "");
1946
+ const tWords = new Set(tSlug.split("-").filter((w) => w.length >= 3));
1947
+ const wWords = wSlug.split("-").filter((w) => w.length >= 3);
1948
+ const overlap = wWords.filter((w) => tWords.has(w)).length;
1949
+ if (dirName.includes(tSlug) || tSlug.includes(wSlug) || overlap >= 1) {
1950
+ claimedPaths.add(t.relPath);
1951
+ if (status === "done" || status === "invalid") {
1952
+ staleTranscripts.push({ path: t.relPath, reason: `work item projects/${slug}/work/${dirName} is ${status}` });
1953
+ }
1954
+ break;
1955
+ }
1956
+ }
1957
+ } else if (!slug) {
1958
+ for (const [dir, status] of workDirs) {
1959
+ if (dir.split("/").pop().startsWith(datePrefix)) {
1960
+ claimedPaths.add(t.relPath);
1961
+ if (status === "done" || status === "invalid") {
1962
+ staleTranscripts.push({ path: t.relPath, reason: `work item ${dir} is ${status}` });
1963
+ }
1964
+ break;
1965
+ }
1895
1966
  }
1896
1967
  }
1897
1968
  }
1969
+ for (const [relDir] of workDirs) {
1970
+ const specPath = join13(input.vault, relDir, "spec.md");
1971
+ try {
1972
+ const specContent = await readFile10(specPath, "utf8");
1973
+ const specFm = extractFrontmatter(specContent);
1974
+ if (specFm.ok && typeof specFm.data.source === "string") {
1975
+ const sourcePath = specFm.data.source;
1976
+ if (sourcePath.startsWith("raw/transcripts/")) claimedPaths.add(sourcePath);
1977
+ }
1978
+ } catch {
1979
+ }
1980
+ }
1981
+ const unclaimedTranscripts = [];
1982
+ const CLAIMABLE_KINDS = /* @__PURE__ */ new Set(["task", "bug"]);
1983
+ for (const t of transcripts) {
1984
+ if (claimedPaths.has(t.relPath)) continue;
1985
+ const meta = transcriptMeta.get(t.relPath);
1986
+ if (!meta) continue;
1987
+ if (CLAIMABLE_KINDS.has(meta.kind) && meta.project) {
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 });
1991
+ }
1992
+ }
1898
1993
  const doneWorkItems = [];
1899
1994
  for (const [relDir, status] of workDirs) {
1900
1995
  const dirName = relDir.split("/").pop();
@@ -1987,17 +2082,20 @@ async function runStale(input) {
1987
2082
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1988
2083
  });
1989
2084
  }
1990
- const total = stale.length + staleTranscripts.length + incompleteWorkItems.length + doneWorkItems.length;
2085
+ const total = stale.length + staleTranscripts.length + unclaimedTranscripts.length + incompleteWorkItems.length + doneWorkItems.length;
1991
2086
  const hintLines = [];
1992
2087
  if (stale.length > 0) hintLines.push(`stale_pages: ${stale.length}`, ...stale.map((p) => ` ${p.page}: ${p.reason}`));
1993
2088
  if (staleTranscripts.length > 0) hintLines.push(`stale_transcripts: ${staleTranscripts.length}`, ...staleTranscripts.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}` : ""}`));
1994
2091
  if (incompleteWorkItems.length > 0) hintLines.push(`incomplete_work_items: ${incompleteWorkItems.length}`, ...incompleteWorkItems.map((w) => ` ${w.path}: ${w.reason}`));
1995
2092
  if (doneWorkItems.length > 0) hintLines.push(`done_work_items: ${doneWorkItems.length}`, ...doneWorkItems.map((w) => ` ${w.path}: ${w.reason}`));
1996
2093
  if (archived.length > 0) hintLines.push(`archived: ${archived.length}`, ...archived.map((a) => ` ${a}`));
1997
2094
  if (hintLines.length === 0) hintLines.push("no stale transcripts or incomplete work items");
1998
2095
  return { exitCode: total > 0 ? ExitCode.STALE_PAGE : ExitCode.OK, result: ok({
1999
- stale: [...stale, ...staleTranscripts.map((t) => ({ page: t.path, reason: t.reason })), ...incompleteWorkItems.map((w) => ({ page: w.path, reason: w.reason })), ...doneWorkItems.map((w) => ({ page: w.path, reason: w.reason }))],
2096
+ stale: [...stale, ...staleTranscripts.map((t) => ({ page: t.path, reason: t.reason })), ...unclaimedTranscripts.map((t) => ({ page: t.path, reason: t.reason })), ...incompleteWorkItems.map((w) => ({ page: w.path, reason: w.reason })), ...doneWorkItems.map((w) => ({ page: w.path, reason: w.reason }))],
2000
2097
  stale_transcripts: staleTranscripts,
2098
+ unclaimed_transcripts: unclaimedTranscripts,
2001
2099
  incomplete_work_items: incompleteWorkItems,
2002
2100
  done_work_items: doneWorkItems,
2003
2101
  archived,
@@ -2005,6 +2103,104 @@ async function runStale(input) {
2005
2103
  }) };
2006
2104
  }
2007
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
+
2008
2204
  // src/commands/pagesize.ts
2009
2205
  async function runPagesize(input) {
2010
2206
  const scan = await scanVault(input.vault);
@@ -2022,19 +2218,19 @@ async function runPagesize(input) {
2022
2218
  }
2023
2219
 
2024
2220
  // src/commands/log-rotate.ts
2025
- import { readFile as readFile11, rename as rename3, writeFile as writeFile6, stat as stat5 } from "fs/promises";
2026
- 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";
2027
2223
  var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
2028
2224
  async function runLogRotate(input) {
2029
2225
  try {
2030
- await stat5(join14(input.vault, "SCHEMA.md"));
2226
+ await stat5(join15(input.vault, "SCHEMA.md"));
2031
2227
  } catch {
2032
2228
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
2033
2229
  }
2034
- const logPath = join14(input.vault, "log.md");
2230
+ const logPath = join15(input.vault, "log.md");
2035
2231
  let logText;
2036
2232
  try {
2037
- logText = await readFile11(logPath, "utf8");
2233
+ logText = await readFile12(logPath, "utf8");
2038
2234
  } catch {
2039
2235
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
2040
2236
  }
@@ -2051,7 +2247,7 @@ async function runLogRotate(input) {
2051
2247
  }
2052
2248
  const newestYear = matches[matches.length - 1][1];
2053
2249
  const rotatedName = `log-${newestYear}.md`;
2054
- const rotatedPath = join14(input.vault, rotatedName);
2250
+ const rotatedPath = join15(input.vault, rotatedName);
2055
2251
  try {
2056
2252
  await rename3(logPath, rotatedPath);
2057
2253
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
@@ -2063,7 +2259,7 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
2063
2259
 
2064
2260
  - Previous log moved to ${rotatedName}
2065
2261
  `;
2066
- await writeFile6(logPath, fresh, "utf8");
2262
+ await writeFile7(logPath, fresh, "utf8");
2067
2263
  } catch (e) {
2068
2264
  return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
2069
2265
  }
@@ -2077,9 +2273,9 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
2077
2273
  }
2078
2274
 
2079
2275
  // src/commands/lint.ts
2080
- import { existsSync as existsSync2 } from "fs";
2081
- import { readFile as readFile13, writeFile as writeFile7 } from "fs/promises";
2082
- 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";
2083
2279
 
2084
2280
  // src/commands/topic-map-check.ts
2085
2281
  var DEFAULT_THRESHOLD = 200;
@@ -2101,13 +2297,13 @@ async function runTopicMapCheck(input) {
2101
2297
  }
2102
2298
 
2103
2299
  // src/commands/index-link-format.ts
2104
- import { readFile as readFile12 } from "fs/promises";
2105
- import { join as join15 } from "path";
2300
+ import { readFile as readFile13 } from "fs/promises";
2301
+ import { join as join16 } from "path";
2106
2302
  var MD_LINK_RE = /\[[^\[\]]+\]\([^)]+\.md\)/;
2107
2303
  async function runIndexLinkFormat(input) {
2108
2304
  let text = "";
2109
2305
  try {
2110
- text = await readFile12(join15(input.vault, "index.md"), "utf8");
2306
+ text = await readFile13(join16(input.vault, "index.md"), "utf8");
2111
2307
  } catch {
2112
2308
  }
2113
2309
  const markdown_links = [];
@@ -2121,7 +2317,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
2121
2317
 
2122
2318
  // src/commands/dedup.ts
2123
2319
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
2124
- import { join as join16 } from "path";
2320
+ import { join as join17 } from "path";
2125
2321
  async function runDedup(input) {
2126
2322
  const scan = await scanVault(input.vault);
2127
2323
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -2149,7 +2345,7 @@ async function runDedup(input) {
2149
2345
  }
2150
2346
  }
2151
2347
  for (const page of scan.data.typedKnowledge) {
2152
- const text = readFileSync3(join16(input.vault, page.relPath), "utf-8");
2348
+ const text = readFileSync3(join17(input.vault, page.relPath), "utf-8");
2153
2349
  let updated = text;
2154
2350
  let changed = false;
2155
2351
  for (const [oldPath, newPath] of replacements) {
@@ -2167,12 +2363,12 @@ async function runDedup(input) {
2167
2363
  }
2168
2364
  }
2169
2365
  if (changed) {
2170
- writeFileSync2(join16(input.vault, page.relPath), updated);
2366
+ writeFileSync2(join17(input.vault, page.relPath), updated);
2171
2367
  rewired.push(page.relPath);
2172
2368
  }
2173
2369
  }
2174
2370
  for (const [oldPath] of replacements) {
2175
- const fullPath = join16(input.vault, oldPath);
2371
+ const fullPath = join17(input.vault, oldPath);
2176
2372
  try {
2177
2373
  unlinkSync2(fullPath);
2178
2374
  removed.push(oldPath);
@@ -2268,7 +2464,7 @@ async function runLint(input) {
2268
2464
  const staleResult = await runStale({ vault: input.vault, days: input.days });
2269
2465
  if (staleResult.result.ok) {
2270
2466
  const st = staleResult.result.data;
2271
- const staleList = [...st.stale_transcripts.map((t) => t.path), ...st.incomplete_work_items.map((w) => w.path), ...(st.done_work_items ?? []).map((w) => w.path)];
2467
+ const staleList = [...st.stale_transcripts.map((t) => t.path), ...(st.unclaimed_transcripts ?? []).map((t) => t.path), ...st.incomplete_work_items.map((w) => w.path), ...(st.done_work_items ?? []).map((w) => w.path)];
2272
2468
  if (staleList.length > 0) buckets.stale_page = staleList;
2273
2469
  }
2274
2470
  const pagesize = await runPagesize({ vault: input.vault, lines: input.lines });
@@ -2319,7 +2515,7 @@ async function runLint(input) {
2319
2515
  let rawPath = entry.replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
2320
2516
  rawPath = rawPath.replace(/^\^\[/, "").replace(/\]$/, "");
2321
2517
  if (!rawPath.startsWith("raw/") && !rawPath.startsWith("_archive/raw/")) continue;
2322
- 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"))) {
2323
2519
  brokenSourceFlags.push(`${page.relPath}: ${rawPath}`);
2324
2520
  }
2325
2521
  }
@@ -2404,11 +2600,11 @@ async function runLint(input) {
2404
2600
  const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
2405
2601
  if (!slugMatch) continue;
2406
2602
  const slug = slugMatch[1];
2407
- const knowledgePath = join17(input.vault, "projects", slug, "knowledge.md");
2408
- if (!existsSync2(knowledgePath)) continue;
2603
+ const knowledgePath = join18(input.vault, "projects", slug, "knowledge.md");
2604
+ if (!existsSync3(knowledgePath)) continue;
2409
2605
  const pageRef = page.relPath.replace(/\.md$/, "");
2410
2606
  try {
2411
- const knowledgeContent = await readFile13(knowledgePath, "utf8");
2607
+ const knowledgeContent = await readFile14(knowledgePath, "utf8");
2412
2608
  if (!knowledgeContent.includes(`[[${pageRef}]]`)) {
2413
2609
  orphanedProjectPages.push(`${page.relPath}: not in projects/${slug}/knowledge.md`);
2414
2610
  }
@@ -2423,7 +2619,7 @@ async function runLint(input) {
2423
2619
  for (const relPath of legacyPages) {
2424
2620
  try {
2425
2621
  const absPath = `${input.vault}/${relPath}`;
2426
- const raw = await readFile13(absPath, "utf8");
2622
+ const raw = await readFile14(absPath, "utf8");
2427
2623
  const split = splitFrontmatter(raw);
2428
2624
  if (!split.ok) {
2429
2625
  unresolved.push(relPath);
@@ -2501,7 +2697,7 @@ async function runLint(input) {
2501
2697
  ${rawFm}
2502
2698
  ---
2503
2699
  ${newBody}`;
2504
- await writeFile7(absPath, newContent, "utf8");
2700
+ await writeFile8(absPath, newContent, "utf8");
2505
2701
  fixed.push(relPath);
2506
2702
  } catch {
2507
2703
  unresolved.push(relPath);
@@ -2518,7 +2714,7 @@ ${newBody}`;
2518
2714
  for (const relPath of noOverview) {
2519
2715
  try {
2520
2716
  const absPath = `${input.vault}/${relPath}`;
2521
- const raw = await readFile13(absPath, "utf8");
2717
+ const raw = await readFile14(absPath, "utf8");
2522
2718
  const split = splitFrontmatter(raw);
2523
2719
  if (!split.ok) {
2524
2720
  unresolved.push(relPath);
@@ -2539,7 +2735,7 @@ ${rawFm}
2539
2735
  ${overviewSection}
2540
2736
 
2541
2737
  ${trimmedBody}`;
2542
- await writeFile7(absPath, newContent, "utf8");
2738
+ await writeFile8(absPath, newContent, "utf8");
2543
2739
  fixed.push(relPath);
2544
2740
  } catch {
2545
2741
  unresolved.push(relPath);
@@ -2555,7 +2751,7 @@ ${trimmedBody}`;
2555
2751
  for (const relPath of missingTldrFlags) {
2556
2752
  try {
2557
2753
  const absPath = `${input.vault}/${relPath}`;
2558
- const raw = await readFile13(absPath, "utf8");
2754
+ const raw = await readFile14(absPath, "utf8");
2559
2755
  const split = splitFrontmatter(raw);
2560
2756
  if (!split.ok) {
2561
2757
  unresolved.push(relPath);
@@ -2573,7 +2769,7 @@ ${rawFm}
2573
2769
  - Pending summary.
2574
2770
 
2575
2771
  ${trimmedBody}`;
2576
- await writeFile7(absPath, newContent, "utf8");
2772
+ await writeFile8(absPath, newContent, "utf8");
2577
2773
  fixed.push(relPath);
2578
2774
  } catch {
2579
2775
  unresolved.push(relPath);
@@ -2591,7 +2787,7 @@ ${trimmedBody}`;
2591
2787
  for (const relPath of wikilinkCitationFlags) {
2592
2788
  try {
2593
2789
  const absPath = `${input.vault}/${relPath}`;
2594
- const raw = await readFile13(absPath, "utf8");
2790
+ const raw = await readFile14(absPath, "utf8");
2595
2791
  const split = splitFrontmatter(raw);
2596
2792
  if (!split.ok) {
2597
2793
  unresolved.push(relPath);
@@ -2655,7 +2851,7 @@ ${trimmedBody}`;
2655
2851
  ${rawFm}
2656
2852
  ---
2657
2853
  ${newBody}`;
2658
- await writeFile7(absPath, newContent, "utf8");
2854
+ await writeFile8(absPath, newContent, "utf8");
2659
2855
  wikilinkFixed.push(relPath);
2660
2856
  } catch {
2661
2857
  unresolved.push(relPath);
@@ -2712,14 +2908,14 @@ ${newBody}`;
2712
2908
  }
2713
2909
 
2714
2910
  // src/commands/config.ts
2715
- import { readFile as readFile14 } from "fs/promises";
2716
- import { existsSync as existsSync3 } from "fs";
2717
- 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";
2718
2914
  function validateKey(key) {
2719
2915
  return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
2720
2916
  }
2721
2917
  function configPath(home) {
2722
- return join18(home, ".skillwiki", ".env");
2918
+ return join19(home, ".skillwiki", ".env");
2723
2919
  }
2724
2920
  async function runConfigGet(input) {
2725
2921
  if (!validateKey(input.key)) {
@@ -2737,7 +2933,7 @@ async function runConfigSet(input) {
2737
2933
  try {
2738
2934
  let originalContent;
2739
2935
  try {
2740
- originalContent = await readFile14(filePath, "utf8");
2936
+ originalContent = await readFile15(filePath, "utf8");
2741
2937
  } catch {
2742
2938
  }
2743
2939
  const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
@@ -2769,17 +2965,17 @@ async function runConfigList(input) {
2769
2965
  }
2770
2966
  async function runConfigPath(input) {
2771
2967
  const filePath = configPath(input.home);
2772
- 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 }) };
2773
2969
  }
2774
2970
 
2775
2971
  // src/commands/doctor.ts
2776
- import { existsSync as existsSync5, lstatSync, readlinkSync, readdirSync, statSync } from "fs";
2777
- 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";
2778
2974
  import { execSync } from "child_process";
2779
2975
 
2780
2976
  // src/utils/auto-update.ts
2781
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
2782
- 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";
2783
2979
  import { spawn } from "child_process";
2784
2980
 
2785
2981
  // src/utils/update-consts.ts
@@ -2790,7 +2986,7 @@ var CLI_DISABLE_FLAG = "--no-update-notifier";
2790
2986
 
2791
2987
  // src/utils/auto-update.ts
2792
2988
  function cachePath(home) {
2793
- return join19(home, ".skillwiki", CACHE_FILENAME);
2989
+ return join20(home, ".skillwiki", CACHE_FILENAME);
2794
2990
  }
2795
2991
  function readCacheRaw(home) {
2796
2992
  try {
@@ -2828,7 +3024,7 @@ function triggerAutoUpdate(home, currentVersion) {
2828
3024
  const { isStale } = readCache(home);
2829
3025
  if (!isStale) return;
2830
3026
  const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
2831
- if (!existsSync4(bgScript)) return;
3027
+ if (!existsSync5(bgScript)) return;
2832
3028
  const child = spawn(process.execPath, [bgScript, home, currentVersion], {
2833
3029
  detached: true,
2834
3030
  stdio: "ignore"
@@ -2840,12 +3036,12 @@ function triggerAutoUpdate(home, currentVersion) {
2840
3036
 
2841
3037
  // src/utils/plugin-registry.ts
2842
3038
  import { readFileSync as readFileSync5 } from "fs";
2843
- import { join as join20 } from "path";
2844
- 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");
2845
3041
  var PLUGIN_KEY = "skillwiki@llm-wiki";
2846
3042
  function readInstalledPlugins(home) {
2847
3043
  try {
2848
- const raw = readFileSync5(join20(home, REGISTRY_PATH), "utf8");
3044
+ const raw = readFileSync5(join21(home, REGISTRY_PATH), "utf8");
2849
3045
  return JSON.parse(raw);
2850
3046
  } catch {
2851
3047
  return null;
@@ -2888,13 +3084,13 @@ function detectCliChannels(argv, home) {
2888
3084
  }
2889
3085
  const plugin = findPlugin(home);
2890
3086
  if (plugin) {
2891
- const pluginBin = join21(plugin.installPath, "bin", "skillwiki");
2892
- if (existsSync5(pluginBin)) {
3087
+ const pluginBin = join22(plugin.installPath, "bin", "skillwiki");
3088
+ if (existsSync6(pluginBin)) {
2893
3089
  channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
2894
3090
  }
2895
3091
  }
2896
- const installBin = join21(home, ".claude", "skills", "bin", "skillwiki");
2897
- if (existsSync5(installBin)) {
3092
+ const installBin = join22(home, ".claude", "skills", "bin", "skillwiki");
3093
+ if (existsSync6(installBin)) {
2898
3094
  channels.push({ name: "install", path: installBin, isDevLink: false });
2899
3095
  }
2900
3096
  return channels;
@@ -2946,7 +3142,7 @@ function checkCliChannels(argv, home) {
2946
3142
  }
2947
3143
  async function checkConfigFile(home) {
2948
3144
  const cfgPath = configPath(home);
2949
- if (!existsSync5(cfgPath)) {
3145
+ if (!existsSync6(cfgPath)) {
2950
3146
  return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
2951
3147
  }
2952
3148
  try {
@@ -2961,7 +3157,7 @@ function checkWikiPathExists(resolvedPath) {
2961
3157
  if (resolvedPath === void 0) {
2962
3158
  return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
2963
3159
  }
2964
- if (existsSync5(resolvedPath) && statSync(resolvedPath).isDirectory()) {
3160
+ if (existsSync6(resolvedPath) && statSync2(resolvedPath).isDirectory()) {
2965
3161
  return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
2966
3162
  }
2967
3163
  return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
@@ -2970,13 +3166,13 @@ function checkVaultStructure(resolvedPath) {
2970
3166
  if (resolvedPath === void 0) {
2971
3167
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
2972
3168
  }
2973
- if (!existsSync5(resolvedPath)) {
3169
+ if (!existsSync6(resolvedPath)) {
2974
3170
  return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
2975
3171
  }
2976
3172
  const missing = [];
2977
- if (!existsSync5(join21(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
3173
+ if (!existsSync6(join22(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
2978
3174
  for (const dir of ["raw", "entities", "concepts", "meta"]) {
2979
- if (!existsSync5(join21(resolvedPath, dir))) missing.push(dir + "/");
3175
+ if (!existsSync6(join22(resolvedPath, dir))) missing.push(dir + "/");
2980
3176
  }
2981
3177
  if (missing.length === 0) {
2982
3178
  return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
@@ -2984,8 +3180,8 @@ function checkVaultStructure(resolvedPath) {
2984
3180
  return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
2985
3181
  }
2986
3182
  function checkSkillsInstalled(home, cwd) {
2987
- const srcDir = cwd ? join21(cwd, "packages", "skills") : void 0;
2988
- if (srcDir && existsSync5(srcDir)) {
3183
+ const srcDir = cwd ? join22(cwd, "packages", "skills") : void 0;
3184
+ if (srcDir && existsSync6(srcDir)) {
2989
3185
  const found = findSkillMd(srcDir);
2990
3186
  if (found.length > 0) {
2991
3187
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
@@ -2998,8 +3194,8 @@ function checkSkillsInstalled(home, cwd) {
2998
3194
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
2999
3195
  }
3000
3196
  }
3001
- const skillsDir = join21(home, ".claude", "skills");
3002
- if (existsSync5(skillsDir)) {
3197
+ const skillsDir = join22(home, ".claude", "skills");
3198
+ if (existsSync6(skillsDir)) {
3003
3199
  const found = findSkillMd(skillsDir);
3004
3200
  if (found.length > 0) {
3005
3201
  return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
@@ -3009,10 +3205,10 @@ function checkSkillsInstalled(home, cwd) {
3009
3205
  }
3010
3206
  function checkDuplicateSkills(home) {
3011
3207
  const plugin = findPlugin(home);
3012
- const skillsDir = join21(home, ".claude", "skills");
3208
+ const skillsDir = join22(home, ".claude", "skills");
3013
3209
  const agentSkillDirs = [
3014
- { label: "~/.codex/skills/", path: join21(home, ".codex", "skills") },
3015
- { 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") }
3016
3212
  ];
3017
3213
  if (!plugin) {
3018
3214
  return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
@@ -3089,8 +3285,8 @@ async function checkProfiles(home) {
3089
3285
  }
3090
3286
  async function checkProjectLocalOverride(cwd) {
3091
3287
  const dir = cwd ?? process.cwd();
3092
- const envPath = join21(dir, ".skillwiki", ".env");
3093
- if (existsSync5(envPath)) {
3288
+ const envPath = join22(dir, ".skillwiki", ".env");
3289
+ if (existsSync6(envPath)) {
3094
3290
  return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
3095
3291
  }
3096
3292
  return check("pass", "project_local", "Project-local config", "None");
@@ -3099,7 +3295,7 @@ function checkVaultGitRemote(resolvedPath) {
3099
3295
  if (resolvedPath === void 0) {
3100
3296
  return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
3101
3297
  }
3102
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3298
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3103
3299
  return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
3104
3300
  }
3105
3301
  try {
@@ -3122,9 +3318,9 @@ function checkObsidianTemplates(resolvedPath) {
3122
3318
  return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
3123
3319
  }
3124
3320
  const missing = [];
3125
- if (!existsSync5(join21(resolvedPath, "_Templates"))) missing.push("_Templates/");
3126
- if (!existsSync5(join21(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
3127
- 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");
3128
3324
  if (missing.length === 0) {
3129
3325
  return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
3130
3326
  }
@@ -3134,8 +3330,8 @@ function checkDotStoreClean(resolvedPath) {
3134
3330
  if (resolvedPath === void 0) {
3135
3331
  return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
3136
3332
  }
3137
- const rawDir = join21(resolvedPath, "raw");
3138
- if (!existsSync5(rawDir)) {
3333
+ const rawDir = join22(resolvedPath, "raw");
3334
+ if (!existsSync6(rawDir)) {
3139
3335
  return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
3140
3336
  }
3141
3337
  const found = [];
@@ -3150,7 +3346,7 @@ function checkDotStoreClean(resolvedPath) {
3150
3346
  if (entry.name === ".DS_Store") {
3151
3347
  found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
3152
3348
  } else if (entry.isDirectory()) {
3153
- walk2(join21(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3349
+ walk2(join22(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
3154
3350
  }
3155
3351
  }
3156
3352
  })(rawDir, "");
@@ -3163,7 +3359,7 @@ function checkSyncLastPush(resolvedPath) {
3163
3359
  if (resolvedPath === void 0) {
3164
3360
  return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
3165
3361
  }
3166
- if (!existsSync5(join21(resolvedPath, ".git"))) {
3362
+ if (!existsSync6(join22(resolvedPath, ".git"))) {
3167
3363
  return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
3168
3364
  }
3169
3365
  let timestamp;
@@ -3205,9 +3401,9 @@ function findSkillMd(dir) {
3205
3401
  }
3206
3402
  for (const entry of entries) {
3207
3403
  if (entry.isFile() && entry.name === "SKILL.md") {
3208
- results.push(join21(dir, entry.name));
3404
+ results.push(join22(dir, entry.name));
3209
3405
  } else if (entry.isDirectory()) {
3210
- results.push(...findSkillMd(join21(dir, entry.name)));
3406
+ results.push(...findSkillMd(join22(dir, entry.name)));
3211
3407
  }
3212
3408
  }
3213
3409
  return results;
@@ -3221,7 +3417,7 @@ function findSkillNames(dir) {
3221
3417
  return results;
3222
3418
  }
3223
3419
  for (const entry of entries) {
3224
- if (entry.isDirectory() && existsSync5(join21(dir, entry.name, "SKILL.md"))) {
3420
+ if (entry.isDirectory() && existsSync6(join22(dir, entry.name, "SKILL.md"))) {
3225
3421
  results.push(entry.name);
3226
3422
  }
3227
3423
  }
@@ -3274,8 +3470,8 @@ async function runDoctor(input) {
3274
3470
  }
3275
3471
 
3276
3472
  // src/commands/archive.ts
3277
- import { rename as rename4, mkdir as mkdir7, readFile as readFile15, writeFile as writeFile8 } from "fs/promises";
3278
- 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";
3279
3475
  async function runArchive(input) {
3280
3476
  const scan = await scanVault(input.vault);
3281
3477
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
@@ -3291,25 +3487,25 @@ async function runArchive(input) {
3291
3487
  }
3292
3488
  if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
3293
3489
  if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
3294
- const archivePath = join22("_archive", relPath).replace(/\\/g, "/");
3295
- 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 });
3296
3492
  let indexUpdated = false;
3297
3493
  if (!isRaw) {
3298
- const indexPath = join22(input.vault, "index.md");
3494
+ const indexPath = join23(input.vault, "index.md");
3299
3495
  try {
3300
- const idx = await readFile15(indexPath, "utf8");
3496
+ const idx = await readFile16(indexPath, "utf8");
3301
3497
  const slug = relPath.replace(/\.md$/, "").split("/").pop();
3302
3498
  const originalLines = idx.split("\n");
3303
3499
  const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
3304
3500
  if (filtered.length !== originalLines.length) {
3305
- await writeFile8(indexPath, filtered.join("\n"), "utf8");
3501
+ await writeFile9(indexPath, filtered.join("\n"), "utf8");
3306
3502
  indexUpdated = true;
3307
3503
  }
3308
3504
  } catch (e) {
3309
3505
  if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
3310
3506
  }
3311
3507
  }
3312
- await rename4(join22(input.vault, relPath), join22(input.vault, archivePath));
3508
+ await rename4(join23(input.vault, relPath), join23(input.vault, archivePath));
3313
3509
  appendLastOp(input.vault, {
3314
3510
  operation: "archive",
3315
3511
  summary: `moved ${relPath} to ${archivePath}`,
@@ -3321,7 +3517,7 @@ async function runArchive(input) {
3321
3517
 
3322
3518
  // src/commands/drift.ts
3323
3519
  import { createHash as createHash2 } from "crypto";
3324
- import { writeFile as writeFile9 } from "fs/promises";
3520
+ import { writeFile as writeFile10 } from "fs/promises";
3325
3521
 
3326
3522
  // src/utils/fetch.ts
3327
3523
  async function controlledFetch(url, opts) {
@@ -3408,7 +3604,7 @@ async function runDrift(input) {
3408
3604
  ${newFm}
3409
3605
  ---
3410
3606
  ${body}`;
3411
- await writeFile9(raw.absPath, newText, "utf8");
3607
+ await writeFile10(raw.absPath, newText, "utf8");
3412
3608
  results.push({
3413
3609
  raw_path: raw.relPath,
3414
3610
  source_url: sourceUrl,
@@ -3451,7 +3647,7 @@ ${body}`;
3451
3647
  }
3452
3648
 
3453
3649
  // src/commands/migrate-citations.ts
3454
- import { writeFile as writeFile10 } from "fs/promises";
3650
+ import { writeFile as writeFile11 } from "fs/promises";
3455
3651
  var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
3456
3652
  function moveMarkersToParagraphEnd(body) {
3457
3653
  const lines = body.split("\n");
@@ -3574,7 +3770,7 @@ ${migratedBody}${newFooter}`;
3574
3770
  continue;
3575
3771
  }
3576
3772
  if (!input.dryRun) {
3577
- await writeFile10(page.absPath, newText, "utf8");
3773
+ await writeFile11(page.absPath, newText, "utf8");
3578
3774
  }
3579
3775
  migrated.push(page.relPath);
3580
3776
  }
@@ -3604,7 +3800,7 @@ ${migratedBody}${newFooter}`;
3604
3800
  }
3605
3801
 
3606
3802
  // src/commands/frontmatter-fix.ts
3607
- import { writeFile as writeFile11 } from "fs/promises";
3803
+ import { writeFile as writeFile12 } from "fs/promises";
3608
3804
  function isoToday() {
3609
3805
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3610
3806
  }
@@ -3646,7 +3842,7 @@ ${newBody}`;
3646
3842
  continue;
3647
3843
  }
3648
3844
  if (!input.dryRun) {
3649
- await writeFile11(page.absPath, newText, "utf8");
3845
+ await writeFile12(page.absPath, newText, "utf8");
3650
3846
  }
3651
3847
  fixed.push(page.relPath);
3652
3848
  }
@@ -3679,14 +3875,14 @@ ${newBody}`;
3679
3875
  // src/commands/update.ts
3680
3876
  import { execSync as execSync2 } from "child_process";
3681
3877
  import { readFileSync as readFileSync6 } from "fs";
3682
- import { join as join23 } from "path";
3878
+ import { join as join24 } from "path";
3683
3879
  function resolveGlobalSkillsRoot() {
3684
3880
  try {
3685
3881
  const globalRoot = execSync2("npm root -g", {
3686
3882
  encoding: "utf8",
3687
3883
  timeout: 5e3
3688
3884
  }).trim();
3689
- return join23(globalRoot, "skillwiki", "skills");
3885
+ return join24(globalRoot, "skillwiki", "skills");
3690
3886
  } catch {
3691
3887
  return null;
3692
3888
  }
@@ -3712,7 +3908,7 @@ async function runUpdate(input) {
3712
3908
  );
3713
3909
  const currentVersion = pkg2.version;
3714
3910
  const tag = input.distTag ?? "latest";
3715
- const target = join23(input.home, ".claude", "skills");
3911
+ const target = join24(input.home, ".claude", "skills");
3716
3912
  let latest;
3717
3913
  try {
3718
3914
  latest = execSync2(`npm view skillwiki@${tag} version`, {
@@ -3782,16 +3978,16 @@ async function runUpdate(input) {
3782
3978
 
3783
3979
  // src/commands/self-update.ts
3784
3980
  import { execSync as execSync3 } from "child_process";
3785
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
3786
- import { join as join24 } from "path";
3981
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
3982
+ import { join as join25 } from "path";
3787
3983
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
3788
3984
  async function runSelfUpdate(input) {
3789
3985
  const currentVersion = JSON.parse(
3790
3986
  readFileSync7(new URL("../../package.json", import.meta.url), "utf8")
3791
3987
  ).version;
3792
3988
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
3793
- const localPkgPath = join24(sourceRoot, "packages", "cli", "package.json");
3794
- const hasLocalSource = existsSync6(localPkgPath);
3989
+ const localPkgPath = join25(sourceRoot, "packages", "cli", "package.json");
3990
+ const hasLocalSource = existsSync7(localPkgPath);
3795
3991
  if (input.check) {
3796
3992
  let availableVersion = null;
3797
3993
  let source;
@@ -3922,10 +4118,10 @@ async function runSelfUpdate(input) {
3922
4118
  }
3923
4119
 
3924
4120
  // src/commands/transcripts.ts
3925
- import { readdir as readdir5, stat as stat6, readFile as readFile16 } from "fs/promises";
3926
- 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";
3927
4123
  async function runTranscripts(input) {
3928
- const dir = join25(input.vault, "raw", "transcripts");
4124
+ const dir = join26(input.vault, "raw", "transcripts");
3929
4125
  let entries;
3930
4126
  try {
3931
4127
  entries = await readdir5(dir, { withFileTypes: true });
@@ -3935,8 +4131,8 @@ async function runTranscripts(input) {
3935
4131
  const transcripts = [];
3936
4132
  for (const entry of entries) {
3937
4133
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
3938
- const filePath = join25(dir, entry.name);
3939
- const content = await readFile16(filePath, "utf8");
4134
+ const filePath = join26(dir, entry.name);
4135
+ const content = await readFile17(filePath, "utf8");
3940
4136
  const fm = extractFrontmatter(content);
3941
4137
  if (!fm.ok) continue;
3942
4138
  const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
@@ -3953,12 +4149,12 @@ async function runTranscripts(input) {
3953
4149
  }
3954
4150
 
3955
4151
  // src/commands/project-index.ts
3956
- import { readdir as readdir6, readFile as readFile17, writeFile as writeFile12, mkdir as mkdir8 } from "fs/promises";
3957
- 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";
3958
4154
  var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
3959
4155
  async function runProjectIndex(input) {
3960
4156
  const slug = input.slug;
3961
- const projectDir = join26(input.vault, "projects", slug);
4157
+ const projectDir = join27(input.vault, "projects", slug);
3962
4158
  try {
3963
4159
  await readdir6(projectDir);
3964
4160
  } catch {
@@ -3969,15 +4165,15 @@ async function runProjectIndex(input) {
3969
4165
  }
3970
4166
  const wikilinkPattern = `[[${slug}]]`;
3971
4167
  const entries = [];
3972
- const compoundDir = join26(input.vault, "projects", slug, "compound");
4168
+ const compoundDir = join27(input.vault, "projects", slug, "compound");
3973
4169
  try {
3974
4170
  const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
3975
4171
  for (const entry of compoundFiles) {
3976
4172
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
3977
- const filePath = join26(compoundDir, entry.name);
4173
+ const filePath = join27(compoundDir, entry.name);
3978
4174
  let text;
3979
4175
  try {
3980
- text = await readFile17(filePath, "utf8");
4176
+ text = await readFile18(filePath, "utf8");
3981
4177
  } catch {
3982
4178
  continue;
3983
4179
  }
@@ -3994,16 +4190,16 @@ async function runProjectIndex(input) {
3994
4190
  for (const dir of LAYER2_DIRS) {
3995
4191
  let files;
3996
4192
  try {
3997
- files = await readdir6(join26(input.vault, dir), { withFileTypes: true });
4193
+ files = await readdir6(join27(input.vault, dir), { withFileTypes: true });
3998
4194
  } catch {
3999
4195
  continue;
4000
4196
  }
4001
4197
  for (const entry of files) {
4002
4198
  if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
4003
- const filePath = join26(input.vault, dir, entry.name);
4199
+ const filePath = join27(input.vault, dir, entry.name);
4004
4200
  let text;
4005
4201
  try {
4006
- text = await readFile17(filePath, "utf8");
4202
+ text = await readFile18(filePath, "utf8");
4007
4203
  } catch {
4008
4204
  continue;
4009
4205
  }
@@ -4024,11 +4220,11 @@ async function runProjectIndex(input) {
4024
4220
  const tb = typeOrder[b.type] ?? 99;
4025
4221
  return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
4026
4222
  });
4027
- const indexPath = join26(projectDir, "knowledge.md");
4223
+ const indexPath = join27(projectDir, "knowledge.md");
4028
4224
  let existing = false;
4029
4225
  let stale = false;
4030
4226
  try {
4031
- const existingText = await readFile17(indexPath, "utf8");
4227
+ const existingText = await readFile18(indexPath, "utf8");
4032
4228
  existing = true;
4033
4229
  const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
4034
4230
  const existingPages = new Set(existingEntries.map((l) => {
@@ -4068,8 +4264,8 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
4068
4264
  }
4069
4265
  if (input.apply) {
4070
4266
  try {
4071
- await mkdir8(dirname10(indexPath), { recursive: true });
4072
- await writeFile12(indexPath, body, "utf8");
4267
+ await mkdir9(dirname10(indexPath), { recursive: true });
4268
+ await writeFile13(indexPath, body, "utf8");
4073
4269
  } catch (e) {
4074
4270
  return {
4075
4271
  exitCode: ExitCode.WRITE_FAILED,
@@ -4097,10 +4293,10 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
4097
4293
  }
4098
4294
 
4099
4295
  // src/commands/compound.ts
4100
- import { writeFile as writeFile13, mkdir as mkdir9, readdir as readdir7, unlink as unlink2 } from "fs/promises";
4101
- import { join as join27 } from "path";
4102
- import { existsSync as existsSync7 } from "fs";
4103
- 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";
4104
4300
  var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
4105
4301
  var FIELD_RE = {
4106
4302
  improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
@@ -4198,17 +4394,17 @@ function extractRetroFields(date, cycleName, block) {
4198
4394
  };
4199
4395
  }
4200
4396
  async function runCompound(input) {
4201
- const logPath = join27(input.vault, "log.md");
4397
+ const logPath = join28(input.vault, "log.md");
4202
4398
  let logText;
4203
4399
  try {
4204
- logText = await readFile18(logPath, "utf8");
4400
+ logText = await readFile19(logPath, "utf8");
4205
4401
  } catch {
4206
4402
  return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
4207
4403
  }
4208
4404
  const entries = parseRetroEntries(logText);
4209
4405
  const promoted = [];
4210
4406
  const skipped = [];
4211
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4407
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4212
4408
  for (const entry of entries) {
4213
4409
  const generalizeValue = entry.generalize.trim();
4214
4410
  if (!/^yes/i.test(generalizeValue)) {
@@ -4216,8 +4412,8 @@ async function runCompound(input) {
4216
4412
  continue;
4217
4413
  }
4218
4414
  const slug = slugify(entry.cycleName);
4219
- const compoundPath = join27(compoundDir, `${slug}.md`);
4220
- if (existsSync7(compoundPath)) {
4415
+ const compoundPath = join28(compoundDir, `${slug}.md`);
4416
+ if (existsSync8(compoundPath)) {
4221
4417
  skipped.push(entry.date);
4222
4418
  continue;
4223
4419
  }
@@ -4255,10 +4451,10 @@ async function runCompound(input) {
4255
4451
  ].join("\n");
4256
4452
  const content = frontmatter + "\n" + body;
4257
4453
  if (!input.dryRun) {
4258
- if (!existsSync7(compoundDir)) {
4259
- await mkdir9(compoundDir, { recursive: true });
4454
+ if (!existsSync8(compoundDir)) {
4455
+ await mkdir10(compoundDir, { recursive: true });
4260
4456
  }
4261
- await writeFile13(compoundPath, content, "utf8");
4457
+ await writeFile14(compoundPath, content, "utf8");
4262
4458
  }
4263
4459
  promoted.push(`${slug}.md`);
4264
4460
  }
@@ -4277,16 +4473,16 @@ async function runCompound(input) {
4277
4473
  };
4278
4474
  }
4279
4475
  async function runCompoundDelete(input) {
4280
- const projectDir = join27(input.vault, "projects", input.project);
4281
- if (!existsSync7(projectDir)) {
4476
+ const projectDir = join28(input.vault, "projects", input.project);
4477
+ if (!existsSync8(projectDir)) {
4282
4478
  return {
4283
4479
  exitCode: ExitCode.PROJECT_NOT_FOUND,
4284
4480
  result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
4285
4481
  };
4286
4482
  }
4287
4483
  const entryName = input.entry.replace(/\.md$/, "");
4288
- const compoundPath = join27(projectDir, "compound", `${entryName}.md`);
4289
- if (!existsSync7(compoundPath)) {
4484
+ const compoundPath = join28(projectDir, "compound", `${entryName}.md`);
4485
+ if (!existsSync8(compoundPath)) {
4290
4486
  return {
4291
4487
  exitCode: ExitCode.FILE_NOT_FOUND,
4292
4488
  result: err("FILE_NOT_FOUND", { path: compoundPath })
@@ -4319,8 +4515,8 @@ knowledge.md regenerated`
4319
4515
  };
4320
4516
  }
4321
4517
  async function runCompoundList(input) {
4322
- const compoundDir = join27(input.vault, "projects", input.project, "compound");
4323
- if (!existsSync7(compoundDir)) {
4518
+ const compoundDir = join28(input.vault, "projects", input.project, "compound");
4519
+ if (!existsSync8(compoundDir)) {
4324
4520
  return {
4325
4521
  exitCode: ExitCode.OK,
4326
4522
  result: ok({
@@ -4350,10 +4546,10 @@ could not read compound directory`
4350
4546
  const entries = [];
4351
4547
  for (const dirent of dirents) {
4352
4548
  if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
4353
- const filePath = join27(compoundDir, dirent.name);
4549
+ const filePath = join28(compoundDir, dirent.name);
4354
4550
  let text;
4355
4551
  try {
4356
- text = await readFile18(filePath, "utf8");
4552
+ text = await readFile19(filePath, "utf8");
4357
4553
  } catch {
4358
4554
  continue;
4359
4555
  }
@@ -4382,9 +4578,9 @@ no compound entries found`;
4382
4578
  }
4383
4579
 
4384
4580
  // src/commands/observe.ts
4385
- import { mkdir as mkdir10, writeFile as writeFile14 } from "fs/promises";
4386
- import { existsSync as existsSync8, statSync as statSync2 } from "fs";
4387
- 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";
4388
4584
  import { createHash as createHash3 } from "crypto";
4389
4585
  var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
4390
4586
  function slugify2(text) {
@@ -4392,7 +4588,7 @@ function slugify2(text) {
4392
4588
  return words || "untitled";
4393
4589
  }
4394
4590
  async function runObserve(input) {
4395
- const kind = input.kind || "note";
4591
+ const kind = input.kind || "task";
4396
4592
  if (!ALLOWED_KINDS.has(kind)) {
4397
4593
  return {
4398
4594
  exitCode: ExitCode.SCHEME_REJECTED,
@@ -4407,15 +4603,15 @@ async function runObserve(input) {
4407
4603
  result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
4408
4604
  };
4409
4605
  }
4410
- if (!existsSync8(input.vault) || !statSync2(input.vault).isDirectory()) {
4606
+ if (!existsSync9(input.vault) || !statSync3(input.vault).isDirectory()) {
4411
4607
  return {
4412
4608
  exitCode: ExitCode.VAULT_PATH_INVALID,
4413
4609
  result: err("VAULT_PATH_INVALID", { path: input.vault })
4414
4610
  };
4415
4611
  }
4416
- const transcriptsDir = join28(input.vault, "raw", "transcripts");
4612
+ const transcriptsDir = join29(input.vault, "raw", "transcripts");
4417
4613
  try {
4418
- await mkdir10(transcriptsDir, { recursive: true });
4614
+ await mkdir11(transcriptsDir, { recursive: true });
4419
4615
  } catch {
4420
4616
  return {
4421
4617
  exitCode: ExitCode.VAULT_PATH_INVALID,
@@ -4425,7 +4621,7 @@ async function runObserve(input) {
4425
4621
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4426
4622
  const slug = slugify2(input.text);
4427
4623
  const fileName = `${today}-observation-${slug}.md`;
4428
- const filePath = join28(transcriptsDir, fileName);
4624
+ const filePath = join29(transcriptsDir, fileName);
4429
4625
  const body = `
4430
4626
  ${input.text.trim()}
4431
4627
  `;
@@ -4443,7 +4639,7 @@ ${input.text.trim()}
4443
4639
  frontmatterLines.push("---");
4444
4640
  const content = frontmatterLines.join("\n") + body;
4445
4641
  try {
4446
- await writeFile14(filePath, content, "utf8");
4642
+ await writeFile15(filePath, content, "utf8");
4447
4643
  } catch (e) {
4448
4644
  return {
4449
4645
  exitCode: ExitCode.WRITE_FAILED,
@@ -4465,8 +4661,8 @@ ${input.text.trim()}
4465
4661
  }
4466
4662
 
4467
4663
  // src/commands/ingest.ts
4468
- import { readFile as readFile19, writeFile as writeFile15, mkdir as mkdir11 } from "fs/promises";
4469
- 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";
4470
4666
  import { createHash as createHash4 } from "crypto";
4471
4667
  var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
4472
4668
  var TYPE_DIR = {
@@ -4625,7 +4821,7 @@ async function runIngest(input) {
4625
4821
  sourceContent = fetchResult.data.body;
4626
4822
  } else {
4627
4823
  try {
4628
- sourceContent = await readFile19(input.source, "utf8");
4824
+ sourceContent = await readFile20(input.source, "utf8");
4629
4825
  } catch {
4630
4826
  return {
4631
4827
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -4640,8 +4836,8 @@ async function runIngest(input) {
4640
4836
  const rawRelPath = `raw/articles/${slug}.md`;
4641
4837
  const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
4642
4838
  const typedRelPath = `${typedDir}/${slug}.md`;
4643
- const rawAbsPath = join29(input.vault, rawRelPath);
4644
- const typedAbsPath = join29(input.vault, typedRelPath);
4839
+ const rawAbsPath = join30(input.vault, rawRelPath);
4840
+ const typedAbsPath = join30(input.vault, typedRelPath);
4645
4841
  const rawContent = buildRawContent(sourceUrl, today, sha256, sourceContent);
4646
4842
  const typedContent = buildTypedContent(
4647
4843
  input.title,
@@ -4704,8 +4900,8 @@ async function runIngest(input) {
4704
4900
  };
4705
4901
  }
4706
4902
  try {
4707
- await mkdir11(join29(input.vault, "raw", "articles"), { recursive: true });
4708
- await writeFile15(rawAbsPath, rawContent, "utf8");
4903
+ await mkdir12(join30(input.vault, "raw", "articles"), { recursive: true });
4904
+ await writeFile16(rawAbsPath, rawContent, "utf8");
4709
4905
  } catch (e) {
4710
4906
  return {
4711
4907
  exitCode: ExitCode.WRITE_FAILED,
@@ -4713,8 +4909,8 @@ async function runIngest(input) {
4713
4909
  };
4714
4910
  }
4715
4911
  try {
4716
- await mkdir11(join29(input.vault, typedDir), { recursive: true });
4717
- await writeFile15(typedAbsPath, typedContent, "utf8");
4912
+ await mkdir12(join30(input.vault, typedDir), { recursive: true });
4913
+ await writeFile16(typedAbsPath, typedContent, "utf8");
4718
4914
  } catch (e) {
4719
4915
  return {
4720
4916
  exitCode: ExitCode.WRITE_FAILED,
@@ -4745,7 +4941,7 @@ async function runIngest(input) {
4745
4941
  }
4746
4942
 
4747
4943
  // src/commands/tag-sync.ts
4748
- import { writeFile as writeFile16 } from "fs/promises";
4944
+ import { writeFile as writeFile17 } from "fs/promises";
4749
4945
  var ENUM_MIRRORS = {
4750
4946
  provenance: ["research", "project", "mixed"],
4751
4947
  confidence: ["high", "medium", "low"]
@@ -4860,7 +5056,7 @@ ${newFm}
4860
5056
  ---
4861
5057
  ${body}`;
4862
5058
  if (!input.dryRun) {
4863
- await writeFile16(page.absPath, newText, "utf8");
5059
+ await writeFile17(page.absPath, newText, "utf8");
4864
5060
  }
4865
5061
  synced.push(page.relPath);
4866
5062
  }
@@ -4889,11 +5085,11 @@ ${body}`;
4889
5085
  }
4890
5086
 
4891
5087
  // src/commands/sync.ts
4892
- import { existsSync as existsSync9 } from "fs";
4893
- import { join as join30 } from "path";
5088
+ import { existsSync as existsSync10 } from "fs";
5089
+ import { join as join31 } from "path";
4894
5090
  function runSyncStatus(input) {
4895
5091
  const vault = input.vault;
4896
- if (!existsSync9(join30(vault, ".git"))) {
5092
+ if (!existsSync10(join31(vault, ".git"))) {
4897
5093
  return {
4898
5094
  exitCode: ExitCode.VAULT_PATH_INVALID,
4899
5095
  result: ok({
@@ -4962,7 +5158,7 @@ function runSyncStatus(input) {
4962
5158
  }
4963
5159
  async function runSyncPush(input) {
4964
5160
  const vault = input.vault;
4965
- if (!existsSync9(join30(vault, ".git"))) {
5161
+ if (!existsSync10(join31(vault, ".git"))) {
4966
5162
  return {
4967
5163
  exitCode: ExitCode.VAULT_PATH_INVALID,
4968
5164
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5047,7 +5243,7 @@ async function runSyncPush(input) {
5047
5243
  }
5048
5244
  async function runSyncPull(input) {
5049
5245
  const vault = input.vault;
5050
- if (!existsSync9(join30(vault, ".git"))) {
5246
+ if (!existsSync10(join31(vault, ".git"))) {
5051
5247
  return {
5052
5248
  exitCode: ExitCode.VAULT_PATH_INVALID,
5053
5249
  result: err("NOT_A_GIT_REPO", { path: vault })
@@ -5122,8 +5318,8 @@ async function runSyncPull(input) {
5122
5318
  }
5123
5319
 
5124
5320
  // src/commands/backup.ts
5125
- import { statSync as statSync3, readdirSync as readdirSync2, readFileSync as readFileSync8, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
5126
- 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";
5127
5323
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
5128
5324
 
5129
5325
  // src/utils/s3-client.ts
@@ -5147,7 +5343,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
5147
5343
  function* walkMarkdown(dir, base) {
5148
5344
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
5149
5345
  if (SKIP_DIRS.has(entry.name)) continue;
5150
- const full = join31(dir, entry.name);
5346
+ const full = join32(dir, entry.name);
5151
5347
  if (entry.isDirectory()) {
5152
5348
  yield* walkMarkdown(full, base);
5153
5349
  } else if (entry.name.endsWith(".md")) {
@@ -5170,8 +5366,8 @@ async function runBackupSync(input) {
5170
5366
  let failed = 0;
5171
5367
  const files = [...walkMarkdown(input.vault, input.vault)];
5172
5368
  for (const relPath of files) {
5173
- const absPath = join31(input.vault, relPath);
5174
- const localStat = statSync3(absPath);
5369
+ const absPath = join32(input.vault, relPath);
5370
+ const localStat = statSync4(absPath);
5175
5371
  let needsUpload = true;
5176
5372
  try {
5177
5373
  const head = await client.send(new HeadObjectCommand({ Bucket: input.bucket, Key: relPath }));
@@ -5246,9 +5442,9 @@ async function runBackupRestore(input) {
5246
5442
  const objects = list.Contents ?? [];
5247
5443
  for (const obj of objects) {
5248
5444
  if (!obj.Key) continue;
5249
- const localPath = join31(target, obj.Key);
5445
+ const localPath = join32(target, obj.Key);
5250
5446
  try {
5251
- const localStat = statSync3(localPath);
5447
+ const localStat = statSync4(localPath);
5252
5448
  if (obj.LastModified && localStat.mtime > obj.LastModified) {
5253
5449
  conflicts++;
5254
5450
  continue;
@@ -5292,11 +5488,11 @@ async function runBackupRestore(input) {
5292
5488
  }
5293
5489
 
5294
5490
  // src/commands/status.ts
5295
- import { existsSync as existsSync10, statSync as statSync4 } from "fs";
5296
- import { readFile as readFile20 } from "fs/promises";
5297
- 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";
5298
5494
  async function runStatus(input) {
5299
- if (!existsSync10(input.vault)) {
5495
+ if (!existsSync11(input.vault)) {
5300
5496
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
5301
5497
  }
5302
5498
  const scan = await scanVault(input.vault);
@@ -5321,7 +5517,7 @@ async function runStatus(input) {
5321
5517
  const compound = scan.data.compound.length;
5322
5518
  let schemaVersion = "v1";
5323
5519
  try {
5324
- const schemaContent = await readFile20(join32(input.vault, "SCHEMA.md"), "utf8");
5520
+ const schemaContent = await readFile21(join33(input.vault, "SCHEMA.md"), "utf8");
5325
5521
  const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
5326
5522
  if (versionMatch) schemaVersion = versionMatch[1];
5327
5523
  } catch {
@@ -5337,7 +5533,7 @@ async function runStatus(input) {
5337
5533
  let maxTime = 0;
5338
5534
  for (const page of allPages) {
5339
5535
  try {
5340
- const st = statSync4(page.absPath);
5536
+ const st = statSync5(page.absPath);
5341
5537
  if (st.mtimeMs > maxTime) {
5342
5538
  maxTime = st.mtimeMs;
5343
5539
  lastModified = st.mtime.toISOString();
@@ -5381,8 +5577,8 @@ async function runStatus(input) {
5381
5577
  }
5382
5578
 
5383
5579
  // src/commands/seed.ts
5384
- import { mkdir as mkdir12, writeFile as writeFile17, stat as stat7 } from "fs/promises";
5385
- 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";
5386
5582
  var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5387
5583
  var EXAMPLE_PAGES = {
5388
5584
  "entities/example-project.md": `---
@@ -5451,30 +5647,30 @@ Real sources are immutable after ingestion \u2014 never edit them.
5451
5647
  `;
5452
5648
  async function runSeed(input) {
5453
5649
  try {
5454
- await stat7(join33(input.vault, "SCHEMA.md"));
5650
+ await stat7(join34(input.vault, "SCHEMA.md"));
5455
5651
  } catch {
5456
5652
  return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
5457
5653
  }
5458
5654
  const created = [];
5459
5655
  const skipped = [];
5460
5656
  for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
5461
- const absPath = join33(input.vault, relPath);
5657
+ const absPath = join34(input.vault, relPath);
5462
5658
  try {
5463
5659
  await stat7(absPath);
5464
5660
  skipped.push(relPath);
5465
5661
  } catch {
5466
- await mkdir12(join33(absPath, ".."), { recursive: true });
5467
- await writeFile17(absPath, content, "utf8");
5662
+ await mkdir13(join34(absPath, ".."), { recursive: true });
5663
+ await writeFile18(absPath, content, "utf8");
5468
5664
  created.push(relPath);
5469
5665
  }
5470
5666
  }
5471
- const rawPath = join33(input.vault, "raw", "articles", "example-source.md");
5667
+ const rawPath = join34(input.vault, "raw", "articles", "example-source.md");
5472
5668
  try {
5473
5669
  await stat7(rawPath);
5474
5670
  skipped.push("raw/articles/example-source.md");
5475
5671
  } catch {
5476
- await mkdir12(join33(rawPath, ".."), { recursive: true });
5477
- await writeFile17(rawPath, EXAMPLE_RAW, "utf8");
5672
+ await mkdir13(join34(rawPath, ".."), { recursive: true });
5673
+ await writeFile18(rawPath, EXAMPLE_RAW, "utf8");
5478
5674
  created.push("raw/articles/example-source.md");
5479
5675
  }
5480
5676
  if (created.length > 0) {
@@ -5496,9 +5692,9 @@ async function runSeed(input) {
5496
5692
  }
5497
5693
 
5498
5694
  // src/commands/canvas.ts
5499
- import { readFile as readFile21, writeFile as writeFile18 } from "fs/promises";
5500
- import { existsSync as existsSync11 } from "fs";
5501
- 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";
5502
5698
  var NODE_WIDTH = 240;
5503
5699
  var NODE_HEIGHT = 60;
5504
5700
  var COLUMN_SPACING = 400;
@@ -5576,8 +5772,8 @@ function buildCanvasEdges(adjacency) {
5576
5772
  return edges;
5577
5773
  }
5578
5774
  async function runCanvasGenerate(input) {
5579
- const graphPath = input.graphPath ?? join34(input.vault, ".skillwiki", "graph.json");
5580
- if (!existsSync11(graphPath)) {
5775
+ const graphPath = input.graphPath ?? join35(input.vault, ".skillwiki", "graph.json");
5776
+ if (!existsSync12(graphPath)) {
5581
5777
  return {
5582
5778
  exitCode: ExitCode.FILE_NOT_FOUND,
5583
5779
  result: err("FILE_NOT_FOUND", {
@@ -5588,7 +5784,7 @@ async function runCanvasGenerate(input) {
5588
5784
  }
5589
5785
  let raw;
5590
5786
  try {
5591
- raw = await readFile21(graphPath, "utf8");
5787
+ raw = await readFile22(graphPath, "utf8");
5592
5788
  } catch (e) {
5593
5789
  return {
5594
5790
  exitCode: ExitCode.FILE_NOT_FOUND,
@@ -5614,9 +5810,9 @@ async function runCanvasGenerate(input) {
5614
5810
  const nodes = buildCanvasNodes(paths);
5615
5811
  const edges = buildCanvasEdges(graph.adjacency);
5616
5812
  const canvas = { nodes, edges };
5617
- const outPath = join34(input.vault, "vault-graph.canvas");
5813
+ const outPath = join35(input.vault, "vault-graph.canvas");
5618
5814
  try {
5619
- await writeFile18(outPath, JSON.stringify(canvas, null, 2));
5815
+ await writeFile19(outPath, JSON.stringify(canvas, null, 2));
5620
5816
  } catch (e) {
5621
5817
  return {
5622
5818
  exitCode: ExitCode.WRITE_FAILED,
@@ -5636,8 +5832,8 @@ written: ${outPath}`
5636
5832
  }
5637
5833
 
5638
5834
  // src/commands/query.ts
5639
- import { readFile as readFile22, stat as stat8 } from "fs/promises";
5640
- import { join as join35 } from "path";
5835
+ import { readFile as readFile23, stat as stat8 } from "fs/promises";
5836
+ import { join as join36 } from "path";
5641
5837
  var W_KEYWORD = 2;
5642
5838
  var W_SOURCE_OVERLAP = 4;
5643
5839
  var W_WIKILINK = 3;
@@ -5758,7 +5954,7 @@ function computeKeywordScore(terms, title, tags, body) {
5758
5954
  return score;
5759
5955
  }
5760
5956
  async function loadOrBuildGraph(vault) {
5761
- const graphPath = join35(vault, ".skillwiki", "graph.json");
5957
+ const graphPath = join36(vault, ".skillwiki", "graph.json");
5762
5958
  let needsBuild = false;
5763
5959
  try {
5764
5960
  const fileStat = await stat8(graphPath);
@@ -5772,7 +5968,7 @@ async function loadOrBuildGraph(vault) {
5772
5968
  if (buildResult.exitCode !== 0) return null;
5773
5969
  }
5774
5970
  try {
5775
- const raw = await readFile22(graphPath, "utf8");
5971
+ const raw = await readFile23(graphPath, "utf8");
5776
5972
  return JSON.parse(raw);
5777
5973
  } catch {
5778
5974
  return null;
@@ -5780,14 +5976,14 @@ async function loadOrBuildGraph(vault) {
5780
5976
  }
5781
5977
 
5782
5978
  // src/utils/auto-commit.ts
5783
- import { existsSync as existsSync12 } from "fs";
5784
- import { join as join36 } from "path";
5979
+ import { existsSync as existsSync13 } from "fs";
5980
+ import { join as join37 } from "path";
5785
5981
  async function postCommit(vault, exitCode) {
5786
5982
  if (exitCode !== 0) return;
5787
5983
  const home = process.env.HOME ?? "";
5788
5984
  const dotenv = await parseDotenvFile(configPath(home));
5789
5985
  if (dotenv["AUTO_COMMIT"] === "false") return;
5790
- if (!existsSync12(join36(vault, ".git"))) return;
5986
+ if (!existsSync13(join37(vault, ".git"))) return;
5791
5987
  const lastOps = readLastOp(vault);
5792
5988
  if (lastOps.length === 0) return;
5793
5989
  const porcelain = git(vault, ["status", "--porcelain"]);
@@ -5838,7 +6034,7 @@ program.command("validate <file>").description("validate vault page frontmatter
5838
6034
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
5839
6035
  });
5840
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) => {
5841
- const out = opts.out ?? join37(vault, ".skillwiki", "graph.json");
6037
+ const out = opts.out ?? join38(vault, ".skillwiki", "graph.json");
5842
6038
  emit(await runGraphBuild({ vault, out }), vault);
5843
6039
  });
5844
6040
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
@@ -5944,10 +6140,15 @@ program.command("topic-map-check [vault]").description("check whether a topic ma
5944
6140
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5945
6141
  else emit(await runTopicMapCheck({ vault: v.vault, threshold: opts.threshold }), v.vault);
5946
6142
  });
5947
- program.command("stale [vault]").description("identify stale transcripts and incomplete work items").option("--archive", "move stale items to _archive/", false).option("--days <n>", "staleness threshold in days", (s) => parseInt(s, 10), 3).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6143
+ program.command("stale [vault]").description("identify stale transcripts and incomplete work items").option("--archive", "move stale items to _archive/", false).option("--days <n>", "staleness threshold in days", (s) => parseInt(s, 10), 3).option("--force-scan", "infer kind/project from filename and content when frontmatter is missing", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6144
+ const v = await resolveVaultArg(vault, opts.wiki);
6145
+ if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6146
+ else emit(await runStale({ vault: v.vault, days: opts.days, archive: !!opts.archive, forceScan: !!opts.forceScan }), v.vault);
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) => {
5948
6149
  const v = await resolveVaultArg(vault, opts.wiki);
5949
6150
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5950
- else emit(await runStale({ vault: v.vault, days: opts.days, archive: !!opts.archive }), v.vault);
6151
+ else emit(await runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug }), v.vault);
5951
6152
  });
5952
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) => {
5953
6154
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -6109,7 +6310,7 @@ program.command("seed [vault]").description("populate a vault with example conte
6109
6310
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6110
6311
  else emit(await runSeed({ vault: v.vault }), v.vault);
6111
6312
  });
6112
- program.command("observe [vault]").description("create a raw transcript observation entry").requiredOption("--text <text>", "observation text").option("--kind <kind>", "observation kind (note|bug|task|idea|session-log)", "note").option("--project <slug>", "associated project slug").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6313
+ program.command("observe [vault]").description("create a raw transcript observation entry").requiredOption("--text <text>", "observation text").option("--kind <kind>", "observation kind (note|bug|task|idea|session-log)", "task").option("--project <slug>", "associated project slug (required for task/bug claim detection)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
6113
6314
  const v = await resolveVaultArg(vault, opts.wiki);
6114
6315
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
6115
6316
  else emit(await runObserve({