skillwiki 0.2.0-beta.3 → 0.2.0-beta.4
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 +277 -25
- package/package.json +13 -4
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { readFileSync } from "fs";
|
|
4
5
|
import { Command } from "commander";
|
|
5
6
|
|
|
6
7
|
// src/utils/output.ts
|
|
@@ -9,9 +10,14 @@ function printJson(r) {
|
|
|
9
10
|
}
|
|
10
11
|
function printHuman(r) {
|
|
11
12
|
if (r.ok) {
|
|
12
|
-
|
|
13
|
+
if (typeof r.data === "object" && r.data !== null && "humanHint" in r.data) {
|
|
14
|
+
process.stdout.write(`${r.data.humanHint}
|
|
15
|
+
`);
|
|
16
|
+
} else {
|
|
17
|
+
process.stdout.write(`OK
|
|
13
18
|
${formatData(r.data)}
|
|
14
19
|
`);
|
|
20
|
+
}
|
|
15
21
|
} else {
|
|
16
22
|
process.stdout.write(`ERR ${r.error}
|
|
17
23
|
${r.detail !== void 0 ? formatData(r.detail) + "\n" : ""}`);
|
|
@@ -53,15 +59,19 @@ var ExitCode = {
|
|
|
53
59
|
LINT_HAS_WARNINGS: 22,
|
|
54
60
|
LINT_HAS_ERRORS: 23,
|
|
55
61
|
ENV_WRITE_CONFLICT: 24,
|
|
56
|
-
NO_VAULT_CONFIGURED: 25
|
|
62
|
+
NO_VAULT_CONFIGURED: 25,
|
|
63
|
+
INVALID_CONFIG_KEY: 26,
|
|
64
|
+
CONFIG_WRITE_FAILED: 27,
|
|
65
|
+
DOCTOR_HAS_WARNINGS: 28,
|
|
66
|
+
DOCTOR_HAS_ERRORS: 29
|
|
57
67
|
};
|
|
58
68
|
|
|
59
69
|
// ../shared/src/json-output.ts
|
|
60
70
|
function ok(data) {
|
|
61
71
|
return { ok: true, data };
|
|
62
72
|
}
|
|
63
|
-
function err(
|
|
64
|
-
return detail === void 0 ? { ok: false, error } : { ok: false, error, detail };
|
|
73
|
+
function err(error2, detail) {
|
|
74
|
+
return detail === void 0 ? { ok: false, error: error2 } : { ok: false, error: error2, detail };
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
// ../shared/src/schemas.ts
|
|
@@ -479,7 +489,8 @@ async function runOverlap(input) {
|
|
|
479
489
|
import { join as join2 } from "path";
|
|
480
490
|
|
|
481
491
|
// src/utils/dotenv.ts
|
|
482
|
-
import { readFile as readFile4 } from "fs/promises";
|
|
492
|
+
import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
493
|
+
import { dirname as dirname2 } from "path";
|
|
483
494
|
var WHITELIST = /* @__PURE__ */ new Set(["WIKI_PATH", "WIKI_LANG"]);
|
|
484
495
|
async function parseDotenvFile(path) {
|
|
485
496
|
let text;
|
|
@@ -502,6 +513,50 @@ async function parseDotenvFile(path) {
|
|
|
502
513
|
}
|
|
503
514
|
return out;
|
|
504
515
|
}
|
|
516
|
+
async function writeDotenv(filePath, entries, originalContent) {
|
|
517
|
+
const lines = originalContent !== void 0 ? updateLines(originalContent, entries) : freshLines(entries);
|
|
518
|
+
await mkdir2(dirname2(filePath), { recursive: true });
|
|
519
|
+
await writeFile2(filePath, lines.join("\n") + "\n", "utf8");
|
|
520
|
+
}
|
|
521
|
+
function freshLines(entries) {
|
|
522
|
+
const out = [];
|
|
523
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
524
|
+
if (value !== void 0) out.push(`${key}=${value}`);
|
|
525
|
+
}
|
|
526
|
+
return out;
|
|
527
|
+
}
|
|
528
|
+
function updateLines(originalContent, entries) {
|
|
529
|
+
let rawLines = originalContent.split(/\r?\n/);
|
|
530
|
+
if (rawLines.length > 0 && rawLines[rawLines.length - 1] === "") {
|
|
531
|
+
rawLines = rawLines.slice(0, -1);
|
|
532
|
+
}
|
|
533
|
+
const keysToWrite = new Set(Object.keys(entries));
|
|
534
|
+
const out = [];
|
|
535
|
+
for (const line of rawLines) {
|
|
536
|
+
const trimmed = line.trim();
|
|
537
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) {
|
|
538
|
+
out.push(line);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
const eq = trimmed.indexOf("=");
|
|
542
|
+
if (eq <= 0) {
|
|
543
|
+
out.push(line);
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const key = trimmed.slice(0, eq).trim();
|
|
547
|
+
if (keysToWrite.has(key)) {
|
|
548
|
+
out.push(`${key}=${entries[key]}`);
|
|
549
|
+
keysToWrite.delete(key);
|
|
550
|
+
} else {
|
|
551
|
+
out.push(line);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
for (const key of keysToWrite) {
|
|
555
|
+
const value = entries[key];
|
|
556
|
+
if (value !== void 0) out.push(`${key}=${value}`);
|
|
557
|
+
}
|
|
558
|
+
return out;
|
|
559
|
+
}
|
|
505
560
|
|
|
506
561
|
// src/utils/wiki-path.ts
|
|
507
562
|
async function resolveInitTimePath(input) {
|
|
@@ -630,7 +685,7 @@ function simulateRemoval(adj, removed) {
|
|
|
630
685
|
|
|
631
686
|
// src/commands/audit.ts
|
|
632
687
|
import { readFile as readFile5, stat as stat2 } from "fs/promises";
|
|
633
|
-
import { dirname as
|
|
688
|
+
import { dirname as dirname3, resolve, join as join3 } from "path";
|
|
634
689
|
|
|
635
690
|
// src/parsers/citations.ts
|
|
636
691
|
var FENCE2 = /```[\s\S]*?```/g;
|
|
@@ -657,7 +712,7 @@ async function runAudit(input) {
|
|
|
657
712
|
if (!fm.ok) return { exitCode: ExitCode.INVALID_FRONTMATTER, result: fm };
|
|
658
713
|
const split = splitFrontmatter(text);
|
|
659
714
|
const body = split.ok ? split.data.body : text;
|
|
660
|
-
const vault = await findVaultRoot(
|
|
715
|
+
const vault = await findVaultRoot(dirname3(resolve(input.file)));
|
|
661
716
|
if (!vault) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID") };
|
|
662
717
|
const markers = extractCitationMarkers(body);
|
|
663
718
|
const resolved = await Promise.all(markers.map(async (m) => {
|
|
@@ -688,7 +743,7 @@ async function findVaultRoot(start) {
|
|
|
688
743
|
return cur;
|
|
689
744
|
} catch {
|
|
690
745
|
}
|
|
691
|
-
const parent =
|
|
746
|
+
const parent = dirname3(cur);
|
|
692
747
|
if (parent === cur) return null;
|
|
693
748
|
cur = parent;
|
|
694
749
|
}
|
|
@@ -700,10 +755,10 @@ import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
|
700
755
|
import { join as join4 } from "path";
|
|
701
756
|
|
|
702
757
|
// src/utils/install-fs.ts
|
|
703
|
-
import { copyFile, mkdir as
|
|
704
|
-
import { dirname as
|
|
758
|
+
import { copyFile, mkdir as mkdir3, rename, writeFile as writeFile3, stat as stat3 } from "fs/promises";
|
|
759
|
+
import { dirname as dirname4 } from "path";
|
|
705
760
|
async function atomicCopyWithBackup(src, dst) {
|
|
706
|
-
await
|
|
761
|
+
await mkdir3(dirname4(dst), { recursive: true });
|
|
707
762
|
let backupPath = null;
|
|
708
763
|
try {
|
|
709
764
|
await stat3(dst);
|
|
@@ -721,9 +776,9 @@ async function atomicCopyWithBackup(src, dst) {
|
|
|
721
776
|
return ok({ copied: true, backupPath });
|
|
722
777
|
}
|
|
723
778
|
async function writeManifest(path, m) {
|
|
724
|
-
await
|
|
779
|
+
await mkdir3(dirname4(path), { recursive: true });
|
|
725
780
|
const enriched = { installed_at: (/* @__PURE__ */ new Date()).toISOString(), ...m };
|
|
726
|
-
await
|
|
781
|
+
await writeFile3(path, JSON.stringify(enriched, null, 2));
|
|
727
782
|
}
|
|
728
783
|
|
|
729
784
|
// src/commands/install.ts
|
|
@@ -839,8 +894,8 @@ async function runLang(input) {
|
|
|
839
894
|
}
|
|
840
895
|
|
|
841
896
|
// src/commands/init.ts
|
|
842
|
-
import { mkdir as
|
|
843
|
-
import { join as join7, dirname as
|
|
897
|
+
import { mkdir as mkdir4, readFile as readFile6, stat as stat5, writeFile as writeFile4 } from "fs/promises";
|
|
898
|
+
import { join as join7, dirname as dirname5 } from "path";
|
|
844
899
|
var DEFAULT_TAXONOMY = [
|
|
845
900
|
"research",
|
|
846
901
|
"comparison",
|
|
@@ -899,9 +954,9 @@ async function runInit(input) {
|
|
|
899
954
|
}
|
|
900
955
|
const created = [];
|
|
901
956
|
try {
|
|
902
|
-
await
|
|
957
|
+
await mkdir4(target, { recursive: true });
|
|
903
958
|
for (const d of VAULT_DIRS) {
|
|
904
|
-
await
|
|
959
|
+
await mkdir4(join7(target, d), { recursive: true });
|
|
905
960
|
created.push(d + "/");
|
|
906
961
|
}
|
|
907
962
|
} catch (e) {
|
|
@@ -913,7 +968,7 @@ async function runInit(input) {
|
|
|
913
968
|
try {
|
|
914
969
|
const schemaTpl = await readFile6(join7(input.templates, "SCHEMA.md"), "utf8");
|
|
915
970
|
const schema = schemaTpl.replace("{{DOMAIN}}", input.domain).replace("{{WIKI_LANG}}", canonicalLang).replace("{{TAXONOMY_YAML}}", taxonomyYaml);
|
|
916
|
-
await
|
|
971
|
+
await writeFile4(join7(target, "SCHEMA.md"), schema, "utf8");
|
|
917
972
|
created.push("SCHEMA.md");
|
|
918
973
|
} catch (e) {
|
|
919
974
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: "SCHEMA.md", message: String(e) }) };
|
|
@@ -921,7 +976,7 @@ async function runInit(input) {
|
|
|
921
976
|
try {
|
|
922
977
|
const idxTpl = await readFile6(join7(input.templates, "index.md"), "utf8");
|
|
923
978
|
const idx = idxTpl.replace("{{INIT_DATE}}", today);
|
|
924
|
-
await
|
|
979
|
+
await writeFile4(join7(target, "index.md"), idx, "utf8");
|
|
925
980
|
created.push("index.md");
|
|
926
981
|
} catch (e) {
|
|
927
982
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: "index.md", message: String(e) }) };
|
|
@@ -929,17 +984,17 @@ async function runInit(input) {
|
|
|
929
984
|
try {
|
|
930
985
|
const logTpl = await readFile6(join7(input.templates, "log.md"), "utf8");
|
|
931
986
|
const log = logTpl.replace(/\{\{INIT_DATE\}\}/g, today).replace("{{DOMAIN}}", input.domain).replace("{{WIKI_LANG}}", canonicalLang);
|
|
932
|
-
await
|
|
987
|
+
await writeFile4(join7(target, "log.md"), log, "utf8");
|
|
933
988
|
created.push("log.md");
|
|
934
989
|
} catch (e) {
|
|
935
990
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: "log.md", message: String(e) }) };
|
|
936
991
|
}
|
|
937
992
|
try {
|
|
938
|
-
await
|
|
993
|
+
await mkdir4(dirname5(envPath), { recursive: true });
|
|
939
994
|
const envBody = `WIKI_PATH=${target}
|
|
940
995
|
WIKI_LANG=${canonicalLang}
|
|
941
996
|
`;
|
|
942
|
-
await
|
|
997
|
+
await writeFile4(envPath, envBody, "utf8");
|
|
943
998
|
} catch (e) {
|
|
944
999
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { file: envPath, message: String(e) }) };
|
|
945
1000
|
}
|
|
@@ -1131,7 +1186,7 @@ async function runPagesize(input) {
|
|
|
1131
1186
|
}
|
|
1132
1187
|
|
|
1133
1188
|
// src/commands/log-rotate.ts
|
|
1134
|
-
import { readFile as readFile10, rename as rename2, writeFile as
|
|
1189
|
+
import { readFile as readFile10, rename as rename2, writeFile as writeFile5, stat as stat6 } from "fs/promises";
|
|
1135
1190
|
import { join as join11 } from "path";
|
|
1136
1191
|
var ENTRY_RE = /^## \[(\d{4})-\d{2}-\d{2}\]/gm;
|
|
1137
1192
|
async function runLogRotate(input) {
|
|
@@ -1172,7 +1227,7 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
|
|
|
1172
1227
|
|
|
1173
1228
|
- Previous log moved to ${rotatedName}
|
|
1174
1229
|
`;
|
|
1175
|
-
await
|
|
1230
|
+
await writeFile5(logPath, fresh, "utf8");
|
|
1176
1231
|
} catch (e) {
|
|
1177
1232
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
|
|
1178
1233
|
}
|
|
@@ -1236,9 +1291,195 @@ async function runLint(input) {
|
|
|
1236
1291
|
};
|
|
1237
1292
|
}
|
|
1238
1293
|
|
|
1294
|
+
// src/commands/config.ts
|
|
1295
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
1296
|
+
import { existsSync } from "fs";
|
|
1297
|
+
import { join as join12 } from "path";
|
|
1298
|
+
function validateKey(key) {
|
|
1299
|
+
return key === "WIKI_PATH" || key === "WIKI_LANG";
|
|
1300
|
+
}
|
|
1301
|
+
function configPath(home) {
|
|
1302
|
+
return join12(home, ".skillwiki", ".env");
|
|
1303
|
+
}
|
|
1304
|
+
async function runConfigGet(input) {
|
|
1305
|
+
if (!validateKey(input.key)) {
|
|
1306
|
+
return { exitCode: ExitCode.INVALID_CONFIG_KEY, result: err("INVALID_CONFIG_KEY", { key: input.key }) };
|
|
1307
|
+
}
|
|
1308
|
+
const map = await parseDotenvFile(configPath(input.home));
|
|
1309
|
+
const value = map[input.key] ?? "";
|
|
1310
|
+
return { exitCode: ExitCode.OK, result: ok({ key: input.key, value, humanHint: value }) };
|
|
1311
|
+
}
|
|
1312
|
+
async function runConfigSet(input) {
|
|
1313
|
+
if (!validateKey(input.key)) {
|
|
1314
|
+
return { exitCode: ExitCode.INVALID_CONFIG_KEY, result: err("INVALID_CONFIG_KEY", { key: input.key }) };
|
|
1315
|
+
}
|
|
1316
|
+
const filePath = configPath(input.home);
|
|
1317
|
+
try {
|
|
1318
|
+
let originalContent;
|
|
1319
|
+
try {
|
|
1320
|
+
originalContent = await readFile11(filePath, "utf8");
|
|
1321
|
+
} catch {
|
|
1322
|
+
}
|
|
1323
|
+
const existing = originalContent !== void 0 ? await parseDotenvFile(filePath) : {};
|
|
1324
|
+
const merged = { ...existing, [input.key]: input.value };
|
|
1325
|
+
await writeDotenv(filePath, merged, originalContent);
|
|
1326
|
+
return { exitCode: ExitCode.OK, result: ok({ key: input.key, value: input.value, written: true, humanHint: `${input.key}=${input.value}` }) };
|
|
1327
|
+
} catch (e) {
|
|
1328
|
+
return { exitCode: ExitCode.CONFIG_WRITE_FAILED, result: err("CONFIG_WRITE_FAILED", { key: input.key, error: String(e) }) };
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
async function runConfigList(input) {
|
|
1332
|
+
const map = await parseDotenvFile(configPath(input.home));
|
|
1333
|
+
const entries = Object.entries(map).map(([key, value]) => ({ key, value: value ?? "" }));
|
|
1334
|
+
return { exitCode: ExitCode.OK, result: ok({ entries, humanHint: entries.map((e) => `${e.key}=${e.value}`).join("\n") }) };
|
|
1335
|
+
}
|
|
1336
|
+
async function runConfigPath(input) {
|
|
1337
|
+
const filePath = configPath(input.home);
|
|
1338
|
+
return { exitCode: ExitCode.OK, result: ok({ path: filePath, exists: existsSync(filePath), humanHint: filePath }) };
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// src/commands/doctor.ts
|
|
1342
|
+
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
1343
|
+
import { join as join13 } from "path";
|
|
1344
|
+
import { execSync } from "child_process";
|
|
1345
|
+
function pass(id, label, detail) {
|
|
1346
|
+
return { id, label, status: "pass", detail };
|
|
1347
|
+
}
|
|
1348
|
+
function warn(id, label, detail) {
|
|
1349
|
+
return { id, label, status: "warn", detail };
|
|
1350
|
+
}
|
|
1351
|
+
function error(id, label, detail) {
|
|
1352
|
+
return { id, label, status: "error", detail };
|
|
1353
|
+
}
|
|
1354
|
+
function checkNodeVersion() {
|
|
1355
|
+
const major = parseInt(process.version.slice(1).split(".")[0], 10);
|
|
1356
|
+
if (major >= 20) {
|
|
1357
|
+
return pass("node_version", "Node.js version", `v${major} >= 20`);
|
|
1358
|
+
}
|
|
1359
|
+
return error("node_version", "Node.js version", `Node.js v${major} is below minimum v20`);
|
|
1360
|
+
}
|
|
1361
|
+
function checkCliOnPath(argv) {
|
|
1362
|
+
if (argv.length >= 2 && argv[1].endsWith("cli.js")) {
|
|
1363
|
+
return warn("cli_on_path", "skillwiki on PATH", "Running via node cli.js (dev mode) \u2014 PATH check skipped");
|
|
1364
|
+
}
|
|
1365
|
+
if (argv.length >= 2 && argv[1] === "skillwiki") {
|
|
1366
|
+
return pass("cli_on_path", "skillwiki on PATH", "Running as skillwiki \u2014 already on PATH");
|
|
1367
|
+
}
|
|
1368
|
+
try {
|
|
1369
|
+
execSync("which skillwiki 2>/dev/null", { encoding: "utf8" }).trim();
|
|
1370
|
+
return pass("cli_on_path", "skillwiki on PATH", "skillwiki found on PATH");
|
|
1371
|
+
} catch {
|
|
1372
|
+
return warn("cli_on_path", "skillwiki on PATH", "skillwiki not found on PATH");
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
async function checkConfigFile(home) {
|
|
1376
|
+
const cfgPath = join13(home, ".skillwiki", ".env");
|
|
1377
|
+
if (!existsSync2(cfgPath)) {
|
|
1378
|
+
return warn("config_file", "Config file exists", `${cfgPath} not found`);
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
const map = await parseDotenvFile(cfgPath);
|
|
1382
|
+
const keys = Object.keys(map);
|
|
1383
|
+
return pass("config_file", "Config file exists", `Found with keys: ${keys.length > 0 ? keys.join(", ") : "(none set)"}`);
|
|
1384
|
+
} catch (e) {
|
|
1385
|
+
return warn("config_file", "Config file exists", `Failed to parse ${cfgPath}: ${String(e)}`);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
async function checkWikiPathSet(input) {
|
|
1389
|
+
const r = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
|
|
1390
|
+
if (r.ok) {
|
|
1391
|
+
return pass("wiki_path_set", "WIKI_PATH configured", `Resolved via ${r.data.source}: ${r.data.path}`);
|
|
1392
|
+
}
|
|
1393
|
+
return error("wiki_path_set", "WIKI_PATH configured", "No vault configured. Run `skillwiki init` or pass --vault.");
|
|
1394
|
+
}
|
|
1395
|
+
function checkWikiPathExists(resolvedPath) {
|
|
1396
|
+
if (resolvedPath === void 0) {
|
|
1397
|
+
return error("wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
1398
|
+
}
|
|
1399
|
+
if (existsSync2(resolvedPath) && statSync(resolvedPath).isDirectory()) {
|
|
1400
|
+
return pass("wiki_path_exists", "Vault directory exists", resolvedPath);
|
|
1401
|
+
}
|
|
1402
|
+
return error("wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
|
|
1403
|
+
}
|
|
1404
|
+
function checkVaultStructure(resolvedPath) {
|
|
1405
|
+
if (resolvedPath === void 0) {
|
|
1406
|
+
return error("vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
|
|
1407
|
+
}
|
|
1408
|
+
if (!existsSync2(resolvedPath)) {
|
|
1409
|
+
return error("vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
|
|
1410
|
+
}
|
|
1411
|
+
const missing = [];
|
|
1412
|
+
if (!existsSync2(join13(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
|
|
1413
|
+
for (const dir of ["raw", "entities", "concepts", "meta"]) {
|
|
1414
|
+
if (!existsSync2(join13(resolvedPath, dir))) missing.push(dir + "/");
|
|
1415
|
+
}
|
|
1416
|
+
if (missing.length === 0) {
|
|
1417
|
+
return pass("vault_structure", "Vault structure valid", "All required files and directories present");
|
|
1418
|
+
}
|
|
1419
|
+
return error("vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")}`);
|
|
1420
|
+
}
|
|
1421
|
+
function checkSkillsInstalled(home) {
|
|
1422
|
+
const skillsDir = join13(home, ".claude", "skills");
|
|
1423
|
+
if (!existsSync2(skillsDir)) {
|
|
1424
|
+
return warn("skills_installed", "Skills installed", `${skillsDir} not found`);
|
|
1425
|
+
}
|
|
1426
|
+
const found = findSkillMd(skillsDir);
|
|
1427
|
+
if (found.length > 0) {
|
|
1428
|
+
return pass("skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found`);
|
|
1429
|
+
}
|
|
1430
|
+
return warn("skills_installed", "Skills installed", "No SKILL.md files found in ~/.claude/skills/");
|
|
1431
|
+
}
|
|
1432
|
+
function findSkillMd(dir) {
|
|
1433
|
+
const results = [];
|
|
1434
|
+
let entries;
|
|
1435
|
+
try {
|
|
1436
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
1437
|
+
} catch {
|
|
1438
|
+
return results;
|
|
1439
|
+
}
|
|
1440
|
+
for (const entry of entries) {
|
|
1441
|
+
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
1442
|
+
results.push(join13(dir, entry.name));
|
|
1443
|
+
} else if (entry.isDirectory()) {
|
|
1444
|
+
results.push(...findSkillMd(join13(dir, entry.name)));
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return results;
|
|
1448
|
+
}
|
|
1449
|
+
async function runDoctor(input) {
|
|
1450
|
+
const checks = [];
|
|
1451
|
+
checks.push(checkNodeVersion());
|
|
1452
|
+
checks.push(checkCliOnPath(input.argv));
|
|
1453
|
+
checks.push(await checkConfigFile(input.home));
|
|
1454
|
+
const wikiPathCheck = await checkWikiPathSet(input);
|
|
1455
|
+
checks.push(wikiPathCheck);
|
|
1456
|
+
const resolved = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
|
|
1457
|
+
const resolvedPath = resolved.ok ? resolved.data.path : void 0;
|
|
1458
|
+
checks.push(checkWikiPathExists(resolvedPath));
|
|
1459
|
+
checks.push(checkVaultStructure(resolvedPath));
|
|
1460
|
+
checks.push(checkSkillsInstalled(input.home));
|
|
1461
|
+
const summary = {
|
|
1462
|
+
pass: checks.filter((c) => c.status === "pass").length,
|
|
1463
|
+
warn: checks.filter((c) => c.status === "warn").length,
|
|
1464
|
+
error: checks.filter((c) => c.status === "error").length
|
|
1465
|
+
};
|
|
1466
|
+
const exitCode = summary.error > 0 ? ExitCode.DOCTOR_HAS_ERRORS : summary.warn > 0 ? ExitCode.DOCTOR_HAS_WARNINGS : ExitCode.OK;
|
|
1467
|
+
const statusIcon = { pass: "\u2713", warn: "\u26A0", error: "\u2717" };
|
|
1468
|
+
const lines = checks.map((c) => {
|
|
1469
|
+
const icon = statusIcon[c.status];
|
|
1470
|
+
const padded = c.label.padEnd(24);
|
|
1471
|
+
return ` ${icon} ${padded} ${c.detail}`;
|
|
1472
|
+
});
|
|
1473
|
+
lines.push("");
|
|
1474
|
+
lines.push(`${summary.pass} pass \xB7 ${summary.warn} warn \xB7 ${summary.error} error`);
|
|
1475
|
+
const humanHint = lines.join("\n");
|
|
1476
|
+
return { exitCode, result: ok({ checks, summary, humanHint }) };
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1239
1479
|
// src/cli.ts
|
|
1480
|
+
var pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
1240
1481
|
var program = new Command();
|
|
1241
|
-
program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(
|
|
1482
|
+
program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(pkg.version);
|
|
1242
1483
|
program.option("--human", "render terminal-readable output instead of JSON");
|
|
1243
1484
|
function emit(r) {
|
|
1244
1485
|
if (program.opts().human) printHuman(r.result);
|
|
@@ -1344,6 +1585,17 @@ program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => p
|
|
|
1344
1585
|
logThreshold: opts.logThreshold
|
|
1345
1586
|
}));
|
|
1346
1587
|
});
|
|
1588
|
+
var configCmd = program.command("config").description("manage skillwiki configuration");
|
|
1589
|
+
configCmd.command("get <key>").description("print the value of a config key").action(async (key) => emit(await runConfigGet({ key, home: process.env.HOME ?? "" })));
|
|
1590
|
+
configCmd.command("set <key> <value>").description("set a config key value").action(async (key, value) => emit(await runConfigSet({ key, value, home: process.env.HOME ?? "" })));
|
|
1591
|
+
configCmd.command("list").description("list all config key=value pairs").action(async () => emit(await runConfigList({ home: process.env.HOME ?? "" })));
|
|
1592
|
+
configCmd.command("path").description("print the config file path").action(async () => emit(await runConfigPath({ home: process.env.HOME ?? "" })));
|
|
1593
|
+
program.command("doctor").description("diagnose skillwiki setup issues").action(async () => emit(await runDoctor({
|
|
1594
|
+
home: process.env.HOME ?? "",
|
|
1595
|
+
envValue: process.env.WIKI_PATH,
|
|
1596
|
+
envLang: process.env.WIKI_LANG,
|
|
1597
|
+
argv: process.argv
|
|
1598
|
+
})));
|
|
1347
1599
|
program.parseAsync(process.argv).catch((e) => {
|
|
1348
1600
|
process.stdout.write(JSON.stringify({ ok: false, error: "INTERNAL", detail: { message: String(e) } }) + "\n");
|
|
1349
1601
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"bin": {
|
|
6
|
-
|
|
5
|
+
"bin": {
|
|
6
|
+
"skillwiki": "dist/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"templates",
|
|
11
|
+
"skills",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
7
14
|
"scripts": {
|
|
8
15
|
"build": "tsup && rm -rf ./skills && cp -r ../skills ./skills",
|
|
9
16
|
"test": "vitest run",
|
|
@@ -23,5 +30,7 @@
|
|
|
23
30
|
"typescript": "^5.7.0",
|
|
24
31
|
"vitest": "^2.1.0"
|
|
25
32
|
},
|
|
26
|
-
"engines": {
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20"
|
|
35
|
+
}
|
|
27
36
|
}
|