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