skillwiki 0.9.1-beta.1 → 0.9.2
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 +356 -321
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "./chunk-TPS5XD2J.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
|
-
import { join as
|
|
17
|
+
import { join as join48 } from "path";
|
|
18
18
|
import { Command as Command2 } from "commander";
|
|
19
19
|
|
|
20
20
|
// ../shared/src/exit-codes.ts
|
|
@@ -1159,8 +1159,8 @@ function simulateRemoval(adj, removed) {
|
|
|
1159
1159
|
}
|
|
1160
1160
|
|
|
1161
1161
|
// src/commands/audit.ts
|
|
1162
|
-
import { readFile as readFile5, stat as
|
|
1163
|
-
import { dirname as dirname3, resolve as resolve2, join as
|
|
1162
|
+
import { readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
1163
|
+
import { dirname as dirname3, resolve as resolve2, join as join6 } from "path";
|
|
1164
1164
|
|
|
1165
1165
|
// src/parsers/citations.ts
|
|
1166
1166
|
var FENCE2 = /```[\s\S]*?```/g;
|
|
@@ -1178,6 +1178,7 @@ function extractCitationMarkers(body) {
|
|
|
1178
1178
|
const out = [];
|
|
1179
1179
|
let m;
|
|
1180
1180
|
while ((m = MARKER_RE.exec(stripped)) !== null) {
|
|
1181
|
+
if (m[1] === "raw/...") continue;
|
|
1181
1182
|
out.push({ marker: m[0], target: m[1] });
|
|
1182
1183
|
}
|
|
1183
1184
|
return out;
|
|
@@ -1267,6 +1268,41 @@ function hasWikilinkCitations(body) {
|
|
|
1267
1268
|
return /\[\[raw\/[^\]]+\]\]/.test(stripped);
|
|
1268
1269
|
}
|
|
1269
1270
|
|
|
1271
|
+
// src/utils/raw-source.ts
|
|
1272
|
+
import { existsSync } from "fs";
|
|
1273
|
+
import { stat as stat2 } from "fs/promises";
|
|
1274
|
+
import { join as join5 } from "path";
|
|
1275
|
+
function normalizeRawSourceTarget(entry) {
|
|
1276
|
+
let target = entry.trim().replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
1277
|
+
target = target.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
1278
|
+
if (!target.startsWith("raw/") && !target.startsWith("_archive/raw/")) return null;
|
|
1279
|
+
return target;
|
|
1280
|
+
}
|
|
1281
|
+
function rawSourceTargetCandidates(vault, target) {
|
|
1282
|
+
const normalized = normalizeRawSourceTarget(target);
|
|
1283
|
+
if (!normalized) return [];
|
|
1284
|
+
const candidates = [join5(vault, normalized)];
|
|
1285
|
+
if (!normalized.endsWith(".md")) candidates.push(join5(vault, `${normalized}.md`));
|
|
1286
|
+
if (normalized.startsWith("raw/")) {
|
|
1287
|
+
candidates.push(join5(vault, "_archive", normalized));
|
|
1288
|
+
if (!normalized.endsWith(".md")) candidates.push(join5(vault, "_archive", `${normalized}.md`));
|
|
1289
|
+
}
|
|
1290
|
+
return [...new Set(candidates)];
|
|
1291
|
+
}
|
|
1292
|
+
function rawSourceTargetExistsSync(vault, target) {
|
|
1293
|
+
return rawSourceTargetCandidates(vault, target).some((candidate) => existsSync(candidate));
|
|
1294
|
+
}
|
|
1295
|
+
async function rawSourceTargetExists(vault, target) {
|
|
1296
|
+
for (const candidate of rawSourceTargetCandidates(vault, target)) {
|
|
1297
|
+
try {
|
|
1298
|
+
await stat2(candidate);
|
|
1299
|
+
return true;
|
|
1300
|
+
} catch {
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return false;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1270
1306
|
// src/commands/audit.ts
|
|
1271
1307
|
async function runAudit(input) {
|
|
1272
1308
|
let text;
|
|
@@ -1283,12 +1319,7 @@ async function runAudit(input) {
|
|
|
1283
1319
|
if (!vault) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID") };
|
|
1284
1320
|
const markers = extractCitationMarkers(body);
|
|
1285
1321
|
const resolved = await Promise.all(markers.map(async (m) => {
|
|
1286
|
-
|
|
1287
|
-
await stat2(join5(vault, m.target));
|
|
1288
|
-
return { ...m, resolved: true };
|
|
1289
|
-
} catch {
|
|
1290
|
-
return { ...m, resolved: false };
|
|
1291
|
-
}
|
|
1322
|
+
return { ...m, resolved: await rawSourceTargetExists(vault, m.target) };
|
|
1292
1323
|
}));
|
|
1293
1324
|
const sources = (fm.data.sources ?? []).map((s) => s.replace(/^\^\[/, "").replace(/\]$/, ""));
|
|
1294
1325
|
const referenced = new Set(resolved.map((m) => m.target));
|
|
@@ -1329,7 +1360,7 @@ async function findVaultRoot(start) {
|
|
|
1329
1360
|
let cur = start;
|
|
1330
1361
|
for (let i = 0; i < 20; i++) {
|
|
1331
1362
|
try {
|
|
1332
|
-
await
|
|
1363
|
+
await stat3(join6(cur, "SCHEMA.md"));
|
|
1333
1364
|
return cur;
|
|
1334
1365
|
} catch {
|
|
1335
1366
|
}
|
|
@@ -1381,17 +1412,17 @@ async function validateCompoundReferences(vault) {
|
|
|
1381
1412
|
}
|
|
1382
1413
|
|
|
1383
1414
|
// src/commands/install.ts
|
|
1384
|
-
import { readdir as readdir2, stat as
|
|
1385
|
-
import { join as
|
|
1415
|
+
import { readdir as readdir2, stat as stat5, symlink, unlink, mkdir as mkdir4, readFile as readFile6 } from "fs/promises";
|
|
1416
|
+
import { join as join7, resolve as resolve3, dirname as dirname5 } from "path";
|
|
1386
1417
|
|
|
1387
1418
|
// src/utils/install-fs.ts
|
|
1388
|
-
import { copyFile, mkdir as mkdir3, rename, writeFile as writeFile4, stat as
|
|
1419
|
+
import { copyFile, mkdir as mkdir3, rename, writeFile as writeFile4, stat as stat4 } from "fs/promises";
|
|
1389
1420
|
import { dirname as dirname4 } from "path";
|
|
1390
1421
|
async function atomicCopyWithBackup(src, dst) {
|
|
1391
1422
|
await mkdir3(dirname4(dst), { recursive: true });
|
|
1392
1423
|
let backupPath = null;
|
|
1393
1424
|
try {
|
|
1394
|
-
await
|
|
1425
|
+
await stat4(dst);
|
|
1395
1426
|
backupPath = `${dst}.bak`;
|
|
1396
1427
|
await copyFile(dst, backupPath);
|
|
1397
1428
|
} catch {
|
|
@@ -1445,7 +1476,7 @@ async function runInstall(input) {
|
|
|
1445
1476
|
const withSkill = [];
|
|
1446
1477
|
for (const d of dirs) {
|
|
1447
1478
|
try {
|
|
1448
|
-
await
|
|
1479
|
+
await stat5(join7(input.skillsRoot, d.name, "SKILL.md"));
|
|
1449
1480
|
withSkill.push(d.name);
|
|
1450
1481
|
} catch {
|
|
1451
1482
|
}
|
|
@@ -1462,10 +1493,10 @@ async function runInstall(input) {
|
|
|
1462
1493
|
const version_warnings = [];
|
|
1463
1494
|
const skillMetas = {};
|
|
1464
1495
|
for (const name of entries) {
|
|
1465
|
-
const src =
|
|
1466
|
-
const dst =
|
|
1496
|
+
const src = join7(input.skillsRoot, name, "SKILL.md");
|
|
1497
|
+
const dst = join7(input.target, name, "SKILL.md");
|
|
1467
1498
|
try {
|
|
1468
|
-
await
|
|
1499
|
+
await stat5(src);
|
|
1469
1500
|
} catch {
|
|
1470
1501
|
return { exitCode: ExitCode.PREFLIGHT_FAILED, result: err("PREFLIGHT_FAILED", { missing: src }) };
|
|
1471
1502
|
}
|
|
@@ -1504,10 +1535,10 @@ async function runInstall(input) {
|
|
|
1504
1535
|
if (r.data.backupPath) backed_up.push(r.data.backupPath);
|
|
1505
1536
|
}
|
|
1506
1537
|
}
|
|
1507
|
-
const binSrc =
|
|
1538
|
+
const binSrc = join7(input.skillsRoot, "bin", "skillwiki");
|
|
1508
1539
|
try {
|
|
1509
|
-
await
|
|
1510
|
-
const binDst =
|
|
1540
|
+
await stat5(binSrc);
|
|
1541
|
+
const binDst = join7(input.target, "bin", "skillwiki");
|
|
1511
1542
|
if (!input.dryRun) {
|
|
1512
1543
|
if (input.symlink) {
|
|
1513
1544
|
const r = await createSymlink(binSrc, binDst);
|
|
@@ -1524,7 +1555,7 @@ async function runInstall(input) {
|
|
|
1524
1555
|
}
|
|
1525
1556
|
} catch {
|
|
1526
1557
|
}
|
|
1527
|
-
const manifest_path =
|
|
1558
|
+
const manifest_path = join7(input.target, "wiki-manifest.json");
|
|
1528
1559
|
if (!input.dryRun) await writeManifest(manifest_path, { installed, backed_up, symlink: input.symlink || void 0, skills: skillMetas });
|
|
1529
1560
|
const mode = input.symlink ? "symlink (dev mode)" : "copy";
|
|
1530
1561
|
const hintLines = [
|
|
@@ -1566,7 +1597,7 @@ async function runPath(input) {
|
|
|
1566
1597
|
}
|
|
1567
1598
|
|
|
1568
1599
|
// src/utils/lang.ts
|
|
1569
|
-
import { join as
|
|
1600
|
+
import { join as join8 } from "path";
|
|
1570
1601
|
var ALIASES = {
|
|
1571
1602
|
english: "en",
|
|
1572
1603
|
en: "en",
|
|
@@ -1589,7 +1620,7 @@ async function resolveLang(input) {
|
|
|
1589
1620
|
if (input.envValue !== void 0 && input.envValue.length > 0) {
|
|
1590
1621
|
return { value: input.envValue, source: "env", canonical: normalizeLang(input.envValue) };
|
|
1591
1622
|
}
|
|
1592
|
-
const dotenv = await parseDotenvFile(
|
|
1623
|
+
const dotenv = await parseDotenvFile(join8(input.home, ".skillwiki", ".env"));
|
|
1593
1624
|
if (dotenv.WIKI_LANG !== void 0) {
|
|
1594
1625
|
return { value: dotenv.WIKI_LANG, source: "skillwiki-dotenv", canonical: normalizeLang(dotenv.WIKI_LANG) };
|
|
1595
1626
|
}
|
|
@@ -1597,7 +1628,7 @@ async function resolveLang(input) {
|
|
|
1597
1628
|
}
|
|
1598
1629
|
|
|
1599
1630
|
// src/commands/lang.ts
|
|
1600
|
-
import { join as
|
|
1631
|
+
import { join as join9 } from "path";
|
|
1601
1632
|
async function runLang(input) {
|
|
1602
1633
|
const resolved = await resolveLang({ flag: input.flag, envValue: input.envValue, home: input.home });
|
|
1603
1634
|
let chain;
|
|
@@ -1606,7 +1637,7 @@ async function runLang(input) {
|
|
|
1606
1637
|
{ source: "flag", matched: input.flag !== void 0 && input.flag.length > 0, value: input.flag },
|
|
1607
1638
|
{ source: "env", matched: input.envValue !== void 0 && input.envValue.length > 0, value: input.envValue }
|
|
1608
1639
|
];
|
|
1609
|
-
const sw = await parseDotenvFile(
|
|
1640
|
+
const sw = await parseDotenvFile(join9(input.home, ".skillwiki", ".env"));
|
|
1610
1641
|
chain.push({ source: "skillwiki-dotenv", matched: sw.WIKI_LANG !== void 0, value: sw.WIKI_LANG });
|
|
1611
1642
|
chain.push({ source: "default", matched: resolved.source === "default", value: "en" });
|
|
1612
1643
|
}
|
|
@@ -1624,7 +1655,7 @@ async function runLang(input) {
|
|
|
1624
1655
|
|
|
1625
1656
|
// src/commands/init.ts
|
|
1626
1657
|
import { mkdir as mkdir5, readFile as readFile7, readdir as readdir3, writeFile as writeFile5 } from "fs/promises";
|
|
1627
|
-
import { join as
|
|
1658
|
+
import { join as join11 } from "path";
|
|
1628
1659
|
|
|
1629
1660
|
// src/parsers/taxonomy.ts
|
|
1630
1661
|
import yaml2 from "js-yaml";
|
|
@@ -1652,16 +1683,16 @@ function extractTaxonomy(schemaText) {
|
|
|
1652
1683
|
}
|
|
1653
1684
|
|
|
1654
1685
|
// src/utils/last-op.ts
|
|
1655
|
-
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, unlinkSync, existsSync } from "fs";
|
|
1656
|
-
import { join as
|
|
1686
|
+
import { readFileSync as readFileSync2, writeFileSync, mkdirSync, unlinkSync, existsSync as existsSync2 } from "fs";
|
|
1687
|
+
import { join as join10 } from "path";
|
|
1657
1688
|
var LAST_OP_DIR = ".skillwiki";
|
|
1658
1689
|
var LAST_OP_FILE = "last-op.json";
|
|
1659
1690
|
function lastOpPath(vault) {
|
|
1660
|
-
return
|
|
1691
|
+
return join10(vault, LAST_OP_DIR, LAST_OP_FILE);
|
|
1661
1692
|
}
|
|
1662
1693
|
function readLastOp(vault) {
|
|
1663
1694
|
const p = lastOpPath(vault);
|
|
1664
|
-
if (!
|
|
1695
|
+
if (!existsSync2(p)) return [];
|
|
1665
1696
|
try {
|
|
1666
1697
|
const raw = readFileSync2(p, "utf8");
|
|
1667
1698
|
const parsed = JSON.parse(raw);
|
|
@@ -1681,8 +1712,8 @@ function readLastOp(vault) {
|
|
|
1681
1712
|
function appendLastOp(vault, entry) {
|
|
1682
1713
|
const existing = readLastOp(vault);
|
|
1683
1714
|
existing.push(entry);
|
|
1684
|
-
const dir =
|
|
1685
|
-
if (!
|
|
1715
|
+
const dir = join10(vault, LAST_OP_DIR);
|
|
1716
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
1686
1717
|
writeFileSync(lastOpPath(vault), JSON.stringify(existing, null, 2), "utf8");
|
|
1687
1718
|
}
|
|
1688
1719
|
function clearLastOp(vault) {
|
|
@@ -1735,13 +1766,13 @@ async function discoverTagsFromPages(target, knownSlugs) {
|
|
|
1735
1766
|
for (const dir of ["entities", "concepts", "comparisons", "queries"]) {
|
|
1736
1767
|
let entries;
|
|
1737
1768
|
try {
|
|
1738
|
-
entries = (await readdir3(
|
|
1769
|
+
entries = (await readdir3(join11(target, dir), { withFileTypes: true })).filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name);
|
|
1739
1770
|
} catch {
|
|
1740
1771
|
continue;
|
|
1741
1772
|
}
|
|
1742
1773
|
for (const file of entries) {
|
|
1743
1774
|
try {
|
|
1744
|
-
const text = await readFile7(
|
|
1775
|
+
const text = await readFile7(join11(target, dir, file), "utf8");
|
|
1745
1776
|
const fm = extractFrontmatter(text);
|
|
1746
1777
|
if (!fm.ok || !fm.data.tags || !Array.isArray(fm.data.tags)) continue;
|
|
1747
1778
|
for (const t of fm.data.tags) {
|
|
@@ -1760,7 +1791,7 @@ async function runInit(input) {
|
|
|
1760
1791
|
const canonicalLang = langRes.canonical;
|
|
1761
1792
|
let oldSchemaText;
|
|
1762
1793
|
try {
|
|
1763
|
-
oldSchemaText = await readFile7(
|
|
1794
|
+
oldSchemaText = await readFile7(join11(target, "SCHEMA.md"), "utf8");
|
|
1764
1795
|
} catch {
|
|
1765
1796
|
}
|
|
1766
1797
|
if (oldSchemaText && !input.force) {
|
|
@@ -1769,7 +1800,7 @@ async function runInit(input) {
|
|
|
1769
1800
|
result: err("INIT_TARGET_NOT_EMPTY", { target })
|
|
1770
1801
|
};
|
|
1771
1802
|
}
|
|
1772
|
-
const envPath =
|
|
1803
|
+
const envPath = join11(input.home, ".skillwiki", ".env");
|
|
1773
1804
|
let existingEnvRaw = "";
|
|
1774
1805
|
try {
|
|
1775
1806
|
existingEnvRaw = await readFile7(envPath, "utf8");
|
|
@@ -1795,7 +1826,7 @@ async function runInit(input) {
|
|
|
1795
1826
|
try {
|
|
1796
1827
|
await mkdir5(target, { recursive: true });
|
|
1797
1828
|
for (const d of VAULT_DIRS) {
|
|
1798
|
-
await mkdir5(
|
|
1829
|
+
await mkdir5(join11(target, d), { recursive: true });
|
|
1799
1830
|
created.push(d + "/");
|
|
1800
1831
|
}
|
|
1801
1832
|
} catch (e) {
|
|
@@ -1824,9 +1855,9 @@ async function runInit(input) {
|
|
|
1824
1855
|
const discovered_tags = discovered.length;
|
|
1825
1856
|
const fullTaxonomyYaml = discovered.length > 0 ? taxonomy.map((t) => ` - ${t}`).join("\n") + "\n # --- Discovered from existing pages ---\n" + discovered.map((t) => ` - ${t}`).join("\n") : taxonomy.map((t) => ` - ${t}`).join("\n");
|
|
1826
1857
|
try {
|
|
1827
|
-
const schemaTpl = await readFile7(
|
|
1858
|
+
const schemaTpl = await readFile7(join11(input.templates, "SCHEMA.md"), "utf8");
|
|
1828
1859
|
const schema = schemaTpl.replace("{{DOMAIN}}", domain).replace("{{WIKI_LANG}}", canonicalLang).replace("{{TAXONOMY_YAML}}", fullTaxonomyYaml);
|
|
1829
|
-
await writeFile5(
|
|
1860
|
+
await writeFile5(join11(target, "SCHEMA.md"), schema, "utf8");
|
|
1830
1861
|
created.push("SCHEMA.md");
|
|
1831
1862
|
} catch (e) {
|
|
1832
1863
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: "SCHEMA.md", message: String(e) }) };
|
|
@@ -1834,7 +1865,7 @@ async function runInit(input) {
|
|
|
1834
1865
|
const preserved = [];
|
|
1835
1866
|
async function writeOrPreserve(fileName, render) {
|
|
1836
1867
|
try {
|
|
1837
|
-
const existing = await readFile7(
|
|
1868
|
+
const existing = await readFile7(join11(target, fileName), "utf8");
|
|
1838
1869
|
if (existing.split("\n").length > 10) {
|
|
1839
1870
|
preserved.push(fileName);
|
|
1840
1871
|
return void 0;
|
|
@@ -1842,7 +1873,7 @@ async function runInit(input) {
|
|
|
1842
1873
|
} catch {
|
|
1843
1874
|
}
|
|
1844
1875
|
try {
|
|
1845
|
-
await writeFile5(
|
|
1876
|
+
await writeFile5(join11(target, fileName), await render(), "utf8");
|
|
1846
1877
|
created.push(fileName);
|
|
1847
1878
|
return void 0;
|
|
1848
1879
|
} catch (e) {
|
|
@@ -1850,7 +1881,7 @@ async function runInit(input) {
|
|
|
1850
1881
|
}
|
|
1851
1882
|
}
|
|
1852
1883
|
const err1 = await writeOrPreserve("index.md", async () => {
|
|
1853
|
-
const tpl = await readFile7(
|
|
1884
|
+
const tpl = await readFile7(join11(input.templates, "index.md"), "utf8");
|
|
1854
1885
|
return tpl.replace("{{INIT_DATE}}", today);
|
|
1855
1886
|
});
|
|
1856
1887
|
if (err1) return err1;
|
|
@@ -1877,7 +1908,7 @@ async function runInit(input) {
|
|
|
1877
1908
|
});
|
|
1878
1909
|
if (errTemplate) return errTemplate;
|
|
1879
1910
|
const err22 = await writeOrPreserve("log.md", async () => {
|
|
1880
|
-
const tpl = await readFile7(
|
|
1911
|
+
const tpl = await readFile7(join11(input.templates, "log.md"), "utf8");
|
|
1881
1912
|
return tpl.replace(/\{\{INIT_DATE\}\}/g, today).replace("{{DOMAIN}}", domain).replace("{{WIKI_LANG}}", canonicalLang);
|
|
1882
1913
|
});
|
|
1883
1914
|
if (err22) return err22;
|
|
@@ -1975,11 +2006,11 @@ ${broken.map((b) => ` ${b.page}:[[${b.slug}]] (line ${b.line})`).join("\n")}` }
|
|
|
1975
2006
|
|
|
1976
2007
|
// src/commands/tag-audit.ts
|
|
1977
2008
|
import { readFile as readFile8 } from "fs/promises";
|
|
1978
|
-
import { join as
|
|
2009
|
+
import { join as join12 } from "path";
|
|
1979
2010
|
async function runTagAudit(input) {
|
|
1980
2011
|
const scan = await scanVault(input.vault);
|
|
1981
2012
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
1982
|
-
const schemaText = await readFile8(
|
|
2013
|
+
const schemaText = await readFile8(join12(input.vault, "SCHEMA.md"), "utf8");
|
|
1983
2014
|
const tax = extractTaxonomy(schemaText);
|
|
1984
2015
|
if (!tax.ok) return { exitCode: ExitCode.INVALID_FRONTMATTER, result: tax };
|
|
1985
2016
|
const allowed = new Set(tax.data);
|
|
@@ -2004,13 +2035,13 @@ async function runTagAudit(input) {
|
|
|
2004
2035
|
|
|
2005
2036
|
// src/commands/index-check.ts
|
|
2006
2037
|
import { readFile as readFile9 } from "fs/promises";
|
|
2007
|
-
import { join as
|
|
2038
|
+
import { join as join13 } from "path";
|
|
2008
2039
|
async function runIndexCheck(input) {
|
|
2009
2040
|
const scan = await scanVault(input.vault);
|
|
2010
2041
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2011
2042
|
let indexText = "";
|
|
2012
2043
|
try {
|
|
2013
|
-
indexText = await readFile9(
|
|
2044
|
+
indexText = await readFile9(join13(input.vault, "index.md"), "utf8");
|
|
2014
2045
|
} catch {
|
|
2015
2046
|
}
|
|
2016
2047
|
const indexSlugsLower = /* @__PURE__ */ new Map();
|
|
@@ -2050,7 +2081,7 @@ async function runIndexCheck(input) {
|
|
|
2050
2081
|
|
|
2051
2082
|
// src/commands/stale.ts
|
|
2052
2083
|
import { readdir as readdir4, rename as rename2, mkdir as mkdir6, readFile as readFile10 } from "fs/promises";
|
|
2053
|
-
import { join as
|
|
2084
|
+
import { join as join14 } from "path";
|
|
2054
2085
|
|
|
2055
2086
|
// src/parsers/expiry-annotations.ts
|
|
2056
2087
|
var HEADING_RE = /^#{1,6}\s+(.+)$/;
|
|
@@ -2098,7 +2129,7 @@ async function runStale(input) {
|
|
|
2098
2129
|
const archived = [];
|
|
2099
2130
|
const workDirs = /* @__PURE__ */ new Map();
|
|
2100
2131
|
const workDirsBySlug = /* @__PURE__ */ new Map();
|
|
2101
|
-
const projectsDir =
|
|
2132
|
+
const projectsDir = join14(input.vault, "projects");
|
|
2102
2133
|
let projectSlugs = [];
|
|
2103
2134
|
try {
|
|
2104
2135
|
projectSlugs = (await readdir4(projectsDir, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
@@ -2111,7 +2142,7 @@ async function runStale(input) {
|
|
|
2111
2142
|
projectSlugs = [input.project];
|
|
2112
2143
|
}
|
|
2113
2144
|
for (const slug of projectSlugs) {
|
|
2114
|
-
const workPath =
|
|
2145
|
+
const workPath = join14(projectsDir, slug, "work");
|
|
2115
2146
|
let entries;
|
|
2116
2147
|
try {
|
|
2117
2148
|
entries = await readdir4(workPath, { withFileTypes: true });
|
|
@@ -2122,7 +2153,7 @@ async function runStale(input) {
|
|
|
2122
2153
|
for (const e of entries) {
|
|
2123
2154
|
if (!e.isDirectory()) continue;
|
|
2124
2155
|
const relDir = `projects/${slug}/work/${e.name}`;
|
|
2125
|
-
const absDir =
|
|
2156
|
+
const absDir = join14(workPath, e.name);
|
|
2126
2157
|
let status = "";
|
|
2127
2158
|
let files;
|
|
2128
2159
|
try {
|
|
@@ -2135,7 +2166,7 @@ async function runStale(input) {
|
|
|
2135
2166
|
for (const f of files) {
|
|
2136
2167
|
if (!f.endsWith(".md")) continue;
|
|
2137
2168
|
try {
|
|
2138
|
-
const fm = extractFrontmatter(await readFile10(
|
|
2169
|
+
const fm = extractFrontmatter(await readFile10(join14(absDir, f), "utf8"));
|
|
2139
2170
|
if (fm.ok && typeof fm.data.status === "string") {
|
|
2140
2171
|
status = fm.data.status;
|
|
2141
2172
|
break;
|
|
@@ -2159,7 +2190,7 @@ async function runStale(input) {
|
|
|
2159
2190
|
const transcriptMeta = /* @__PURE__ */ new Map();
|
|
2160
2191
|
for (const t of transcripts) {
|
|
2161
2192
|
try {
|
|
2162
|
-
const content = await readFile10(
|
|
2193
|
+
const content = await readFile10(join14(input.vault, t.relPath), "utf8");
|
|
2163
2194
|
const fm = extractFrontmatter(content);
|
|
2164
2195
|
let kind = fm.ok && typeof fm.data.kind === "string" ? fm.data.kind : "";
|
|
2165
2196
|
let project = fm.ok && typeof fm.data.project === "string" ? fm.data.project : "";
|
|
@@ -2227,7 +2258,7 @@ async function runStale(input) {
|
|
|
2227
2258
|
}
|
|
2228
2259
|
}
|
|
2229
2260
|
for (const [relDir] of workDirs) {
|
|
2230
|
-
const specPath =
|
|
2261
|
+
const specPath = join14(input.vault, relDir, "spec.md");
|
|
2231
2262
|
try {
|
|
2232
2263
|
const specContent = await readFile10(specPath, "utf8");
|
|
2233
2264
|
const specFm = extractFrontmatter(specContent);
|
|
@@ -2258,7 +2289,7 @@ async function runStale(input) {
|
|
|
2258
2289
|
if (daysSince(dateStr) < input.days) continue;
|
|
2259
2290
|
let files;
|
|
2260
2291
|
try {
|
|
2261
|
-
files = await readdir4(
|
|
2292
|
+
files = await readdir4(join14(input.vault, relDir));
|
|
2262
2293
|
} catch {
|
|
2263
2294
|
continue;
|
|
2264
2295
|
}
|
|
@@ -2274,7 +2305,7 @@ async function runStale(input) {
|
|
|
2274
2305
|
const stale = [];
|
|
2275
2306
|
for (const page of scan.data.typedKnowledge) {
|
|
2276
2307
|
try {
|
|
2277
|
-
const text = await readFile10(
|
|
2308
|
+
const text = await readFile10(join14(input.vault, page.relPath), "utf8");
|
|
2278
2309
|
const fm = extractFrontmatter(text);
|
|
2279
2310
|
if (fm.ok && typeof fm.data.updated === "string") {
|
|
2280
2311
|
if (input.project) {
|
|
@@ -2294,7 +2325,7 @@ async function runStale(input) {
|
|
|
2294
2325
|
const staleSections = [];
|
|
2295
2326
|
for (const page of scan.data.typedKnowledge) {
|
|
2296
2327
|
try {
|
|
2297
|
-
const text = await readFile10(
|
|
2328
|
+
const text = await readFile10(join14(input.vault, page.relPath), "utf8");
|
|
2298
2329
|
const projectFilter = input.project;
|
|
2299
2330
|
if (projectFilter) {
|
|
2300
2331
|
const fm = extractFrontmatter(text);
|
|
@@ -2323,11 +2354,11 @@ async function runStale(input) {
|
|
|
2323
2354
|
}
|
|
2324
2355
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2325
2356
|
if (input.archive) {
|
|
2326
|
-
const archiveDir =
|
|
2357
|
+
const archiveDir = join14(input.vault, "_archive", today);
|
|
2327
2358
|
await mkdir6(archiveDir, { recursive: true });
|
|
2328
2359
|
const citedRawPaths = /* @__PURE__ */ new Set();
|
|
2329
2360
|
for (const page of scan.data.typedKnowledge) {
|
|
2330
|
-
const text = await readFile10(
|
|
2361
|
+
const text = await readFile10(join14(input.vault, page.relPath), "utf8").catch(() => "");
|
|
2331
2362
|
for (const line of text.split("\n")) {
|
|
2332
2363
|
for (const m of line.matchAll(/\^\[(raw\/[^\]]+)\]/g)) {
|
|
2333
2364
|
citedRawPaths.add(m[1]);
|
|
@@ -2339,9 +2370,9 @@ async function runStale(input) {
|
|
|
2339
2370
|
}
|
|
2340
2371
|
for (const t of staleTranscripts) {
|
|
2341
2372
|
if (citedRawPaths.has(t.path) || citedRawPaths.has(t.path.replace(/\.md$/, ""))) continue;
|
|
2342
|
-
const dest =
|
|
2373
|
+
const dest = join14(archiveDir, t.path.split("/").pop());
|
|
2343
2374
|
try {
|
|
2344
|
-
await rename2(
|
|
2375
|
+
await rename2(join14(input.vault, t.path), dest);
|
|
2345
2376
|
archived.push(t.path);
|
|
2346
2377
|
} catch {
|
|
2347
2378
|
}
|
|
@@ -2351,18 +2382,18 @@ async function runStale(input) {
|
|
|
2351
2382
|
if (parts.length >= 4 && parts[0] === "projects") {
|
|
2352
2383
|
const slug = parts[1];
|
|
2353
2384
|
const itemName = parts[3];
|
|
2354
|
-
const histDir =
|
|
2385
|
+
const histDir = join14(input.vault, "projects", slug, "history", "archived-work");
|
|
2355
2386
|
await mkdir6(histDir, { recursive: true });
|
|
2356
|
-
const dest =
|
|
2387
|
+
const dest = join14(histDir, itemName);
|
|
2357
2388
|
try {
|
|
2358
|
-
await rename2(
|
|
2389
|
+
await rename2(join14(input.vault, w.path), dest);
|
|
2359
2390
|
archived.push(w.path);
|
|
2360
2391
|
} catch {
|
|
2361
2392
|
}
|
|
2362
2393
|
} else {
|
|
2363
|
-
const dest =
|
|
2394
|
+
const dest = join14(archiveDir, w.path.replace(/\//g, "_"));
|
|
2364
2395
|
try {
|
|
2365
|
-
await rename2(
|
|
2396
|
+
await rename2(join14(input.vault, w.path), dest);
|
|
2366
2397
|
archived.push(w.path);
|
|
2367
2398
|
} catch {
|
|
2368
2399
|
}
|
|
@@ -2402,8 +2433,8 @@ async function runStale(input) {
|
|
|
2402
2433
|
|
|
2403
2434
|
// src/commands/claim.ts
|
|
2404
2435
|
import { mkdir as mkdir7, writeFile as writeFile6, readFile as readFile11 } from "fs/promises";
|
|
2405
|
-
import { existsSync as
|
|
2406
|
-
import { join as
|
|
2436
|
+
import { existsSync as existsSync3, statSync } from "fs";
|
|
2437
|
+
import { join as join15 } from "path";
|
|
2407
2438
|
function extractDate(filename) {
|
|
2408
2439
|
const m = filename.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
2409
2440
|
return m?.[1] ?? "";
|
|
@@ -2415,11 +2446,11 @@ function extractProjectSlug(projectField) {
|
|
|
2415
2446
|
return projectField.replace(/^\[\[/, "").replace(/\]\]$/, "").replace(/^"|"$/g, "");
|
|
2416
2447
|
}
|
|
2417
2448
|
async function runClaim(input) {
|
|
2418
|
-
if (!
|
|
2449
|
+
if (!existsSync3(input.vault) || !statSync(input.vault).isDirectory()) {
|
|
2419
2450
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { path: input.vault }) };
|
|
2420
2451
|
}
|
|
2421
|
-
const absTranscript =
|
|
2422
|
-
if (!
|
|
2452
|
+
const absTranscript = join15(input.vault, input.transcript);
|
|
2453
|
+
if (!existsSync3(absTranscript)) {
|
|
2423
2454
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: input.transcript }) };
|
|
2424
2455
|
}
|
|
2425
2456
|
const content = await readFile11(absTranscript, "utf8");
|
|
@@ -2434,8 +2465,8 @@ async function runClaim(input) {
|
|
|
2434
2465
|
result: err("SCHEME_REJECTED", { message: "No project specified. Use --project or set project in transcript frontmatter." })
|
|
2435
2466
|
};
|
|
2436
2467
|
}
|
|
2437
|
-
const projectDir =
|
|
2438
|
-
if (!
|
|
2468
|
+
const projectDir = join15(input.vault, "projects", projectSlug);
|
|
2469
|
+
if (!existsSync3(projectDir) || !statSync(projectDir).isDirectory()) {
|
|
2439
2470
|
return {
|
|
2440
2471
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
2441
2472
|
result: err("PROJECT_NOT_FOUND", { project: projectSlug })
|
|
@@ -2451,10 +2482,10 @@ async function runClaim(input) {
|
|
|
2451
2482
|
}
|
|
2452
2483
|
const workSlug = input.slug || extractSlugFromFilename(filename);
|
|
2453
2484
|
const dirName = `${date}-${workSlug}`;
|
|
2454
|
-
const workDir =
|
|
2485
|
+
const workDir = join15(projectDir, "work", dirName);
|
|
2455
2486
|
const relWorkDir = `projects/${projectSlug}/work/${dirName}`;
|
|
2456
2487
|
const relSpecPath = `${relWorkDir}/spec.md`;
|
|
2457
|
-
if (
|
|
2488
|
+
if (existsSync3(workDir)) {
|
|
2458
2489
|
return {
|
|
2459
2490
|
exitCode: ExitCode.OK,
|
|
2460
2491
|
result: ok({
|
|
@@ -2480,7 +2511,7 @@ async function runClaim(input) {
|
|
|
2480
2511
|
`Claimed from ${input.transcript}`,
|
|
2481
2512
|
""
|
|
2482
2513
|
];
|
|
2483
|
-
await writeFile6(
|
|
2514
|
+
await writeFile6(join15(workDir, "spec.md"), specLines.join("\n"), "utf8");
|
|
2484
2515
|
appendLastOp(input.vault, {
|
|
2485
2516
|
operation: "claim",
|
|
2486
2517
|
summary: `claimed ${input.transcript} \u2192 ${relWorkDir}`,
|
|
@@ -2515,16 +2546,16 @@ async function runPagesize(input) {
|
|
|
2515
2546
|
}
|
|
2516
2547
|
|
|
2517
2548
|
// src/commands/log-rotate.ts
|
|
2518
|
-
import { readFile as readFile12, rename as rename3, writeFile as writeFile7, stat as
|
|
2519
|
-
import { join as
|
|
2549
|
+
import { readFile as readFile12, rename as rename3, writeFile as writeFile7, stat as stat6 } from "fs/promises";
|
|
2550
|
+
import { join as join16 } from "path";
|
|
2520
2551
|
var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
|
|
2521
2552
|
async function runLogRotate(input) {
|
|
2522
2553
|
try {
|
|
2523
|
-
await
|
|
2554
|
+
await stat6(join16(input.vault, "SCHEMA.md"));
|
|
2524
2555
|
} catch {
|
|
2525
2556
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
2526
2557
|
}
|
|
2527
|
-
const logPath =
|
|
2558
|
+
const logPath = join16(input.vault, "log.md");
|
|
2528
2559
|
let logText;
|
|
2529
2560
|
try {
|
|
2530
2561
|
logText = await readFile12(logPath, "utf8");
|
|
@@ -2544,7 +2575,7 @@ async function runLogRotate(input) {
|
|
|
2544
2575
|
}
|
|
2545
2576
|
const newestYear = matches[matches.length - 1][1];
|
|
2546
2577
|
const rotatedName = `log-${newestYear}.md`;
|
|
2547
|
-
const rotatedPath =
|
|
2578
|
+
const rotatedPath = join16(input.vault, rotatedName);
|
|
2548
2579
|
try {
|
|
2549
2580
|
await rename3(logPath, rotatedPath);
|
|
2550
2581
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -2570,14 +2601,14 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
|
|
|
2570
2601
|
}
|
|
2571
2602
|
|
|
2572
2603
|
// src/commands/log-append.ts
|
|
2573
|
-
import { readFile as readFile13, rename as rename4, writeFile as writeFile8, stat as
|
|
2574
|
-
import { join as
|
|
2604
|
+
import { readFile as readFile13, rename as rename4, writeFile as writeFile8, stat as stat7 } from "fs/promises";
|
|
2605
|
+
import { join as join18 } from "path";
|
|
2575
2606
|
|
|
2576
2607
|
// src/utils/log-lock.ts
|
|
2577
|
-
import { existsSync as
|
|
2578
|
-
import { join as
|
|
2608
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, statSync as statSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2609
|
+
import { join as join17 } from "path";
|
|
2579
2610
|
function logLockPath(vault) {
|
|
2580
|
-
return
|
|
2611
|
+
return join17(vault, ".skillwiki", "log-append.lock");
|
|
2581
2612
|
}
|
|
2582
2613
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
2583
2614
|
async function acquireLogLock(vault, opts = {}) {
|
|
@@ -2585,8 +2616,8 @@ async function acquireLogLock(vault, opts = {}) {
|
|
|
2585
2616
|
const pollMs = opts.pollMs ?? 50;
|
|
2586
2617
|
const staleMs = opts.staleMs ?? 1e4;
|
|
2587
2618
|
const path = logLockPath(vault);
|
|
2588
|
-
const dir =
|
|
2589
|
-
if (!
|
|
2619
|
+
const dir = join17(vault, ".skillwiki");
|
|
2620
|
+
if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
|
|
2590
2621
|
const deadline = Date.now() + retryMs;
|
|
2591
2622
|
const content = JSON.stringify({ pid: process.pid, acquired: (/* @__PURE__ */ new Date()).toISOString() }) + "\n";
|
|
2592
2623
|
for (; ; ) {
|
|
@@ -2621,7 +2652,7 @@ function releaseLogLock(vault) {
|
|
|
2621
2652
|
var ENTRY_RE2 = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
|
|
2622
2653
|
async function runLogAppend(input) {
|
|
2623
2654
|
try {
|
|
2624
|
-
await
|
|
2655
|
+
await stat7(join18(input.vault, "SCHEMA.md"));
|
|
2625
2656
|
} catch {
|
|
2626
2657
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
2627
2658
|
}
|
|
@@ -2633,7 +2664,7 @@ async function runLogAppend(input) {
|
|
|
2633
2664
|
if (!acquired.ok) {
|
|
2634
2665
|
return { exitCode: ExitCode.LOG_APPEND_LOCK_HELD, result: err("LOG_APPEND_LOCK_HELD", { vault: input.vault }) };
|
|
2635
2666
|
}
|
|
2636
|
-
const logPath =
|
|
2667
|
+
const logPath = join18(input.vault, "log.md");
|
|
2637
2668
|
try {
|
|
2638
2669
|
let logText;
|
|
2639
2670
|
try {
|
|
@@ -2671,9 +2702,9 @@ ${content}
|
|
|
2671
2702
|
}
|
|
2672
2703
|
|
|
2673
2704
|
// src/commands/lint.ts
|
|
2674
|
-
import { existsSync as
|
|
2705
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2675
2706
|
import { readFile as readFile17 } from "fs/promises";
|
|
2676
|
-
import { join as
|
|
2707
|
+
import { join as join23 } from "path";
|
|
2677
2708
|
|
|
2678
2709
|
// src/commands/sparse-community.ts
|
|
2679
2710
|
async function runSparseCommunity(input) {
|
|
@@ -2709,12 +2740,12 @@ async function runTopicMapCheck(input) {
|
|
|
2709
2740
|
|
|
2710
2741
|
// src/commands/index-link-format.ts
|
|
2711
2742
|
import { readFile as readFile14 } from "fs/promises";
|
|
2712
|
-
import { join as
|
|
2743
|
+
import { join as join19 } from "path";
|
|
2713
2744
|
var MD_LINK_RE = /\[[^\[\]]+\]\([^)]+\.md\)/;
|
|
2714
2745
|
async function runIndexLinkFormat(input) {
|
|
2715
2746
|
let text = "";
|
|
2716
2747
|
try {
|
|
2717
|
-
text = await readFile14(
|
|
2748
|
+
text = await readFile14(join19(input.vault, "index.md"), "utf8");
|
|
2718
2749
|
} catch {
|
|
2719
2750
|
}
|
|
2720
2751
|
const markdown_links = [];
|
|
@@ -2728,7 +2759,7 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
|
|
|
2728
2759
|
|
|
2729
2760
|
// src/commands/dedup.ts
|
|
2730
2761
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2731
|
-
import { join as
|
|
2762
|
+
import { join as join20 } from "path";
|
|
2732
2763
|
async function runDedup(input) {
|
|
2733
2764
|
const scan = await scanVault(input.vault);
|
|
2734
2765
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
@@ -2756,7 +2787,7 @@ async function runDedup(input) {
|
|
|
2756
2787
|
}
|
|
2757
2788
|
}
|
|
2758
2789
|
for (const page of scan.data.typedKnowledge) {
|
|
2759
|
-
const text = readFileSync3(
|
|
2790
|
+
const text = readFileSync3(join20(input.vault, page.relPath), "utf-8");
|
|
2760
2791
|
let updated = text;
|
|
2761
2792
|
let changed = false;
|
|
2762
2793
|
for (const [oldPath, newPath] of replacements) {
|
|
@@ -2774,12 +2805,12 @@ async function runDedup(input) {
|
|
|
2774
2805
|
}
|
|
2775
2806
|
}
|
|
2776
2807
|
if (changed) {
|
|
2777
|
-
writeFileSync3(
|
|
2808
|
+
writeFileSync3(join20(input.vault, page.relPath), updated);
|
|
2778
2809
|
rewired.push(page.relPath);
|
|
2779
2810
|
}
|
|
2780
2811
|
}
|
|
2781
2812
|
for (const [oldPath] of replacements) {
|
|
2782
|
-
const fullPath =
|
|
2813
|
+
const fullPath = join20(input.vault, oldPath);
|
|
2783
2814
|
try {
|
|
2784
2815
|
unlinkSync3(fullPath);
|
|
2785
2816
|
removed.push(oldPath);
|
|
@@ -2816,7 +2847,7 @@ async function runDedup(input) {
|
|
|
2816
2847
|
// src/utils/safe-write.ts
|
|
2817
2848
|
import { open, readFile as readFile15, rename as rename5, unlink as unlink2, writeFile as writeFile9 } from "fs/promises";
|
|
2818
2849
|
import { randomBytes } from "crypto";
|
|
2819
|
-
import { dirname as dirname7, basename, join as
|
|
2850
|
+
import { dirname as dirname7, basename, join as join21 } from "path";
|
|
2820
2851
|
var DEFAULT_MIN_BODY_RATIO = 0.5;
|
|
2821
2852
|
var DEFAULT_MIN_OLD_BODY_BYTES = 200;
|
|
2822
2853
|
function bodyBytes(text) {
|
|
@@ -2865,7 +2896,7 @@ async function safeWritePage(absPath, newContent, opts = {}) {
|
|
|
2865
2896
|
}
|
|
2866
2897
|
const dir = dirname7(absPath);
|
|
2867
2898
|
const tmpName = `.${basename(absPath)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
2868
|
-
const tmpPath =
|
|
2899
|
+
const tmpPath = join21(dir, tmpName);
|
|
2869
2900
|
try {
|
|
2870
2901
|
const handle = await open(tmpPath, "w");
|
|
2871
2902
|
try {
|
|
@@ -2929,9 +2960,9 @@ async function runRawBodyDedup(vault) {
|
|
|
2929
2960
|
}
|
|
2930
2961
|
|
|
2931
2962
|
// src/commands/path-too-long.ts
|
|
2932
|
-
import { existsSync as
|
|
2963
|
+
import { existsSync as existsSync5 } from "fs";
|
|
2933
2964
|
import { mkdir as mkdir8, readFile as readFile16, rename as rename6, unlink as unlink3 } from "fs/promises";
|
|
2934
|
-
import { dirname as dirname8, join as
|
|
2965
|
+
import { dirname as dirname8, join as join22, posix, resolve as resolve4 } from "path";
|
|
2935
2966
|
var MAX_PATH_LENGTH = 240;
|
|
2936
2967
|
var WINDOWS_ABSOLUTE_PATH_LIMIT = 259;
|
|
2937
2968
|
async function runPathTooLong(input) {
|
|
@@ -2964,10 +2995,10 @@ async function fixPathTooLong(input) {
|
|
|
2964
2995
|
}
|
|
2965
2996
|
try {
|
|
2966
2997
|
if (target.mode === "dedupe") {
|
|
2967
|
-
await unlink3(
|
|
2998
|
+
await unlink3(join22(input.vault, violation.relPath));
|
|
2968
2999
|
} else {
|
|
2969
|
-
await mkdir8(dirname8(
|
|
2970
|
-
await rename6(
|
|
3000
|
+
await mkdir8(dirname8(join22(input.vault, target.relPath)), { recursive: true });
|
|
3001
|
+
await rename6(join22(input.vault, violation.relPath), join22(input.vault, target.relPath));
|
|
2971
3002
|
}
|
|
2972
3003
|
fixed.push({ from: violation.relPath, to: target.relPath });
|
|
2973
3004
|
} catch {
|
|
@@ -3044,9 +3075,9 @@ function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
|
3044
3075
|
async function resolveFixTarget(vault, original, preferred, maxLength) {
|
|
3045
3076
|
for (const candidate of candidateRelPaths(preferred, maxLength)) {
|
|
3046
3077
|
if (candidate === original || candidate.length > maxLength) continue;
|
|
3047
|
-
const candidatePath =
|
|
3048
|
-
if (!
|
|
3049
|
-
if (await hasSameContent(
|
|
3078
|
+
const candidatePath = join22(vault, candidate);
|
|
3079
|
+
if (!existsSync5(candidatePath)) return { relPath: candidate, mode: "rename" };
|
|
3080
|
+
if (await hasSameContent(join22(vault, original), candidatePath)) {
|
|
3050
3081
|
return { relPath: candidate, mode: "dedupe" };
|
|
3051
3082
|
}
|
|
3052
3083
|
}
|
|
@@ -3543,7 +3574,7 @@ async function runLint(input) {
|
|
|
3543
3574
|
const noOverview = [];
|
|
3544
3575
|
const fmWikilinkFlags = [];
|
|
3545
3576
|
const wikilinkCitationFlags = [];
|
|
3546
|
-
const brokenSourceFlags =
|
|
3577
|
+
const brokenSourceFlags = /* @__PURE__ */ new Set();
|
|
3547
3578
|
const missingTldrFlags = [];
|
|
3548
3579
|
const missingDiagramFlags = [];
|
|
3549
3580
|
for (const page of scan.data.typedKnowledge) {
|
|
@@ -3558,11 +3589,15 @@ async function runLint(input) {
|
|
|
3558
3589
|
if (hasWikilinkCitations(body)) wikilinkCitationFlags.push(page.relPath);
|
|
3559
3590
|
const sourcesEntries = extractSourceEntries(rawFm);
|
|
3560
3591
|
for (const entry of sourcesEntries) {
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
if (!
|
|
3564
|
-
|
|
3565
|
-
|
|
3592
|
+
const rawPath = normalizeRawSourceTarget(entry);
|
|
3593
|
+
if (!rawPath) continue;
|
|
3594
|
+
if (!rawSourceTargetExistsSync(input.vault, rawPath)) {
|
|
3595
|
+
brokenSourceFlags.add(`${page.relPath}: ${rawPath}`);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
for (const marker of extractCitationMarkers(body)) {
|
|
3599
|
+
if (!rawSourceTargetExistsSync(input.vault, marker.target)) {
|
|
3600
|
+
brokenSourceFlags.add(`${page.relPath}: ${marker.target}`);
|
|
3566
3601
|
}
|
|
3567
3602
|
}
|
|
3568
3603
|
const fmLinks = rawFm.match(/\[\[([^\[\]|]+)(?:\|[^\[\]]*)?\]\]/g) ?? [];
|
|
@@ -3601,7 +3636,7 @@ async function runLint(input) {
|
|
|
3601
3636
|
if (noOverview.length > 0) buckets.missing_overview = noOverview;
|
|
3602
3637
|
if (fmWikilinkFlags.length > 0) buckets.frontmatter_wikilink = fmWikilinkFlags;
|
|
3603
3638
|
if (wikilinkCitationFlags.length > 0) buckets.wikilink_citation = wikilinkCitationFlags;
|
|
3604
|
-
if (brokenSourceFlags.
|
|
3639
|
+
if (brokenSourceFlags.size > 0) buckets.broken_sources = [...brokenSourceFlags];
|
|
3605
3640
|
if (missingTldrFlags.length > 0) buckets.missing_tldr = missingTldrFlags;
|
|
3606
3641
|
if (missingDiagramFlags.length > 0) buckets.missing_diagram = missingDiagramFlags;
|
|
3607
3642
|
const workItemHealth = [];
|
|
@@ -3652,8 +3687,8 @@ async function runLint(input) {
|
|
|
3652
3687
|
const slugMatch = String(entry).match(/\[\[([^\]]+)\]\]/);
|
|
3653
3688
|
if (!slugMatch) continue;
|
|
3654
3689
|
const slug = slugMatch[1];
|
|
3655
|
-
const knowledgePath =
|
|
3656
|
-
if (!
|
|
3690
|
+
const knowledgePath = join23(input.vault, "projects", slug, "knowledge.md");
|
|
3691
|
+
if (!existsSync6(knowledgePath)) continue;
|
|
3657
3692
|
const pageRef = page.relPath.replace(/\.md$/, "");
|
|
3658
3693
|
try {
|
|
3659
3694
|
const knowledgeContent = await readFile17(knowledgePath, "utf8");
|
|
@@ -4098,25 +4133,25 @@ ${match.length === 0 ? "0 violations" : match.map((b) => ` ${b.kind}: ${b.items
|
|
|
4098
4133
|
}
|
|
4099
4134
|
|
|
4100
4135
|
// src/commands/health.ts
|
|
4101
|
-
import { existsSync as
|
|
4102
|
-
import { dirname as dirname10, join as
|
|
4136
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync, writeFileSync as writeFileSync6 } from "fs";
|
|
4137
|
+
import { dirname as dirname10, join as join29, resolve as resolve6 } from "path";
|
|
4103
4138
|
import { platform as platform3 } from "os";
|
|
4104
4139
|
|
|
4105
4140
|
// src/commands/doctor.ts
|
|
4106
|
-
import { existsSync as
|
|
4107
|
-
import { join as
|
|
4141
|
+
import { existsSync as existsSync11, lstatSync, readlinkSync, readdirSync as readdirSync2, statSync as statSync3, readFileSync as readFileSync7 } from "fs";
|
|
4142
|
+
import { join as join28, resolve as resolve5 } from "path";
|
|
4108
4143
|
import { execSync as execSync2 } from "child_process";
|
|
4109
4144
|
import { platform as platform2 } from "os";
|
|
4110
4145
|
|
|
4111
4146
|
// src/commands/config.ts
|
|
4112
4147
|
import { readFile as readFile18 } from "fs/promises";
|
|
4113
|
-
import { existsSync as
|
|
4114
|
-
import { join as
|
|
4148
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4149
|
+
import { join as join24 } from "path";
|
|
4115
4150
|
function validateKey(key) {
|
|
4116
4151
|
return CONFIG_KEYS.includes(key) || isValidWikiProfileKey(key);
|
|
4117
4152
|
}
|
|
4118
4153
|
function configPath(home) {
|
|
4119
|
-
return
|
|
4154
|
+
return join24(home, ".skillwiki", ".env");
|
|
4120
4155
|
}
|
|
4121
4156
|
async function runConfigGet(input) {
|
|
4122
4157
|
if (!validateKey(input.key)) {
|
|
@@ -4166,15 +4201,15 @@ async function runConfigList(input) {
|
|
|
4166
4201
|
}
|
|
4167
4202
|
async function runConfigPath(input) {
|
|
4168
4203
|
const filePath = configPath(input.home);
|
|
4169
|
-
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists:
|
|
4204
|
+
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync7(filePath), humanHint: filePath }) };
|
|
4170
4205
|
}
|
|
4171
4206
|
|
|
4172
4207
|
// src/utils/auto-update.ts
|
|
4173
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as
|
|
4174
|
-
import { join as
|
|
4208
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
|
|
4209
|
+
import { join as join25, dirname as dirname9 } from "path";
|
|
4175
4210
|
import { spawn } from "child_process";
|
|
4176
4211
|
function cachePath(home) {
|
|
4177
|
-
return
|
|
4212
|
+
return join25(home, ".skillwiki", CACHE_FILENAME);
|
|
4178
4213
|
}
|
|
4179
4214
|
function readCacheRaw(home) {
|
|
4180
4215
|
try {
|
|
@@ -4218,7 +4253,7 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
4218
4253
|
if (!isStale2) return;
|
|
4219
4254
|
const distTag = distTagFromCache(home);
|
|
4220
4255
|
const bgScript = new URL("../auto-update-bg.js", import.meta.url).pathname;
|
|
4221
|
-
if (!
|
|
4256
|
+
if (!existsSync8(bgScript)) return;
|
|
4222
4257
|
const child = spawn(process.execPath, [bgScript, home, currentVersion, distTag], {
|
|
4223
4258
|
detached: true,
|
|
4224
4259
|
stdio: "ignore"
|
|
@@ -4229,14 +4264,14 @@ function triggerAutoUpdate(home, currentVersion) {
|
|
|
4229
4264
|
}
|
|
4230
4265
|
|
|
4231
4266
|
// src/utils/plugin-registry.ts
|
|
4232
|
-
import { existsSync as
|
|
4233
|
-
import { join as
|
|
4234
|
-
var REGISTRY_PATH =
|
|
4235
|
-
var CODEX_CONFIG_PATH =
|
|
4267
|
+
import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
4268
|
+
import { join as join26 } from "path";
|
|
4269
|
+
var REGISTRY_PATH = join26(".claude", "plugins", "installed_plugins.json");
|
|
4270
|
+
var CODEX_CONFIG_PATH = join26(".codex", "config.toml");
|
|
4236
4271
|
var PLUGIN_KEY = "skillwiki@llm-wiki";
|
|
4237
4272
|
function readInstalledPlugins(home) {
|
|
4238
4273
|
try {
|
|
4239
|
-
const raw = readFileSync5(
|
|
4274
|
+
const raw = readFileSync5(join26(home, REGISTRY_PATH), "utf8");
|
|
4240
4275
|
return JSON.parse(raw);
|
|
4241
4276
|
} catch {
|
|
4242
4277
|
return null;
|
|
@@ -4272,8 +4307,8 @@ function findPluginInstallations(home, key = PLUGIN_KEY) {
|
|
|
4272
4307
|
function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
4273
4308
|
const config = readCodexPluginConfig(home, key, marketplace);
|
|
4274
4309
|
if (!config?.enabled) return null;
|
|
4275
|
-
const cacheRoot =
|
|
4276
|
-
if (!
|
|
4310
|
+
const cacheRoot = join26(home, ".codex", "plugins", "cache", marketplace, pluginName);
|
|
4311
|
+
if (!existsSync9(cacheRoot)) return null;
|
|
4277
4312
|
let versions;
|
|
4278
4313
|
try {
|
|
4279
4314
|
versions = readdirSync(cacheRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
@@ -4288,7 +4323,7 @@ function findCodexPlugin(home, key, pluginName, marketplace) {
|
|
|
4288
4323
|
key,
|
|
4289
4324
|
pluginName,
|
|
4290
4325
|
marketplace,
|
|
4291
|
-
installPath:
|
|
4326
|
+
installPath: join26(cacheRoot, version),
|
|
4292
4327
|
version,
|
|
4293
4328
|
sourceType: config.sourceType,
|
|
4294
4329
|
source: config.source
|
|
@@ -4305,7 +4340,7 @@ function parsePluginKey(key) {
|
|
|
4305
4340
|
function readCodexPluginConfig(home, key, marketplace) {
|
|
4306
4341
|
let raw;
|
|
4307
4342
|
try {
|
|
4308
|
-
raw = readFileSync5(
|
|
4343
|
+
raw = readFileSync5(join26(home, CODEX_CONFIG_PATH), "utf8");
|
|
4309
4344
|
} catch {
|
|
4310
4345
|
return null;
|
|
4311
4346
|
}
|
|
@@ -4350,7 +4385,7 @@ function parseTomlScalar(rawValue) {
|
|
|
4350
4385
|
import { execSync } from "child_process";
|
|
4351
4386
|
import { platform } from "os";
|
|
4352
4387
|
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, unlinkSync as unlinkSync4, readFileSync as readFile19 } from "fs";
|
|
4353
|
-
import { join as
|
|
4388
|
+
import { join as join27 } from "path";
|
|
4354
4389
|
var OS = platform();
|
|
4355
4390
|
function findRcloneMountPid() {
|
|
4356
4391
|
try {
|
|
@@ -4508,7 +4543,7 @@ function detectFuseMount(vaultPath) {
|
|
|
4508
4543
|
return null;
|
|
4509
4544
|
}
|
|
4510
4545
|
function writeTest(dir) {
|
|
4511
|
-
const testFile =
|
|
4546
|
+
const testFile = join27(dir, `.doctor-write-test-${process.pid}.tmp`);
|
|
4512
4547
|
const payload = `skillwiki doctor write test \u2014 ${Date.now()} \u2014 ${Math.random().toString(36).slice(2)}`;
|
|
4513
4548
|
const start = Date.now();
|
|
4514
4549
|
try {
|
|
@@ -4608,13 +4643,13 @@ function detectCliChannels(argv, home) {
|
|
|
4608
4643
|
}
|
|
4609
4644
|
const plugin = findPlugin(home);
|
|
4610
4645
|
if (plugin) {
|
|
4611
|
-
const pluginBin =
|
|
4612
|
-
if (
|
|
4646
|
+
const pluginBin = join28(plugin.installPath, "bin", "skillwiki");
|
|
4647
|
+
if (existsSync11(pluginBin)) {
|
|
4613
4648
|
channels.push({ name: "plugin", path: pluginBin, isDevLink: false });
|
|
4614
4649
|
}
|
|
4615
4650
|
}
|
|
4616
|
-
const installBin =
|
|
4617
|
-
if (
|
|
4651
|
+
const installBin = join28(home, ".claude", "skills", "bin", "skillwiki");
|
|
4652
|
+
if (existsSync11(installBin)) {
|
|
4618
4653
|
channels.push({ name: "install", path: installBin, isDevLink: false });
|
|
4619
4654
|
}
|
|
4620
4655
|
return channels;
|
|
@@ -4666,7 +4701,7 @@ function checkCliChannels(argv, home) {
|
|
|
4666
4701
|
}
|
|
4667
4702
|
async function checkConfigFile(home) {
|
|
4668
4703
|
const cfgPath = configPath(home);
|
|
4669
|
-
if (!
|
|
4704
|
+
if (!existsSync11(cfgPath)) {
|
|
4670
4705
|
return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
|
|
4671
4706
|
}
|
|
4672
4707
|
try {
|
|
@@ -4681,7 +4716,7 @@ function checkWikiPathExists(resolvedPath) {
|
|
|
4681
4716
|
if (resolvedPath === void 0) {
|
|
4682
4717
|
return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4683
4718
|
}
|
|
4684
|
-
if (
|
|
4719
|
+
if (existsSync11(resolvedPath) && statSync3(resolvedPath).isDirectory()) {
|
|
4685
4720
|
return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
4686
4721
|
}
|
|
4687
4722
|
return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
@@ -4690,13 +4725,13 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4690
4725
|
if (resolvedPath === void 0) {
|
|
4691
4726
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4692
4727
|
}
|
|
4693
|
-
if (!
|
|
4728
|
+
if (!existsSync11(resolvedPath)) {
|
|
4694
4729
|
return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
4695
4730
|
}
|
|
4696
4731
|
const missing = [];
|
|
4697
|
-
if (!
|
|
4732
|
+
if (!existsSync11(join28(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
4698
4733
|
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
4699
|
-
if (!
|
|
4734
|
+
if (!existsSync11(join28(resolvedPath, dir))) missing.push(dir + "/");
|
|
4700
4735
|
}
|
|
4701
4736
|
if (missing.length === 0) {
|
|
4702
4737
|
return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
|
|
@@ -4704,8 +4739,8 @@ function checkVaultStructure(resolvedPath) {
|
|
|
4704
4739
|
return check("warn", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")} \u2014 run \`skillwiki init\` to add CodeWiki structure`);
|
|
4705
4740
|
}
|
|
4706
4741
|
function checkSkillsInstalled(home, cwd) {
|
|
4707
|
-
const srcDir = cwd ?
|
|
4708
|
-
if (srcDir &&
|
|
4742
|
+
const srcDir = cwd ? join28(cwd, "packages", "skills") : void 0;
|
|
4743
|
+
if (srcDir && existsSync11(srcDir)) {
|
|
4709
4744
|
const found = findInstalledSkillMd(srcDir);
|
|
4710
4745
|
if (found.length > 0) {
|
|
4711
4746
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (source)`);
|
|
@@ -4718,8 +4753,8 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4718
4753
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (plugin v${plugin.version})`);
|
|
4719
4754
|
}
|
|
4720
4755
|
}
|
|
4721
|
-
const skillsDir =
|
|
4722
|
-
if (
|
|
4756
|
+
const skillsDir = join28(home, ".claude", "skills");
|
|
4757
|
+
if (existsSync11(skillsDir)) {
|
|
4723
4758
|
const found = findInstalledSkillMd(skillsDir);
|
|
4724
4759
|
if (found.length > 0) {
|
|
4725
4760
|
return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found (CLI install)`);
|
|
@@ -4729,10 +4764,10 @@ function checkSkillsInstalled(home, cwd) {
|
|
|
4729
4764
|
}
|
|
4730
4765
|
function checkDuplicateSkills(home) {
|
|
4731
4766
|
const plugin = findPlugin(home);
|
|
4732
|
-
const skillsDir =
|
|
4767
|
+
const skillsDir = join28(home, ".claude", "skills");
|
|
4733
4768
|
const agentSkillDirs = [
|
|
4734
|
-
{ label: "~/.codex/skills/", path:
|
|
4735
|
-
{ label: "~/.agents/skills/", path:
|
|
4769
|
+
{ label: "~/.codex/skills/", path: join28(home, ".codex", "skills") },
|
|
4770
|
+
{ label: "~/.agents/skills/", path: join28(home, ".agents", "skills") }
|
|
4736
4771
|
];
|
|
4737
4772
|
if (!plugin) {
|
|
4738
4773
|
return check("pass", "skills_duplicate", "Skills not duplicated", "Single install channel");
|
|
@@ -4831,8 +4866,8 @@ async function checkProfiles(home) {
|
|
|
4831
4866
|
}
|
|
4832
4867
|
async function checkProjectLocalOverride(cwd) {
|
|
4833
4868
|
const dir = cwd ?? process.cwd();
|
|
4834
|
-
const envPath =
|
|
4835
|
-
if (
|
|
4869
|
+
const envPath = join28(dir, ".skillwiki", ".env");
|
|
4870
|
+
if (existsSync11(envPath)) {
|
|
4836
4871
|
return check("pass", "project_local", "Project-local config", `Found: ${envPath}`);
|
|
4837
4872
|
}
|
|
4838
4873
|
return check("pass", "project_local", "Project-local config", "None");
|
|
@@ -4841,7 +4876,7 @@ function checkVaultGitRemote(resolvedPath) {
|
|
|
4841
4876
|
if (resolvedPath === void 0) {
|
|
4842
4877
|
return check("error", "vault_git_remote", "Vault git remote", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4843
4878
|
}
|
|
4844
|
-
if (!
|
|
4879
|
+
if (!existsSync11(join28(resolvedPath, ".git"))) {
|
|
4845
4880
|
return check("warn", "vault_git_remote", "Vault git remote", "Vault is not a git repository \u2014 sync features unavailable");
|
|
4846
4881
|
}
|
|
4847
4882
|
try {
|
|
@@ -4864,9 +4899,9 @@ function checkObsidianTemplates(resolvedPath) {
|
|
|
4864
4899
|
return check("error", "obsidian_templates", "Obsidian templates", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4865
4900
|
}
|
|
4866
4901
|
const missing = [];
|
|
4867
|
-
if (!
|
|
4868
|
-
if (!
|
|
4869
|
-
if (!
|
|
4902
|
+
if (!existsSync11(join28(resolvedPath, "_Templates"))) missing.push("_Templates/");
|
|
4903
|
+
if (!existsSync11(join28(resolvedPath, ".obsidian", "templates.json"))) missing.push(".obsidian/templates.json");
|
|
4904
|
+
if (!existsSync11(join28(resolvedPath, ".obsidian", "app.json"))) missing.push(".obsidian/app.json");
|
|
4870
4905
|
if (missing.length === 0) {
|
|
4871
4906
|
return check("pass", "obsidian_templates", "Obsidian templates", "Template folder and config present");
|
|
4872
4907
|
}
|
|
@@ -4876,8 +4911,8 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4876
4911
|
if (resolvedPath === void 0) {
|
|
4877
4912
|
return check("error", "dsstore_clean", "No .DS_Store in raw/", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4878
4913
|
}
|
|
4879
|
-
const rawDir =
|
|
4880
|
-
if (!
|
|
4914
|
+
const rawDir = join28(resolvedPath, "raw");
|
|
4915
|
+
if (!existsSync11(rawDir)) {
|
|
4881
4916
|
return check("pass", "dsstore_clean", "No .DS_Store in raw/", "raw/ directory not found \u2014 check skipped");
|
|
4882
4917
|
}
|
|
4883
4918
|
const found = [];
|
|
@@ -4892,7 +4927,7 @@ function checkDotStoreClean(resolvedPath) {
|
|
|
4892
4927
|
if (entry.name === ".DS_Store") {
|
|
4893
4928
|
found.push(rel ? `${rel}/.DS_Store` : ".DS_Store");
|
|
4894
4929
|
} else if (entry.isDirectory()) {
|
|
4895
|
-
walk2(
|
|
4930
|
+
walk2(join28(dir, entry.name), rel ? `${rel}/${entry.name}` : entry.name);
|
|
4896
4931
|
}
|
|
4897
4932
|
}
|
|
4898
4933
|
})(rawDir, "");
|
|
@@ -4905,7 +4940,7 @@ function checkSyncLastPush(resolvedPath) {
|
|
|
4905
4940
|
if (resolvedPath === void 0) {
|
|
4906
4941
|
return check("error", "sync_last_push", "Vault sync recency", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
4907
4942
|
}
|
|
4908
|
-
if (!
|
|
4943
|
+
if (!existsSync11(join28(resolvedPath, ".git"))) {
|
|
4909
4944
|
return check("pass", "sync_last_push", "Vault sync recency", "No git repo \u2014 sync check skipped");
|
|
4910
4945
|
}
|
|
4911
4946
|
let timestamp;
|
|
@@ -4953,7 +4988,7 @@ function checkVaultGitDirty(resolvedPath) {
|
|
|
4953
4988
|
if (resolvedPath === void 0) {
|
|
4954
4989
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No vault path \u2014 check skipped");
|
|
4955
4990
|
}
|
|
4956
|
-
if (!
|
|
4991
|
+
if (!existsSync11(join28(resolvedPath, ".git"))) {
|
|
4957
4992
|
return check("pass", "vault_git_dirty", "Vault git dirty state", "No git repo \u2014 check skipped");
|
|
4958
4993
|
}
|
|
4959
4994
|
try {
|
|
@@ -4994,7 +5029,7 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
4994
5029
|
if (resolvedPath === void 0) {
|
|
4995
5030
|
return check("pass", id, label, "No vault path \u2014 check skipped");
|
|
4996
5031
|
}
|
|
4997
|
-
if (!
|
|
5032
|
+
if (!existsSync11(join28(resolvedPath, ".git"))) {
|
|
4998
5033
|
return check("pass", id, label, "No git repo \u2014 check skipped");
|
|
4999
5034
|
}
|
|
5000
5035
|
if (!hasOriginMain(resolvedPath)) {
|
|
@@ -5016,11 +5051,11 @@ function checkVaultGitComparison(resolvedPath, id, label, range, nonZeroSuffix,
|
|
|
5016
5051
|
}
|
|
5017
5052
|
function pullLogPaths(home) {
|
|
5018
5053
|
const paths = platform2() === "darwin" ? [
|
|
5019
|
-
|
|
5020
|
-
|
|
5054
|
+
join28(home, "Library", "Logs", "wiki-pull.log"),
|
|
5055
|
+
join28(home, ".local", "state", "vault-sync", "log", "wiki-pull.log")
|
|
5021
5056
|
] : [
|
|
5022
|
-
|
|
5023
|
-
|
|
5057
|
+
join28(home, ".local", "state", "vault-sync", "log", "wiki-pull.log"),
|
|
5058
|
+
join28(home, "Library", "Logs", "wiki-pull.log")
|
|
5024
5059
|
];
|
|
5025
5060
|
return [...new Set(paths)];
|
|
5026
5061
|
}
|
|
@@ -5032,7 +5067,7 @@ function isRecentLogLine(line, nowMs) {
|
|
|
5032
5067
|
return nowMs - ts <= 24 * 60 * 60 * 1e3;
|
|
5033
5068
|
}
|
|
5034
5069
|
function checkVaultGitPullFailures(home) {
|
|
5035
|
-
const path = pullLogPaths(home).find((p) =>
|
|
5070
|
+
const path = pullLogPaths(home).find((p) => existsSync11(p));
|
|
5036
5071
|
if (!path) {
|
|
5037
5072
|
return check("pass", "vault_git_pull_failures", "Vault pull failures", "No wiki-pull.log found \u2014 check skipped");
|
|
5038
5073
|
}
|
|
@@ -5060,8 +5095,8 @@ function checkS3MountPerf(resolvedPath) {
|
|
|
5060
5095
|
return check("pass", "s3_mount_perf", "S3 mount performance", "local disk");
|
|
5061
5096
|
}
|
|
5062
5097
|
const mountPoint = fuse.mountPoint;
|
|
5063
|
-
const conceptsDir =
|
|
5064
|
-
if (!
|
|
5098
|
+
const conceptsDir = join28(resolvedPath, "concepts");
|
|
5099
|
+
if (!existsSync11(conceptsDir)) {
|
|
5065
5100
|
return check("pass", "s3_mount_perf", "S3 mount performance", `S3 FUSE mount (${mountPoint}), no concepts/ to benchmark`);
|
|
5066
5101
|
}
|
|
5067
5102
|
const start = Date.now();
|
|
@@ -5243,8 +5278,8 @@ function checkWriteTest(resolvedPath) {
|
|
|
5243
5278
|
if (!fuse) {
|
|
5244
5279
|
return check("pass", "s3_write_test", "S3 write test", "local disk \u2014 check skipped");
|
|
5245
5280
|
}
|
|
5246
|
-
const conceptsDir =
|
|
5247
|
-
if (!
|
|
5281
|
+
const conceptsDir = join28(resolvedPath, "concepts");
|
|
5282
|
+
if (!existsSync11(conceptsDir)) {
|
|
5248
5283
|
return check("pass", "s3_write_test", "S3 write test", "no concepts/ dir to test \u2014 check skipped");
|
|
5249
5284
|
}
|
|
5250
5285
|
const result = writeTest(conceptsDir);
|
|
@@ -5330,7 +5365,7 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
5330
5365
|
}
|
|
5331
5366
|
function readVaultSyncConfig(home) {
|
|
5332
5367
|
try {
|
|
5333
|
-
const content = readFileSync7(
|
|
5368
|
+
const content = readFileSync7(join28(home, ".skillwiki", ".env"), "utf8");
|
|
5334
5369
|
let installed = false;
|
|
5335
5370
|
let role;
|
|
5336
5371
|
for (const line of content.split(/\r?\n/)) {
|
|
@@ -5364,12 +5399,12 @@ function vaultSyncChecks(input) {
|
|
|
5364
5399
|
];
|
|
5365
5400
|
}
|
|
5366
5401
|
const isMac = os === "darwin";
|
|
5367
|
-
const logDir = input.logDir ?? (isMac ?
|
|
5368
|
-
const shareDir = input.shareDir ?? (isMac ?
|
|
5369
|
-
const filterPath = input.filterPath ??
|
|
5402
|
+
const logDir = input.logDir ?? (isMac ? join28(home, "Library", "Logs") : join28(home, ".local", "state", "vault-sync", "log"));
|
|
5403
|
+
const shareDir = input.shareDir ?? (isMac ? join28(home, "Library", "Application Support", "vault-sync", "bin") : join28(home, ".local", "share", "vault-sync", "bin"));
|
|
5404
|
+
const filterPath = input.filterPath ?? join28(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
5370
5405
|
const snapshotPath = input.snapshotScriptPath ?? "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
5371
|
-
const pushScriptPath =
|
|
5372
|
-
const c1 =
|
|
5406
|
+
const pushScriptPath = join28(shareDir, "wiki-push.sh");
|
|
5407
|
+
const c1 = existsSync11(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
|
|
5373
5408
|
let c2;
|
|
5374
5409
|
try {
|
|
5375
5410
|
if (isMac) {
|
|
@@ -5420,7 +5455,7 @@ function vaultSyncChecks(input) {
|
|
|
5420
5455
|
"Scheduler check failed \u2014 run vault-sync-install"
|
|
5421
5456
|
);
|
|
5422
5457
|
}
|
|
5423
|
-
const logFile =
|
|
5458
|
+
const logFile = join28(logDir, "wiki-push.log");
|
|
5424
5459
|
let c3;
|
|
5425
5460
|
try {
|
|
5426
5461
|
const logContent = readFileSync7(logFile, "utf8");
|
|
@@ -5479,7 +5514,7 @@ function vaultSyncChecks(input) {
|
|
|
5479
5514
|
}
|
|
5480
5515
|
}
|
|
5481
5516
|
} catch {
|
|
5482
|
-
c3 =
|
|
5517
|
+
c3 = existsSync11(logDir) ? check(
|
|
5483
5518
|
"warn",
|
|
5484
5519
|
"vault_sync_last_push_age",
|
|
5485
5520
|
"Vault sync last push recency",
|
|
@@ -5491,7 +5526,7 @@ function vaultSyncChecks(input) {
|
|
|
5491
5526
|
`Log directory not found at ${logDir}`
|
|
5492
5527
|
);
|
|
5493
5528
|
}
|
|
5494
|
-
const fetchLogFile =
|
|
5529
|
+
const fetchLogFile = join28(logDir, "wiki-fetch.log");
|
|
5495
5530
|
let cFetch;
|
|
5496
5531
|
try {
|
|
5497
5532
|
const logContent = readFileSync7(fetchLogFile, "utf8");
|
|
@@ -5538,7 +5573,7 @@ function vaultSyncChecks(input) {
|
|
|
5538
5573
|
}
|
|
5539
5574
|
let c4;
|
|
5540
5575
|
try {
|
|
5541
|
-
if (!
|
|
5576
|
+
if (!existsSync11(filterPath)) {
|
|
5542
5577
|
c4 = check(
|
|
5543
5578
|
"error",
|
|
5544
5579
|
"vault_sync_filter_present",
|
|
@@ -5587,7 +5622,7 @@ function vaultSyncChecks(input) {
|
|
|
5587
5622
|
);
|
|
5588
5623
|
} else {
|
|
5589
5624
|
try {
|
|
5590
|
-
if (!
|
|
5625
|
+
if (!existsSync11(snapshotPath)) {
|
|
5591
5626
|
c5 = check(
|
|
5592
5627
|
"error",
|
|
5593
5628
|
"vault_sync_snapshot_guard",
|
|
@@ -5633,15 +5668,15 @@ function findSkillMd(dir) {
|
|
|
5633
5668
|
}
|
|
5634
5669
|
for (const entry of entries) {
|
|
5635
5670
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
5636
|
-
results.push(
|
|
5671
|
+
results.push(join28(dir, entry.name));
|
|
5637
5672
|
} else if (entry.isDirectory()) {
|
|
5638
|
-
results.push(...findSkillMd(
|
|
5673
|
+
results.push(...findSkillMd(join28(dir, entry.name)));
|
|
5639
5674
|
}
|
|
5640
5675
|
}
|
|
5641
5676
|
return results;
|
|
5642
5677
|
}
|
|
5643
5678
|
function findInstalledSkillMd(dir) {
|
|
5644
|
-
const directSkills = findSkillNames(dir).map((name) =>
|
|
5679
|
+
const directSkills = findSkillNames(dir).map((name) => join28(dir, name, "SKILL.md"));
|
|
5645
5680
|
return directSkills.length > 0 ? directSkills : findSkillMd(dir);
|
|
5646
5681
|
}
|
|
5647
5682
|
function findSkillNames(dir) {
|
|
@@ -5653,7 +5688,7 @@ function findSkillNames(dir) {
|
|
|
5653
5688
|
return results;
|
|
5654
5689
|
}
|
|
5655
5690
|
for (const entry of entries) {
|
|
5656
|
-
if (entry.isDirectory() &&
|
|
5691
|
+
if (entry.isDirectory() && existsSync11(join28(dir, entry.name, "SKILL.md"))) {
|
|
5657
5692
|
results.push(entry.name);
|
|
5658
5693
|
}
|
|
5659
5694
|
}
|
|
@@ -5697,7 +5732,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
5697
5732
|
}
|
|
5698
5733
|
let logLines = 0;
|
|
5699
5734
|
try {
|
|
5700
|
-
logLines = readFileSync7(
|
|
5735
|
+
logLines = readFileSync7(join28(resolvedPath, "log.md"), "utf8").split("\n").length;
|
|
5701
5736
|
} catch {
|
|
5702
5737
|
}
|
|
5703
5738
|
return [
|
|
@@ -5881,7 +5916,7 @@ function summarizeChecks(checks) {
|
|
|
5881
5916
|
};
|
|
5882
5917
|
}
|
|
5883
5918
|
function classifyLog(path, id, label, okPattern) {
|
|
5884
|
-
if (!
|
|
5919
|
+
if (!existsSync12(path)) return { id, label, status: "warn", detail: `log file missing: ${path}` };
|
|
5885
5920
|
const lines = readFileSync8(path, "utf8").split(/\r?\n/).filter(Boolean);
|
|
5886
5921
|
const last = lines[lines.length - 1] ?? "";
|
|
5887
5922
|
if (!last) return { id, label, status: "warn", detail: `log file empty: ${path}` };
|
|
@@ -5900,28 +5935,28 @@ function runVaultSyncHealth(home, syncMode) {
|
|
|
5900
5935
|
};
|
|
5901
5936
|
}
|
|
5902
5937
|
const isMac = platform3() === "darwin";
|
|
5903
|
-
const shareDir = isMac ?
|
|
5904
|
-
const logDir = isMac ?
|
|
5905
|
-
const filterPath =
|
|
5938
|
+
const shareDir = isMac ? join29(home, "Library", "Application Support", "vault-sync", "bin") : join29(home, ".local", "share", "vault-sync", "bin");
|
|
5939
|
+
const logDir = isMac ? join29(home, "Library", "Logs") : join29(home, ".local", "state", "vault-sync", "log");
|
|
5940
|
+
const filterPath = join29(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
5906
5941
|
const checks = [];
|
|
5907
|
-
const pushScript =
|
|
5908
|
-
checks.push(
|
|
5942
|
+
const pushScript = join29(shareDir, "wiki-push.sh");
|
|
5943
|
+
checks.push(existsSync12(pushScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${pushScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${pushScript}` });
|
|
5909
5944
|
if (isMac) {
|
|
5910
|
-
const pushPlist =
|
|
5911
|
-
const fetchPlist =
|
|
5912
|
-
checks.push(
|
|
5945
|
+
const pushPlist = join29(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
|
|
5946
|
+
const fetchPlist = join29(home, "Library", "LaunchAgents", "com.karlchow.wiki-fetch.plist");
|
|
5947
|
+
checks.push(existsSync12(pushPlist) && existsSync12(fetchPlist) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "launchd unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "launchd unit files missing (read-only mode)" });
|
|
5913
5948
|
checks.push({ id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "macOS host \u2014 check skipped" });
|
|
5914
5949
|
} else {
|
|
5915
|
-
const pushTimer =
|
|
5916
|
-
const fetchTimer =
|
|
5917
|
-
const fuseTimer =
|
|
5918
|
-
const fuseService =
|
|
5919
|
-
checks.push(
|
|
5920
|
-
checks.push(
|
|
5921
|
-
}
|
|
5922
|
-
checks.push(classifyLog(
|
|
5923
|
-
checks.push(classifyLog(
|
|
5924
|
-
if (!
|
|
5950
|
+
const pushTimer = join29(home, ".config", "systemd", "user", "wiki-push.timer");
|
|
5951
|
+
const fetchTimer = join29(home, ".config", "systemd", "user", "wiki-fetch.timer");
|
|
5952
|
+
const fuseTimer = join29(home, ".config", "systemd", "user", "wiki-fuse-refresh.timer");
|
|
5953
|
+
const fuseService = join29(home, ".config", "systemd", "user", "wiki-fuse-refresh.service");
|
|
5954
|
+
checks.push(existsSync12(pushTimer) && existsSync12(fetchTimer) ? { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "pass", detail: "systemd timer unit files present (read-only mode)" } : { id: "vault_sync_jobs_enabled", label: "Vault sync jobs enabled", status: "warn", detail: "systemd timer unit files missing (read-only mode)" });
|
|
5955
|
+
checks.push(existsSync12(fuseTimer) && existsSync12(fuseService) ? { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "pass", detail: "wiki-fuse-refresh unit files present (read-only mode)" } : { id: "vault_sync_fuse_refresh_job", label: "Vault sync fuse refresh job", status: "warn", detail: "wiki-fuse-refresh unit files missing (read-only mode)" });
|
|
5956
|
+
}
|
|
5957
|
+
checks.push(classifyLog(join29(logDir, "wiki-push.log"), "vault_sync_last_push_age", "Vault sync last push recency", /OK push/));
|
|
5958
|
+
checks.push(classifyLog(join29(logDir, "wiki-fetch.log"), "vault_sync_last_fetch_status", "Vault sync last fetch status", /NOTIFY|OK behind|OK/));
|
|
5959
|
+
if (!existsSync12(filterPath)) {
|
|
5925
5960
|
checks.push({ id: "vault_sync_filter_present", label: "Vault sync filter file present", status: "error", detail: `Filter missing: ${filterPath}` });
|
|
5926
5961
|
} else {
|
|
5927
5962
|
const content = readFileSync8(filterPath, "utf8");
|
|
@@ -6147,7 +6182,7 @@ async function runHealth(input) {
|
|
|
6147
6182
|
|
|
6148
6183
|
// src/commands/archive.ts
|
|
6149
6184
|
import { rename as rename7, mkdir as mkdir9, readFile as readFile20, writeFile as writeFile10 } from "fs/promises";
|
|
6150
|
-
import { join as
|
|
6185
|
+
import { join as join30, dirname as dirname11 } from "path";
|
|
6151
6186
|
function countWikilinks(body, slug) {
|
|
6152
6187
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6153
6188
|
const re = new RegExp(`\\[\\[${escaped}(?:[|#][^\\]]*)?\\]\\]`, "g");
|
|
@@ -6175,7 +6210,7 @@ async function runArchive(input) {
|
|
|
6175
6210
|
if (!relPath) return { exitCode: ExitCode.ARCHIVE_TARGET_NOT_FOUND, result: err("ARCHIVE_TARGET_NOT_FOUND", { page: input.page }) };
|
|
6176
6211
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
6177
6212
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
6178
|
-
const archivePath =
|
|
6213
|
+
const archivePath = join30("_archive", relPath).replace(/\\/g, "/");
|
|
6179
6214
|
let cascade;
|
|
6180
6215
|
if (input.cascade) {
|
|
6181
6216
|
const wikilinkRefs = [];
|
|
@@ -6199,7 +6234,7 @@ async function runArchive(input) {
|
|
|
6199
6234
|
const indexRefs = [];
|
|
6200
6235
|
if (!isRaw) {
|
|
6201
6236
|
try {
|
|
6202
|
-
const idx = await readFile20(
|
|
6237
|
+
const idx = await readFile20(join30(input.vault, "index.md"), "utf8");
|
|
6203
6238
|
idx.split("\n").forEach((line, i) => {
|
|
6204
6239
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
6205
6240
|
});
|
|
@@ -6225,7 +6260,7 @@ async function runArchive(input) {
|
|
|
6225
6260
|
}
|
|
6226
6261
|
if (input.cascade && input.apply && cascade) {
|
|
6227
6262
|
for (const ref of cascade.source_array_refs) {
|
|
6228
|
-
const absPath =
|
|
6263
|
+
const absPath = join30(input.vault, ref.page);
|
|
6229
6264
|
const text = await readFile20(absPath, "utf8");
|
|
6230
6265
|
const split = splitFrontmatter(text);
|
|
6231
6266
|
if (!split.ok) continue;
|
|
@@ -6243,10 +6278,10 @@ ${fmRewritten}
|
|
|
6243
6278
|
}
|
|
6244
6279
|
}
|
|
6245
6280
|
}
|
|
6246
|
-
await mkdir9(dirname11(
|
|
6281
|
+
await mkdir9(dirname11(join30(input.vault, archivePath)), { recursive: true });
|
|
6247
6282
|
let indexUpdated = false;
|
|
6248
6283
|
if (!isRaw) {
|
|
6249
|
-
const indexPath =
|
|
6284
|
+
const indexPath = join30(input.vault, "index.md");
|
|
6250
6285
|
try {
|
|
6251
6286
|
const idx = await readFile20(indexPath, "utf8");
|
|
6252
6287
|
const originalLines = idx.split("\n");
|
|
@@ -6259,7 +6294,7 @@ ${fmRewritten}
|
|
|
6259
6294
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
6260
6295
|
}
|
|
6261
6296
|
}
|
|
6262
|
-
await rename7(
|
|
6297
|
+
await rename7(join30(input.vault, relPath), join30(input.vault, archivePath));
|
|
6263
6298
|
appendLastOp(input.vault, {
|
|
6264
6299
|
operation: input.cascade ? "archive-cascade" : "archive",
|
|
6265
6300
|
summary: `moved ${relPath} to ${archivePath}${input.cascade ? ` (cascade: ${cascade?.source_array_refs.length ?? 0} source arrays updated)` : ""}`,
|
|
@@ -6668,7 +6703,7 @@ ${newBody}`;
|
|
|
6668
6703
|
|
|
6669
6704
|
// src/commands/update.ts
|
|
6670
6705
|
import { execSync as execSync3 } from "child_process";
|
|
6671
|
-
import { join as
|
|
6706
|
+
import { join as join31 } from "path";
|
|
6672
6707
|
|
|
6673
6708
|
// src/utils/package-info.ts
|
|
6674
6709
|
import { readFileSync as readFileSync9 } from "fs";
|
|
@@ -6698,7 +6733,7 @@ function resolveGlobalSkillsRoot() {
|
|
|
6698
6733
|
encoding: "utf8",
|
|
6699
6734
|
timeout: 5e3
|
|
6700
6735
|
}).trim();
|
|
6701
|
-
return
|
|
6736
|
+
return join31(globalRoot, "skillwiki", "skills");
|
|
6702
6737
|
} catch {
|
|
6703
6738
|
return null;
|
|
6704
6739
|
}
|
|
@@ -6722,7 +6757,7 @@ async function runUpdate(input) {
|
|
|
6722
6757
|
const pkg2 = readCliPackageJson();
|
|
6723
6758
|
const currentVersion = pkg2.version;
|
|
6724
6759
|
const tag = normalizeDistTag(input.distTag);
|
|
6725
|
-
const target =
|
|
6760
|
+
const target = join31(input.home, ".claude", "skills");
|
|
6726
6761
|
let latest;
|
|
6727
6762
|
try {
|
|
6728
6763
|
latest = execSync3(`npm view skillwiki@${tag} version`, {
|
|
@@ -6793,15 +6828,15 @@ async function runUpdate(input) {
|
|
|
6793
6828
|
|
|
6794
6829
|
// src/commands/self-update.ts
|
|
6795
6830
|
import { execSync as execSync4 } from "child_process";
|
|
6796
|
-
import { existsSync as
|
|
6797
|
-
import { join as
|
|
6831
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
6832
|
+
import { join as join32 } from "path";
|
|
6798
6833
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
6799
6834
|
async function runSelfUpdate(input) {
|
|
6800
6835
|
const currentVersion = readCliPackageJson().version;
|
|
6801
6836
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
6802
6837
|
const distTag = normalizeDistTag(input.distTag);
|
|
6803
|
-
const localPkgPath =
|
|
6804
|
-
const hasLocalSource =
|
|
6838
|
+
const localPkgPath = join32(sourceRoot, "packages", "cli", "package.json");
|
|
6839
|
+
const hasLocalSource = existsSync13(localPkgPath);
|
|
6805
6840
|
if (input.check) {
|
|
6806
6841
|
let availableVersion = null;
|
|
6807
6842
|
let source;
|
|
@@ -6932,10 +6967,10 @@ async function runSelfUpdate(input) {
|
|
|
6932
6967
|
}
|
|
6933
6968
|
|
|
6934
6969
|
// src/commands/transcripts.ts
|
|
6935
|
-
import { readdir as readdir5, stat as
|
|
6936
|
-
import { join as
|
|
6970
|
+
import { readdir as readdir5, stat as stat8, readFile as readFile21 } from "fs/promises";
|
|
6971
|
+
import { join as join33 } from "path";
|
|
6937
6972
|
async function runTranscripts(input) {
|
|
6938
|
-
const dir =
|
|
6973
|
+
const dir = join33(input.vault, "raw", "transcripts");
|
|
6939
6974
|
let entries;
|
|
6940
6975
|
try {
|
|
6941
6976
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -6945,13 +6980,13 @@ async function runTranscripts(input) {
|
|
|
6945
6980
|
const transcripts = [];
|
|
6946
6981
|
for (const entry of entries) {
|
|
6947
6982
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6948
|
-
const filePath =
|
|
6983
|
+
const filePath = join33(dir, entry.name);
|
|
6949
6984
|
const content = await readFile21(filePath, "utf8");
|
|
6950
6985
|
const fm = extractFrontmatter(content);
|
|
6951
6986
|
if (!fm.ok) continue;
|
|
6952
6987
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
6953
6988
|
if (input.since && ingested && ingested < input.since) continue;
|
|
6954
|
-
const s = await
|
|
6989
|
+
const s = await stat8(filePath);
|
|
6955
6990
|
transcripts.push({
|
|
6956
6991
|
file: `raw/transcripts/${entry.name}`,
|
|
6957
6992
|
ingested,
|
|
@@ -6964,11 +6999,11 @@ async function runTranscripts(input) {
|
|
|
6964
6999
|
|
|
6965
7000
|
// src/commands/project-index.ts
|
|
6966
7001
|
import { readdir as readdir6, readFile as readFile22, writeFile as writeFile11, mkdir as mkdir10 } from "fs/promises";
|
|
6967
|
-
import { join as
|
|
7002
|
+
import { join as join34, dirname as dirname12 } from "path";
|
|
6968
7003
|
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
6969
7004
|
async function runProjectIndex(input) {
|
|
6970
7005
|
const slug = input.slug;
|
|
6971
|
-
const projectDir =
|
|
7006
|
+
const projectDir = join34(input.vault, "projects", slug);
|
|
6972
7007
|
try {
|
|
6973
7008
|
await readdir6(projectDir);
|
|
6974
7009
|
} catch {
|
|
@@ -6979,12 +7014,12 @@ async function runProjectIndex(input) {
|
|
|
6979
7014
|
}
|
|
6980
7015
|
const wikilinkPattern = `[[${slug}]]`;
|
|
6981
7016
|
const entries = [];
|
|
6982
|
-
const compoundDir =
|
|
7017
|
+
const compoundDir = join34(input.vault, "projects", slug, "compound");
|
|
6983
7018
|
try {
|
|
6984
7019
|
const compoundFiles = await readdir6(compoundDir, { withFileTypes: true });
|
|
6985
7020
|
for (const entry of compoundFiles) {
|
|
6986
7021
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
6987
|
-
const filePath =
|
|
7022
|
+
const filePath = join34(compoundDir, entry.name);
|
|
6988
7023
|
let text;
|
|
6989
7024
|
try {
|
|
6990
7025
|
text = await readFile22(filePath, "utf8");
|
|
@@ -7004,13 +7039,13 @@ async function runProjectIndex(input) {
|
|
|
7004
7039
|
for (const dir of LAYER2_DIRS) {
|
|
7005
7040
|
let files;
|
|
7006
7041
|
try {
|
|
7007
|
-
files = await readdir6(
|
|
7042
|
+
files = await readdir6(join34(input.vault, dir), { withFileTypes: true });
|
|
7008
7043
|
} catch {
|
|
7009
7044
|
continue;
|
|
7010
7045
|
}
|
|
7011
7046
|
for (const entry of files) {
|
|
7012
7047
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
7013
|
-
const filePath =
|
|
7048
|
+
const filePath = join34(input.vault, dir, entry.name);
|
|
7014
7049
|
let text;
|
|
7015
7050
|
try {
|
|
7016
7051
|
text = await readFile22(filePath, "utf8");
|
|
@@ -7034,7 +7069,7 @@ async function runProjectIndex(input) {
|
|
|
7034
7069
|
const tb = typeOrder[b.type] ?? 99;
|
|
7035
7070
|
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
7036
7071
|
});
|
|
7037
|
-
const indexPath =
|
|
7072
|
+
const indexPath = join34(projectDir, "knowledge.md");
|
|
7038
7073
|
let existing = false;
|
|
7039
7074
|
let stale = false;
|
|
7040
7075
|
try {
|
|
@@ -7108,8 +7143,8 @@ ${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e
|
|
|
7108
7143
|
|
|
7109
7144
|
// src/commands/compound.ts
|
|
7110
7145
|
import { writeFile as writeFile12, mkdir as mkdir11, readdir as readdir7, unlink as unlink4 } from "fs/promises";
|
|
7111
|
-
import { join as
|
|
7112
|
-
import { existsSync as
|
|
7146
|
+
import { join as join35 } from "path";
|
|
7147
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7113
7148
|
import { readFile as readFile23 } from "fs/promises";
|
|
7114
7149
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
7115
7150
|
var FIELD_RE = {
|
|
@@ -7208,7 +7243,7 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
7208
7243
|
};
|
|
7209
7244
|
}
|
|
7210
7245
|
async function runCompound(input) {
|
|
7211
|
-
const logPath =
|
|
7246
|
+
const logPath = join35(input.vault, "log.md");
|
|
7212
7247
|
let logText;
|
|
7213
7248
|
try {
|
|
7214
7249
|
logText = await readFile23(logPath, "utf8");
|
|
@@ -7218,7 +7253,7 @@ async function runCompound(input) {
|
|
|
7218
7253
|
const entries = parseRetroEntries(logText);
|
|
7219
7254
|
const promoted = [];
|
|
7220
7255
|
const skipped = [];
|
|
7221
|
-
const compoundDir =
|
|
7256
|
+
const compoundDir = join35(input.vault, "projects", input.project, "compound");
|
|
7222
7257
|
for (const entry of entries) {
|
|
7223
7258
|
const generalizeValue = entry.generalize.trim();
|
|
7224
7259
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -7226,8 +7261,8 @@ async function runCompound(input) {
|
|
|
7226
7261
|
continue;
|
|
7227
7262
|
}
|
|
7228
7263
|
const slug = slugify(entry.cycleName);
|
|
7229
|
-
const compoundPath =
|
|
7230
|
-
if (
|
|
7264
|
+
const compoundPath = join35(compoundDir, `${slug}.md`);
|
|
7265
|
+
if (existsSync14(compoundPath)) {
|
|
7231
7266
|
skipped.push(entry.date);
|
|
7232
7267
|
continue;
|
|
7233
7268
|
}
|
|
@@ -7265,7 +7300,7 @@ async function runCompound(input) {
|
|
|
7265
7300
|
].join("\n");
|
|
7266
7301
|
const content = frontmatter + "\n" + body;
|
|
7267
7302
|
if (!input.dryRun) {
|
|
7268
|
-
if (!
|
|
7303
|
+
if (!existsSync14(compoundDir)) {
|
|
7269
7304
|
await mkdir11(compoundDir, { recursive: true });
|
|
7270
7305
|
}
|
|
7271
7306
|
await writeFile12(compoundPath, content, "utf8");
|
|
@@ -7287,16 +7322,16 @@ async function runCompound(input) {
|
|
|
7287
7322
|
};
|
|
7288
7323
|
}
|
|
7289
7324
|
async function runCompoundDelete(input) {
|
|
7290
|
-
const projectDir =
|
|
7291
|
-
if (!
|
|
7325
|
+
const projectDir = join35(input.vault, "projects", input.project);
|
|
7326
|
+
if (!existsSync14(projectDir)) {
|
|
7292
7327
|
return {
|
|
7293
7328
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
7294
7329
|
result: err("PROJECT_NOT_FOUND", { slug: input.project, path: projectDir })
|
|
7295
7330
|
};
|
|
7296
7331
|
}
|
|
7297
7332
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
7298
|
-
const compoundPath =
|
|
7299
|
-
if (!
|
|
7333
|
+
const compoundPath = join35(projectDir, "compound", `${entryName}.md`);
|
|
7334
|
+
if (!existsSync14(compoundPath)) {
|
|
7300
7335
|
return {
|
|
7301
7336
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
7302
7337
|
result: err("FILE_NOT_FOUND", { path: compoundPath })
|
|
@@ -7329,8 +7364,8 @@ knowledge.md regenerated`
|
|
|
7329
7364
|
};
|
|
7330
7365
|
}
|
|
7331
7366
|
async function runCompoundList(input) {
|
|
7332
|
-
const compoundDir =
|
|
7333
|
-
if (!
|
|
7367
|
+
const compoundDir = join35(input.vault, "projects", input.project, "compound");
|
|
7368
|
+
if (!existsSync14(compoundDir)) {
|
|
7334
7369
|
return {
|
|
7335
7370
|
exitCode: ExitCode.OK,
|
|
7336
7371
|
result: ok({
|
|
@@ -7360,7 +7395,7 @@ could not read compound directory`
|
|
|
7360
7395
|
const entries = [];
|
|
7361
7396
|
for (const dirent of dirents) {
|
|
7362
7397
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
7363
|
-
const filePath =
|
|
7398
|
+
const filePath = join35(compoundDir, dirent.name);
|
|
7364
7399
|
let text;
|
|
7365
7400
|
try {
|
|
7366
7401
|
text = await readFile23(filePath, "utf8");
|
|
@@ -7393,8 +7428,8 @@ no compound entries found`;
|
|
|
7393
7428
|
|
|
7394
7429
|
// src/commands/observe.ts
|
|
7395
7430
|
import { mkdir as mkdir12, writeFile as writeFile13 } from "fs/promises";
|
|
7396
|
-
import { existsSync as
|
|
7397
|
-
import { join as
|
|
7431
|
+
import { existsSync as existsSync15, statSync as statSync4 } from "fs";
|
|
7432
|
+
import { join as join36 } from "path";
|
|
7398
7433
|
import { createHash as createHash4 } from "crypto";
|
|
7399
7434
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
7400
7435
|
function slugify2(text) {
|
|
@@ -7417,13 +7452,13 @@ async function runObserve(input) {
|
|
|
7417
7452
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
7418
7453
|
};
|
|
7419
7454
|
}
|
|
7420
|
-
if (!
|
|
7455
|
+
if (!existsSync15(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
7421
7456
|
return {
|
|
7422
7457
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
7423
7458
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
7424
7459
|
};
|
|
7425
7460
|
}
|
|
7426
|
-
const transcriptsDir =
|
|
7461
|
+
const transcriptsDir = join36(input.vault, "raw", "transcripts");
|
|
7427
7462
|
try {
|
|
7428
7463
|
await mkdir12(transcriptsDir, { recursive: true });
|
|
7429
7464
|
} catch {
|
|
@@ -7435,7 +7470,7 @@ async function runObserve(input) {
|
|
|
7435
7470
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7436
7471
|
const slug = slugify2(input.text);
|
|
7437
7472
|
const fileName = `${today}-observation-${slug}.md`;
|
|
7438
|
-
const filePath =
|
|
7473
|
+
const filePath = join36(transcriptsDir, fileName);
|
|
7439
7474
|
const body = `
|
|
7440
7475
|
${input.text.trim()}
|
|
7441
7476
|
`;
|
|
@@ -7476,7 +7511,7 @@ ${input.text.trim()}
|
|
|
7476
7511
|
|
|
7477
7512
|
// src/commands/session-brief.ts
|
|
7478
7513
|
import { mkdir as mkdir13, readFile as readFile24, writeFile as writeFile14 } from "fs/promises";
|
|
7479
|
-
import { join as
|
|
7514
|
+
import { join as join37, relative as relative3, sep as sep3 } from "path";
|
|
7480
7515
|
var MAX_WORDS = 900;
|
|
7481
7516
|
async function runSessionBrief(input) {
|
|
7482
7517
|
const scan = await scanVault(input.vault);
|
|
@@ -7554,7 +7589,7 @@ async function resolveProject(input) {
|
|
|
7554
7589
|
const envProject = input.env?.SKILLWIKI_PROJECT;
|
|
7555
7590
|
if (envProject) return envProject;
|
|
7556
7591
|
const cwd = input.cwd ?? process.cwd();
|
|
7557
|
-
const projectDotenv = await readProjectSlug(
|
|
7592
|
+
const projectDotenv = await readProjectSlug(join37(cwd, ".skillwiki", ".env"));
|
|
7558
7593
|
if (projectDotenv) return projectDotenv;
|
|
7559
7594
|
const inferred = inferProjectFromPath(input.vault, cwd);
|
|
7560
7595
|
if (inferred) return inferred;
|
|
@@ -7688,7 +7723,7 @@ function appendTextSection(lines, title, items, empty) {
|
|
|
7688
7723
|
lines.push("");
|
|
7689
7724
|
}
|
|
7690
7725
|
async function loadHealthWarnings(vault) {
|
|
7691
|
-
const text = await readIfExists2(
|
|
7726
|
+
const text = await readIfExists2(join37(vault, ".skillwiki", "health.json"));
|
|
7692
7727
|
if (!text) return [];
|
|
7693
7728
|
try {
|
|
7694
7729
|
const parsed = JSON.parse(text);
|
|
@@ -7701,11 +7736,11 @@ async function loadHealthWarnings(vault) {
|
|
|
7701
7736
|
}
|
|
7702
7737
|
}
|
|
7703
7738
|
async function writeBriefArtifacts(vault, input) {
|
|
7704
|
-
const metaPath =
|
|
7705
|
-
const cacheMdPath =
|
|
7706
|
-
const cacheJsonPath =
|
|
7707
|
-
await mkdir13(
|
|
7708
|
-
await mkdir13(
|
|
7739
|
+
const metaPath = join37(vault, "meta", "latest-session-brief.md");
|
|
7740
|
+
const cacheMdPath = join37(vault, ".skillwiki", "session-brief.md");
|
|
7741
|
+
const cacheJsonPath = join37(vault, ".skillwiki", "session-brief.json");
|
|
7742
|
+
await mkdir13(join37(vault, "meta"), { recursive: true });
|
|
7743
|
+
await mkdir13(join37(vault, ".skillwiki"), { recursive: true });
|
|
7709
7744
|
const committed = renderCommittedBrief(input);
|
|
7710
7745
|
const previousComparable = comparableBrief(await readIfExists2(metaPath));
|
|
7711
7746
|
const nextComparable = comparableBrief(committed);
|
|
@@ -7765,7 +7800,7 @@ function renderCommittedBrief(input) {
|
|
|
7765
7800
|
].filter((line) => line !== "").join("\n");
|
|
7766
7801
|
}
|
|
7767
7802
|
async function ensureIndexEntry(vault) {
|
|
7768
|
-
const indexPath =
|
|
7803
|
+
const indexPath = join37(vault, "index.md");
|
|
7769
7804
|
let text = await readIfExists2(indexPath);
|
|
7770
7805
|
if (!text) return false;
|
|
7771
7806
|
if (text.includes("[[meta/latest-session-brief]]")) return false;
|
|
@@ -7784,7 +7819,7 @@ async function ensureIndexEntry(vault) {
|
|
|
7784
7819
|
return true;
|
|
7785
7820
|
}
|
|
7786
7821
|
async function appendMaterialLog(vault, today) {
|
|
7787
|
-
const logPath =
|
|
7822
|
+
const logPath = join37(vault, "log.md");
|
|
7788
7823
|
const text = await readIfExists2(logPath);
|
|
7789
7824
|
if (!text) return false;
|
|
7790
7825
|
const entry = `
|
|
@@ -7837,7 +7872,7 @@ function dateFromPath(path) {
|
|
|
7837
7872
|
|
|
7838
7873
|
// src/commands/ingest.ts
|
|
7839
7874
|
import { readFile as readFile25, writeFile as writeFile15, mkdir as mkdir14 } from "fs/promises";
|
|
7840
|
-
import { join as
|
|
7875
|
+
import { join as join38 } from "path";
|
|
7841
7876
|
import { createHash as createHash5 } from "crypto";
|
|
7842
7877
|
var ALLOWED_TYPES = /* @__PURE__ */ new Set(["entity", "concept", "comparison", "query"]);
|
|
7843
7878
|
var TYPE_DIR = {
|
|
@@ -8011,8 +8046,8 @@ async function runIngest(input) {
|
|
|
8011
8046
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
8012
8047
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
8013
8048
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
8014
|
-
const rawAbsPath =
|
|
8015
|
-
const typedAbsPath =
|
|
8049
|
+
const rawAbsPath = join38(input.vault, rawRelPath);
|
|
8050
|
+
const typedAbsPath = join38(input.vault, typedRelPath);
|
|
8016
8051
|
const identity = assessSourceIdentity({
|
|
8017
8052
|
rawPath: rawRelPath,
|
|
8018
8053
|
sourceUrl: sourceUrl ?? void 0,
|
|
@@ -8094,7 +8129,7 @@ async function runIngest(input) {
|
|
|
8094
8129
|
};
|
|
8095
8130
|
}
|
|
8096
8131
|
try {
|
|
8097
|
-
await mkdir14(
|
|
8132
|
+
await mkdir14(join38(input.vault, "raw", "articles"), { recursive: true });
|
|
8098
8133
|
await writeFile15(rawAbsPath, rawContent, "utf8");
|
|
8099
8134
|
} catch (e) {
|
|
8100
8135
|
return {
|
|
@@ -8103,7 +8138,7 @@ async function runIngest(input) {
|
|
|
8103
8138
|
};
|
|
8104
8139
|
}
|
|
8105
8140
|
try {
|
|
8106
|
-
await mkdir14(
|
|
8141
|
+
await mkdir14(join38(input.vault, typedDir), { recursive: true });
|
|
8107
8142
|
await writeFile15(typedAbsPath, typedContent, "utf8");
|
|
8108
8143
|
} catch (e) {
|
|
8109
8144
|
return {
|
|
@@ -8282,12 +8317,12 @@ ${body}`;
|
|
|
8282
8317
|
}
|
|
8283
8318
|
|
|
8284
8319
|
// src/commands/sync.ts
|
|
8285
|
-
import { existsSync as
|
|
8286
|
-
import { join as
|
|
8320
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8321
|
+
import { join as join40 } from "path";
|
|
8287
8322
|
|
|
8288
8323
|
// src/utils/sync-lock.ts
|
|
8289
|
-
import { existsSync as
|
|
8290
|
-
import { join as
|
|
8324
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync2, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
8325
|
+
import { join as join39 } from "path";
|
|
8291
8326
|
import { createHash as createHash6 } from "crypto";
|
|
8292
8327
|
function getSessionId() {
|
|
8293
8328
|
if (process.env.CLAUDE_SESSION_ID) return process.env.CLAUDE_SESSION_ID;
|
|
@@ -8295,11 +8330,11 @@ function getSessionId() {
|
|
|
8295
8330
|
return process.pid.toString();
|
|
8296
8331
|
}
|
|
8297
8332
|
function lockPath(vault) {
|
|
8298
|
-
return
|
|
8333
|
+
return join39(vault, ".skillwiki", "sync.lock");
|
|
8299
8334
|
}
|
|
8300
8335
|
function readLock(vault) {
|
|
8301
8336
|
const path = lockPath(vault);
|
|
8302
|
-
if (!
|
|
8337
|
+
if (!existsSync16(path)) return null;
|
|
8303
8338
|
try {
|
|
8304
8339
|
const raw = readFileSync11(path, "utf8");
|
|
8305
8340
|
return JSON.parse(raw);
|
|
@@ -8314,8 +8349,8 @@ function isStale(lock, now) {
|
|
|
8314
8349
|
}
|
|
8315
8350
|
function acquireLock(vault, opts = {}) {
|
|
8316
8351
|
const path = lockPath(vault);
|
|
8317
|
-
const dir =
|
|
8318
|
-
if (!
|
|
8352
|
+
const dir = join39(vault, ".skillwiki");
|
|
8353
|
+
if (!existsSync16(dir)) {
|
|
8319
8354
|
mkdirSync5(dir, { recursive: true });
|
|
8320
8355
|
}
|
|
8321
8356
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -8360,7 +8395,7 @@ function writeLockedFile(path, lock) {
|
|
|
8360
8395
|
}
|
|
8361
8396
|
function releaseLock(vault, opts = {}) {
|
|
8362
8397
|
const path = lockPath(vault);
|
|
8363
|
-
if (!
|
|
8398
|
+
if (!existsSync16(path)) {
|
|
8364
8399
|
return { released: false };
|
|
8365
8400
|
}
|
|
8366
8401
|
const sessionId = opts.sessionId ?? getSessionId();
|
|
@@ -8389,7 +8424,7 @@ function releaseLock(vault, opts = {}) {
|
|
|
8389
8424
|
function runSyncStatus(input) {
|
|
8390
8425
|
const vault = input.vault;
|
|
8391
8426
|
const includeStashes = input.includeStashes ?? false;
|
|
8392
|
-
if (!
|
|
8427
|
+
if (!existsSync17(join40(vault, ".git"))) {
|
|
8393
8428
|
return {
|
|
8394
8429
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8395
8430
|
result: ok({
|
|
@@ -8467,7 +8502,7 @@ function runSyncStatus(input) {
|
|
|
8467
8502
|
}
|
|
8468
8503
|
async function runSyncPush(input) {
|
|
8469
8504
|
const vault = input.vault;
|
|
8470
|
-
if (!
|
|
8505
|
+
if (!existsSync17(join40(vault, ".git"))) {
|
|
8471
8506
|
return {
|
|
8472
8507
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8473
8508
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -8590,7 +8625,7 @@ function enableGitLongPathsOnWindows(vault) {
|
|
|
8590
8625
|
}
|
|
8591
8626
|
async function runSyncPull(input) {
|
|
8592
8627
|
const vault = input.vault;
|
|
8593
|
-
if (!
|
|
8628
|
+
if (!existsSync17(join40(vault, ".git"))) {
|
|
8594
8629
|
return {
|
|
8595
8630
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8596
8631
|
result: err("NOT_A_GIT_REPO", { path: vault })
|
|
@@ -8762,7 +8797,7 @@ function runSyncPeers(input) {
|
|
|
8762
8797
|
}
|
|
8763
8798
|
function runSyncLock(input) {
|
|
8764
8799
|
const vault = input.vault;
|
|
8765
|
-
if (!
|
|
8800
|
+
if (!existsSync17(vault)) {
|
|
8766
8801
|
return {
|
|
8767
8802
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8768
8803
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -8797,7 +8832,7 @@ function runSyncLock(input) {
|
|
|
8797
8832
|
}
|
|
8798
8833
|
function runSyncUnlock(input) {
|
|
8799
8834
|
const vault = input.vault;
|
|
8800
|
-
if (!
|
|
8835
|
+
if (!existsSync17(vault)) {
|
|
8801
8836
|
return {
|
|
8802
8837
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
8803
8838
|
result: err("VAULT_PATH_INVALID", { path: vault })
|
|
@@ -8831,7 +8866,7 @@ function runSyncUnlock(input) {
|
|
|
8831
8866
|
|
|
8832
8867
|
// src/commands/backup.ts
|
|
8833
8868
|
import { statSync as statSync5, readdirSync as readdirSync3, readFileSync as readFileSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync8 } from "fs";
|
|
8834
|
-
import { join as
|
|
8869
|
+
import { join as join41, relative as relative4, dirname as dirname13 } from "path";
|
|
8835
8870
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
8836
8871
|
|
|
8837
8872
|
// src/utils/s3-client.ts
|
|
@@ -8855,7 +8890,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node
|
|
|
8855
8890
|
function* walkMarkdown(dir, base) {
|
|
8856
8891
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
8857
8892
|
if (SKIP_DIRS2.has(entry.name)) continue;
|
|
8858
|
-
const full =
|
|
8893
|
+
const full = join41(dir, entry.name);
|
|
8859
8894
|
if (entry.isDirectory()) {
|
|
8860
8895
|
yield* walkMarkdown(full, base);
|
|
8861
8896
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -8878,7 +8913,7 @@ async function runBackupSync(input) {
|
|
|
8878
8913
|
let failed = 0;
|
|
8879
8914
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
8880
8915
|
for (const relPath of files) {
|
|
8881
|
-
const absPath =
|
|
8916
|
+
const absPath = join41(input.vault, relPath);
|
|
8882
8917
|
const localStat = statSync5(absPath);
|
|
8883
8918
|
let needsUpload = true;
|
|
8884
8919
|
try {
|
|
@@ -8954,7 +8989,7 @@ async function runBackupRestore(input) {
|
|
|
8954
8989
|
const objects = list.Contents ?? [];
|
|
8955
8990
|
for (const obj of objects) {
|
|
8956
8991
|
if (!obj.Key) continue;
|
|
8957
|
-
const localPath =
|
|
8992
|
+
const localPath = join41(target, obj.Key);
|
|
8958
8993
|
try {
|
|
8959
8994
|
const localStat = statSync5(localPath);
|
|
8960
8995
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -9000,11 +9035,11 @@ async function runBackupRestore(input) {
|
|
|
9000
9035
|
}
|
|
9001
9036
|
|
|
9002
9037
|
// src/commands/status.ts
|
|
9003
|
-
import { existsSync as
|
|
9038
|
+
import { existsSync as existsSync18, statSync as statSync6 } from "fs";
|
|
9004
9039
|
import { readFile as readFile26 } from "fs/promises";
|
|
9005
|
-
import { join as
|
|
9040
|
+
import { join as join42 } from "path";
|
|
9006
9041
|
async function runStatus(input) {
|
|
9007
|
-
if (!
|
|
9042
|
+
if (!existsSync18(input.vault)) {
|
|
9008
9043
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { vault: input.vault }) };
|
|
9009
9044
|
}
|
|
9010
9045
|
const scan = await scanVault(input.vault);
|
|
@@ -9029,7 +9064,7 @@ async function runStatus(input) {
|
|
|
9029
9064
|
const compound = scan.data.compound.length;
|
|
9030
9065
|
let schemaVersion = "v1";
|
|
9031
9066
|
try {
|
|
9032
|
-
const schemaContent = await readFile26(
|
|
9067
|
+
const schemaContent = await readFile26(join42(input.vault, "SCHEMA.md"), "utf8");
|
|
9033
9068
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
9034
9069
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
9035
9070
|
} catch {
|
|
@@ -9089,8 +9124,8 @@ async function runStatus(input) {
|
|
|
9089
9124
|
}
|
|
9090
9125
|
|
|
9091
9126
|
// src/commands/seed.ts
|
|
9092
|
-
import { mkdir as mkdir15, writeFile as writeFile16, stat as
|
|
9093
|
-
import { join as
|
|
9127
|
+
import { mkdir as mkdir15, writeFile as writeFile16, stat as stat9 } from "fs/promises";
|
|
9128
|
+
import { join as join43 } from "path";
|
|
9094
9129
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9095
9130
|
var EXAMPLE_PAGES = {
|
|
9096
9131
|
"entities/example-project.md": `---
|
|
@@ -9159,29 +9194,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
9159
9194
|
`;
|
|
9160
9195
|
async function runSeed(input) {
|
|
9161
9196
|
try {
|
|
9162
|
-
await
|
|
9197
|
+
await stat9(join43(input.vault, "SCHEMA.md"));
|
|
9163
9198
|
} catch {
|
|
9164
9199
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
9165
9200
|
}
|
|
9166
9201
|
const created = [];
|
|
9167
9202
|
const skipped = [];
|
|
9168
9203
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
9169
|
-
const absPath =
|
|
9204
|
+
const absPath = join43(input.vault, relPath);
|
|
9170
9205
|
try {
|
|
9171
|
-
await
|
|
9206
|
+
await stat9(absPath);
|
|
9172
9207
|
skipped.push(relPath);
|
|
9173
9208
|
} catch {
|
|
9174
|
-
await mkdir15(
|
|
9209
|
+
await mkdir15(join43(absPath, ".."), { recursive: true });
|
|
9175
9210
|
await writeFile16(absPath, content, "utf8");
|
|
9176
9211
|
created.push(relPath);
|
|
9177
9212
|
}
|
|
9178
9213
|
}
|
|
9179
|
-
const rawPath =
|
|
9214
|
+
const rawPath = join43(input.vault, "raw", "articles", "example-source.md");
|
|
9180
9215
|
try {
|
|
9181
|
-
await
|
|
9216
|
+
await stat9(rawPath);
|
|
9182
9217
|
skipped.push("raw/articles/example-source.md");
|
|
9183
9218
|
} catch {
|
|
9184
|
-
await mkdir15(
|
|
9219
|
+
await mkdir15(join43(rawPath, ".."), { recursive: true });
|
|
9185
9220
|
await writeFile16(rawPath, EXAMPLE_RAW, "utf8");
|
|
9186
9221
|
created.push("raw/articles/example-source.md");
|
|
9187
9222
|
}
|
|
@@ -9205,8 +9240,8 @@ async function runSeed(input) {
|
|
|
9205
9240
|
|
|
9206
9241
|
// src/commands/canvas.ts
|
|
9207
9242
|
import { readFile as readFile27, writeFile as writeFile17 } from "fs/promises";
|
|
9208
|
-
import { existsSync as
|
|
9209
|
-
import { join as
|
|
9243
|
+
import { existsSync as existsSync19 } from "fs";
|
|
9244
|
+
import { join as join44 } from "path";
|
|
9210
9245
|
var NODE_WIDTH = 240;
|
|
9211
9246
|
var NODE_HEIGHT = 60;
|
|
9212
9247
|
var COLUMN_SPACING = 400;
|
|
@@ -9284,8 +9319,8 @@ function buildCanvasEdges(adjacency) {
|
|
|
9284
9319
|
return edges;
|
|
9285
9320
|
}
|
|
9286
9321
|
async function runCanvasGenerate(input) {
|
|
9287
|
-
const graphPath = input.graphPath ??
|
|
9288
|
-
if (!
|
|
9322
|
+
const graphPath = input.graphPath ?? join44(input.vault, ".skillwiki", "graph.json");
|
|
9323
|
+
if (!existsSync19(graphPath)) {
|
|
9289
9324
|
return {
|
|
9290
9325
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
9291
9326
|
result: err("FILE_NOT_FOUND", {
|
|
@@ -9322,7 +9357,7 @@ async function runCanvasGenerate(input) {
|
|
|
9322
9357
|
const nodes = buildCanvasNodes(paths);
|
|
9323
9358
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
9324
9359
|
const canvas = { nodes, edges };
|
|
9325
|
-
const outPath =
|
|
9360
|
+
const outPath = join44(input.vault, "vault-graph.canvas");
|
|
9326
9361
|
try {
|
|
9327
9362
|
await writeFile17(outPath, JSON.stringify(canvas, null, 2));
|
|
9328
9363
|
} catch (e) {
|
|
@@ -9344,8 +9379,8 @@ written: ${outPath}`
|
|
|
9344
9379
|
}
|
|
9345
9380
|
|
|
9346
9381
|
// src/commands/query.ts
|
|
9347
|
-
import { readFile as readFile28, stat as
|
|
9348
|
-
import { join as
|
|
9382
|
+
import { readFile as readFile28, stat as stat10 } from "fs/promises";
|
|
9383
|
+
import { join as join45 } from "path";
|
|
9349
9384
|
var W_KEYWORD = 2;
|
|
9350
9385
|
var W_SOURCE_OVERLAP = 4;
|
|
9351
9386
|
var W_WIKILINK = 3;
|
|
@@ -9466,10 +9501,10 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
9466
9501
|
return score;
|
|
9467
9502
|
}
|
|
9468
9503
|
async function loadOrBuildGraph(vault) {
|
|
9469
|
-
const graphPath =
|
|
9504
|
+
const graphPath = join45(vault, ".skillwiki", "graph.json");
|
|
9470
9505
|
let needsBuild = false;
|
|
9471
9506
|
try {
|
|
9472
|
-
const fileStat = await
|
|
9507
|
+
const fileStat = await stat10(graphPath);
|
|
9473
9508
|
const ageHours = (Date.now() - fileStat.mtimeMs) / (1e3 * 60 * 60);
|
|
9474
9509
|
if (ageHours > 24) needsBuild = true;
|
|
9475
9510
|
} catch {
|
|
@@ -9490,9 +9525,9 @@ async function loadOrBuildGraph(vault) {
|
|
|
9490
9525
|
// src/commands/fleet.ts
|
|
9491
9526
|
import { readFile as readFile29 } from "fs/promises";
|
|
9492
9527
|
import { hostname as nodeHostname, userInfo } from "os";
|
|
9493
|
-
import { join as
|
|
9528
|
+
import { join as join46 } from "path";
|
|
9494
9529
|
import yaml3 from "js-yaml";
|
|
9495
|
-
var FLEET_REL_PATH =
|
|
9530
|
+
var FLEET_REL_PATH = join46("projects", "llm-wiki", "architecture", "fleet.yaml");
|
|
9496
9531
|
async function runFleetValidate(input) {
|
|
9497
9532
|
const loaded = await loadFleetManifest(input.file);
|
|
9498
9533
|
if (!loaded.ok) {
|
|
@@ -9523,7 +9558,7 @@ async function runFleetContext(input) {
|
|
|
9523
9558
|
const osHostname = input.osHostname ?? safeEnvValue(env.HOSTNAME) ?? nodeHostname();
|
|
9524
9559
|
const user = input.user ?? safeEnvValue(env.USER) ?? safeUserName();
|
|
9525
9560
|
const vault = input.vault ?? safeEnvValue(env.WIKI_PATH);
|
|
9526
|
-
const file = input.file ?? (vault ?
|
|
9561
|
+
const file = input.file ?? (vault ? join46(vault, FLEET_REL_PATH) : void 0);
|
|
9527
9562
|
const loaded = file ? await loadFleetManifest(file) : { ok: false, error: "FILE_NOT_FOUND" };
|
|
9528
9563
|
if (!loaded.ok) {
|
|
9529
9564
|
const markdown2 = formatUnknownContext({ osHostname, user, cwd, vault, reason: "fleet manifest unavailable or invalid" });
|
|
@@ -9630,7 +9665,7 @@ async function resolveHostId(input) {
|
|
|
9630
9665
|
if (input.env.SKILLWIKI_HOST_ID) return { hostId: input.env.SKILLWIKI_HOST_ID, source: "SKILLWIKI_HOST_ID" };
|
|
9631
9666
|
if (input.env.AGENT_HOST_ID) return { hostId: input.env.AGENT_HOST_ID, source: "AGENT_HOST_ID" };
|
|
9632
9667
|
if (input.home) {
|
|
9633
|
-
const dotenv = await parseDotenvFile(
|
|
9668
|
+
const dotenv = await parseDotenvFile(join46(input.home, ".skillwiki", ".env"));
|
|
9634
9669
|
if (dotenv.SKILLWIKI_HOST_ID) {
|
|
9635
9670
|
return { hostId: dotenv.SKILLWIKI_HOST_ID, source: "~/.skillwiki/.env:SKILLWIKI_HOST_ID" };
|
|
9636
9671
|
}
|
|
@@ -9740,14 +9775,14 @@ function safeUserName() {
|
|
|
9740
9775
|
}
|
|
9741
9776
|
|
|
9742
9777
|
// src/utils/auto-commit.ts
|
|
9743
|
-
import { existsSync as
|
|
9744
|
-
import { join as
|
|
9778
|
+
import { existsSync as existsSync20 } from "fs";
|
|
9779
|
+
import { join as join47 } from "path";
|
|
9745
9780
|
async function postCommit(vault, exitCode) {
|
|
9746
9781
|
if (exitCode !== 0) return;
|
|
9747
9782
|
const home = process.env.HOME ?? "";
|
|
9748
9783
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
9749
9784
|
if (dotenv["AUTO_COMMIT"] === "false") return;
|
|
9750
|
-
if (!
|
|
9785
|
+
if (!existsSync20(join47(vault, ".git"))) return;
|
|
9751
9786
|
const lastOps = readLastOp(vault);
|
|
9752
9787
|
if (lastOps.length === 0) return;
|
|
9753
9788
|
const porcelain = git(vault, ["status", "--porcelain"]);
|
|
@@ -9798,7 +9833,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
9798
9833
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
9799
9834
|
});
|
|
9800
9835
|
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) => {
|
|
9801
|
-
const out = opts.out ??
|
|
9836
|
+
const out = opts.out ?? join48(vault, ".skillwiki", "graph.json");
|
|
9802
9837
|
emit(await runGraphBuild({ vault, out }), vault);
|
|
9803
9838
|
});
|
|
9804
9839
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|