skillwiki 0.2.1-beta.10 → 0.2.1-beta.12
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 +141 -17
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -162,9 +162,25 @@ var CompoundSchema = z.object({
|
|
|
162
162
|
promoted_to: wikilink.optional(),
|
|
163
163
|
cssclasses: z.array(z.string()).optional()
|
|
164
164
|
});
|
|
165
|
+
var MetaSchema = z.object({
|
|
166
|
+
title: z.string().min(1),
|
|
167
|
+
aliases: z.array(z.string()).optional(),
|
|
168
|
+
created: isoDate,
|
|
169
|
+
updated: isoDate,
|
|
170
|
+
type: z.literal("meta"),
|
|
171
|
+
tags: z.array(z.string()),
|
|
172
|
+
confidence: z.enum(["high", "medium", "low"]).optional(),
|
|
173
|
+
provenance: z.enum(["research", "project", "mixed"]).optional(),
|
|
174
|
+
provenance_projects: z.array(wikilink).min(2, "meta pages must reference \u22652 projects")
|
|
175
|
+
}).superRefine((v, ctx) => {
|
|
176
|
+
if (v.provenance && v.provenance !== "research" && (!v.provenance_projects || v.provenance_projects.length === 0)) {
|
|
177
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["provenance_projects"], message: "required when provenance != research" });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
165
180
|
function detectSchema(fm) {
|
|
166
181
|
const COMPOUND_TYPES = /* @__PURE__ */ new Set(["lesson", "pattern", "antipattern", "gotcha"]);
|
|
167
182
|
if (typeof fm.type === "string" && COMPOUND_TYPES.has(fm.type) && "project" in fm) return { schema: "compound" };
|
|
183
|
+
if (fm.type === "meta") return { schema: "meta" };
|
|
168
184
|
if ("type" in fm && "sources" in fm) return { schema: "typed-knowledge" };
|
|
169
185
|
if (typeof fm.sha256 === "string" && "ingested" in fm) return { schema: "raw" };
|
|
170
186
|
if ("kind" in fm && "status" in fm) return { schema: "work-item" };
|
|
@@ -303,7 +319,8 @@ var SCHEMAS = {
|
|
|
303
319
|
"typed-knowledge": TypedKnowledgeSchema,
|
|
304
320
|
"raw": RawSourceSchema,
|
|
305
321
|
"work-item": WorkItemSchema,
|
|
306
|
-
"compound": CompoundSchema
|
|
322
|
+
"compound": CompoundSchema,
|
|
323
|
+
"meta": MetaSchema
|
|
307
324
|
};
|
|
308
325
|
async function runValidate(input) {
|
|
309
326
|
let text;
|
|
@@ -342,7 +359,7 @@ import { dirname } from "path";
|
|
|
342
359
|
// src/utils/vault.ts
|
|
343
360
|
import { readFile as readFile3, readdir, stat } from "fs/promises";
|
|
344
361
|
import { join, relative, sep } from "path";
|
|
345
|
-
var TYPED_DIRS = ["entities", "concepts", "comparisons", "queries"];
|
|
362
|
+
var TYPED_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
346
363
|
async function scanVault(root) {
|
|
347
364
|
try {
|
|
348
365
|
await stat(join(root, "SCHEMA.md"));
|
|
@@ -1596,6 +1613,9 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
|
|
|
1596
1613
|
return { exitCode: ExitCode.OK, result: ok({ entries, threshold: input.threshold, rotated: true, rotated_to: rotatedName, humanHint: `rotated ${entries} entries to ${rotatedName}` }) };
|
|
1597
1614
|
}
|
|
1598
1615
|
|
|
1616
|
+
// src/commands/lint.ts
|
|
1617
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "fs/promises";
|
|
1618
|
+
|
|
1599
1619
|
// src/commands/topic-map-check.ts
|
|
1600
1620
|
var DEFAULT_THRESHOLD = 200;
|
|
1601
1621
|
async function runTopicMapCheck(input) {
|
|
@@ -1732,6 +1752,8 @@ var WARNING_ORDER = ["index_incomplete", "index_link_format", "stale_page", "pag
|
|
|
1732
1752
|
var INFO_ORDER = ["bridges", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation"];
|
|
1733
1753
|
async function runLint(input) {
|
|
1734
1754
|
const buckets = {};
|
|
1755
|
+
const fixed = [];
|
|
1756
|
+
const unresolved = [];
|
|
1735
1757
|
const links = await runLinks({ vault: input.vault });
|
|
1736
1758
|
if (links.result.ok && links.result.data.broken.length > 0) buckets.broken_wikilinks = links.result.data.broken;
|
|
1737
1759
|
if (!links.result.ok && links.result.error === "INVALID_FRONTMATTER") {
|
|
@@ -1822,6 +1844,105 @@ async function runLint(input) {
|
|
|
1822
1844
|
if (noOverview.length > 0) buckets.missing_overview = noOverview;
|
|
1823
1845
|
if (fmWikilinkFlags.length > 0) buckets.frontmatter_wikilink = fmWikilinkFlags;
|
|
1824
1846
|
if (wikilinkCitationFlags.length > 0) buckets.wikilink_citation = wikilinkCitationFlags;
|
|
1847
|
+
if (input.fix && legacyPages.length > 0) {
|
|
1848
|
+
const FENCE_RE2 = /```[\s\S]*?```/g;
|
|
1849
|
+
const INLINE_MARKER = /\^\[raw\/[^\]]+\]/g;
|
|
1850
|
+
for (const relPath of legacyPages) {
|
|
1851
|
+
try {
|
|
1852
|
+
const absPath = `${input.vault}/${relPath}`;
|
|
1853
|
+
const raw = await readFile12(absPath, "utf8");
|
|
1854
|
+
const split = splitFrontmatter(raw);
|
|
1855
|
+
if (!split.ok) {
|
|
1856
|
+
unresolved.push(relPath);
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
const body = split.data.body;
|
|
1860
|
+
const rawFm = split.data.rawFrontmatter;
|
|
1861
|
+
const stripped = body.replace(FENCE_RE2, "");
|
|
1862
|
+
const lines = stripped.split("\n");
|
|
1863
|
+
const inlineMarkers = [];
|
|
1864
|
+
let inSources = false;
|
|
1865
|
+
for (const line of lines) {
|
|
1866
|
+
if (/^## Sources\b/.test(line.trim())) {
|
|
1867
|
+
inSources = true;
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
if (inSources) continue;
|
|
1871
|
+
for (const m of line.matchAll(INLINE_MARKER)) {
|
|
1872
|
+
inlineMarkers.push(m[0]);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
if (inlineMarkers.length === 0) {
|
|
1876
|
+
unresolved.push(relPath);
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1879
|
+
const bodyLines = body.split("\n");
|
|
1880
|
+
let inSrc = false;
|
|
1881
|
+
const newBodyLines = [];
|
|
1882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1883
|
+
for (const line of bodyLines) {
|
|
1884
|
+
if (/^## Sources\b/.test(line.trim())) {
|
|
1885
|
+
inSrc = true;
|
|
1886
|
+
newBodyLines.push(line);
|
|
1887
|
+
continue;
|
|
1888
|
+
}
|
|
1889
|
+
if (inSrc) {
|
|
1890
|
+
newBodyLines.push(line);
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
const lineWithoutMarkers = line.replace(INLINE_MARKER, "").trim();
|
|
1894
|
+
if (lineWithoutMarkers.length === 0 && INLINE_MARKER.test(line)) {
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
let cleaned = line;
|
|
1898
|
+
for (const marker of inlineMarkers) {
|
|
1899
|
+
if (seen.has(marker)) continue;
|
|
1900
|
+
const escapedMarker = marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1901
|
+
const trailingRe = new RegExp(`([.!?]\\s*)${escapedMarker}`);
|
|
1902
|
+
if (trailingRe.test(cleaned)) {
|
|
1903
|
+
cleaned = cleaned.replace(trailingRe, "$1");
|
|
1904
|
+
seen.add(marker);
|
|
1905
|
+
}
|
|
1906
|
+
const midRe = new RegExp(`${escapedMarker}\\s*`);
|
|
1907
|
+
if (!seen.has(marker) && midRe.test(cleaned)) {
|
|
1908
|
+
cleaned = cleaned.replace(midRe, "");
|
|
1909
|
+
seen.add(marker);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
newBodyLines.push(cleaned);
|
|
1913
|
+
}
|
|
1914
|
+
let newBody = newBodyLines.join("\n");
|
|
1915
|
+
const dedupedMarkers = [...new Set(inlineMarkers)];
|
|
1916
|
+
if (inSrc) {
|
|
1917
|
+
const existingSources = new Set(
|
|
1918
|
+
body.split("\n").filter((l) => /^- \^\[raw\//.test(l.trim())).map((l) => l.trim().replace(/^- /, ""))
|
|
1919
|
+
);
|
|
1920
|
+
const newMarkers = dedupedMarkers.filter((m) => !existingSources.has(m));
|
|
1921
|
+
const sourceLines = newMarkers.map((m) => `- ${m}`);
|
|
1922
|
+
if (sourceLines.length > 0) {
|
|
1923
|
+
newBody = newBody.trimEnd() + "\n" + sourceLines.join("\n") + "\n";
|
|
1924
|
+
}
|
|
1925
|
+
} else {
|
|
1926
|
+
const sourceLines = dedupedMarkers.map((m) => `- ${m}`);
|
|
1927
|
+
newBody = newBody.trimEnd() + "\n\n## Sources\n\n" + sourceLines.join("\n") + "\n";
|
|
1928
|
+
}
|
|
1929
|
+
const newContent = `---
|
|
1930
|
+
${rawFm}
|
|
1931
|
+
---
|
|
1932
|
+
${newBody}`;
|
|
1933
|
+
await writeFile6(absPath, newContent, "utf8");
|
|
1934
|
+
fixed.push(relPath);
|
|
1935
|
+
} catch {
|
|
1936
|
+
unresolved.push(relPath);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
if (fixed.length > 0) {
|
|
1940
|
+
const fixedSet = new Set(fixed);
|
|
1941
|
+
const remaining = legacyPages.filter((p) => !fixedSet.has(p));
|
|
1942
|
+
if (remaining.length > 0) buckets.legacy_citation_style = remaining;
|
|
1943
|
+
else delete buckets.legacy_citation_style;
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1825
1946
|
}
|
|
1826
1947
|
const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
1827
1948
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
@@ -1849,13 +1970,15 @@ async function runLint(input) {
|
|
|
1849
1970
|
vault: { path: input.vault, source: input.source ?? "resolved" },
|
|
1850
1971
|
summary,
|
|
1851
1972
|
by_severity: { error: errorOut, warning: warningOut, info: infoOut },
|
|
1973
|
+
fixed,
|
|
1974
|
+
unresolved,
|
|
1852
1975
|
humanHint: hintLines.join("\n")
|
|
1853
1976
|
})
|
|
1854
1977
|
};
|
|
1855
1978
|
}
|
|
1856
1979
|
|
|
1857
1980
|
// src/commands/config.ts
|
|
1858
|
-
import { readFile as
|
|
1981
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
1859
1982
|
import { existsSync } from "fs";
|
|
1860
1983
|
import { join as join14 } from "path";
|
|
1861
1984
|
function validateKey(key) {
|
|
@@ -1880,7 +2003,7 @@ async function runConfigSet(input) {
|
|
|
1880
2003
|
try {
|
|
1881
2004
|
let originalContent;
|
|
1882
2005
|
try {
|
|
1883
|
-
originalContent = await
|
|
2006
|
+
originalContent = await readFile13(filePath, "utf8");
|
|
1884
2007
|
} catch {
|
|
1885
2008
|
}
|
|
1886
2009
|
const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
|
|
@@ -2193,7 +2316,7 @@ async function runDoctor(input) {
|
|
|
2193
2316
|
}
|
|
2194
2317
|
|
|
2195
2318
|
// src/commands/archive.ts
|
|
2196
|
-
import { rename as rename3, mkdir as mkdir5, readFile as
|
|
2319
|
+
import { rename as rename3, mkdir as mkdir5, readFile as readFile14, writeFile as writeFile7 } from "fs/promises";
|
|
2197
2320
|
import { join as join18, dirname as dirname7 } from "path";
|
|
2198
2321
|
async function runArchive(input) {
|
|
2199
2322
|
const scan = await scanVault(input.vault);
|
|
@@ -2216,12 +2339,12 @@ async function runArchive(input) {
|
|
|
2216
2339
|
if (!isRaw) {
|
|
2217
2340
|
const indexPath = join18(input.vault, "index.md");
|
|
2218
2341
|
try {
|
|
2219
|
-
const idx = await
|
|
2342
|
+
const idx = await readFile14(indexPath, "utf8");
|
|
2220
2343
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
2221
2344
|
const originalLines = idx.split("\n");
|
|
2222
2345
|
const filtered = originalLines.filter((l) => !l.includes(`[[${slug}]]`));
|
|
2223
2346
|
if (filtered.length !== originalLines.length) {
|
|
2224
|
-
await
|
|
2347
|
+
await writeFile7(indexPath, filtered.join("\n"), "utf8");
|
|
2225
2348
|
indexUpdated = true;
|
|
2226
2349
|
}
|
|
2227
2350
|
} catch (e) {
|
|
@@ -2234,7 +2357,7 @@ async function runArchive(input) {
|
|
|
2234
2357
|
|
|
2235
2358
|
// src/commands/drift.ts
|
|
2236
2359
|
import { createHash as createHash2 } from "crypto";
|
|
2237
|
-
import { writeFile as
|
|
2360
|
+
import { writeFile as writeFile8 } from "fs/promises";
|
|
2238
2361
|
|
|
2239
2362
|
// src/utils/fetch.ts
|
|
2240
2363
|
async function controlledFetch(url, opts) {
|
|
@@ -2320,7 +2443,7 @@ async function runDrift(input) {
|
|
|
2320
2443
|
${newFm}
|
|
2321
2444
|
---
|
|
2322
2445
|
${body}`;
|
|
2323
|
-
await
|
|
2446
|
+
await writeFile8(raw.absPath, newText, "utf8");
|
|
2324
2447
|
results.push({
|
|
2325
2448
|
raw_path: raw.relPath,
|
|
2326
2449
|
source_url: sourceUrl,
|
|
@@ -2355,7 +2478,7 @@ ${body}`;
|
|
|
2355
2478
|
}
|
|
2356
2479
|
|
|
2357
2480
|
// src/commands/migrate-citations.ts
|
|
2358
|
-
import { writeFile as
|
|
2481
|
+
import { writeFile as writeFile9 } from "fs/promises";
|
|
2359
2482
|
var MARKER_RE2 = /\^\[(raw\/[^\]]+)\]/g;
|
|
2360
2483
|
function moveMarkersToParagraphEnd(body) {
|
|
2361
2484
|
const lines = body.split("\n");
|
|
@@ -2478,7 +2601,7 @@ ${migratedBody}${newFooter}`;
|
|
|
2478
2601
|
continue;
|
|
2479
2602
|
}
|
|
2480
2603
|
if (!input.dryRun) {
|
|
2481
|
-
await
|
|
2604
|
+
await writeFile9(page.absPath, newText, "utf8");
|
|
2482
2605
|
}
|
|
2483
2606
|
migrated.push(page.relPath);
|
|
2484
2607
|
}
|
|
@@ -2500,7 +2623,7 @@ ${migratedBody}${newFooter}`;
|
|
|
2500
2623
|
}
|
|
2501
2624
|
|
|
2502
2625
|
// src/commands/frontmatter-fix.ts
|
|
2503
|
-
import { writeFile as
|
|
2626
|
+
import { writeFile as writeFile10 } from "fs/promises";
|
|
2504
2627
|
function isoToday() {
|
|
2505
2628
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2506
2629
|
}
|
|
@@ -2542,7 +2665,7 @@ ${newBody}`;
|
|
|
2542
2665
|
continue;
|
|
2543
2666
|
}
|
|
2544
2667
|
if (!input.dryRun) {
|
|
2545
|
-
await
|
|
2668
|
+
await writeFile10(page.absPath, newText, "utf8");
|
|
2546
2669
|
}
|
|
2547
2670
|
fixed.push(page.relPath);
|
|
2548
2671
|
}
|
|
@@ -2626,7 +2749,7 @@ async function runUpdate(input) {
|
|
|
2626
2749
|
}
|
|
2627
2750
|
|
|
2628
2751
|
// src/commands/transcripts.ts
|
|
2629
|
-
import { readdir as readdir4, stat as stat6, readFile as
|
|
2752
|
+
import { readdir as readdir4, stat as stat6, readFile as readFile15 } from "fs/promises";
|
|
2630
2753
|
import { join as join19 } from "path";
|
|
2631
2754
|
async function runTranscripts(input) {
|
|
2632
2755
|
const dir = join19(input.vault, "raw", "transcripts");
|
|
@@ -2640,7 +2763,7 @@ async function runTranscripts(input) {
|
|
|
2640
2763
|
for (const entry of entries) {
|
|
2641
2764
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2642
2765
|
const filePath = join19(dir, entry.name);
|
|
2643
|
-
const content = await
|
|
2766
|
+
const content = await readFile15(filePath, "utf8");
|
|
2644
2767
|
const fm = extractFrontmatter(content);
|
|
2645
2768
|
if (!fm.ok) continue;
|
|
2646
2769
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
@@ -2763,7 +2886,7 @@ program.command("log-rotate [vault]").option("--threshold <n>", "entry count thr
|
|
|
2763
2886
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2764
2887
|
else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: !!opts.apply }));
|
|
2765
2888
|
});
|
|
2766
|
-
program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2889
|
+
program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--fix", "auto-fix legacy_citation_style violations").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
2767
2890
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2768
2891
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2769
2892
|
else emit(await runLint({
|
|
@@ -2771,7 +2894,8 @@ program.command("lint [vault]").option("--days <n>", "stale threshold", (s) => p
|
|
|
2771
2894
|
source: vault ? "flag" : void 0,
|
|
2772
2895
|
days: opts.days,
|
|
2773
2896
|
lines: opts.lines,
|
|
2774
|
-
logThreshold: opts.logThreshold
|
|
2897
|
+
logThreshold: opts.logThreshold,
|
|
2898
|
+
fix: opts.fix ?? false
|
|
2775
2899
|
}));
|
|
2776
2900
|
});
|
|
2777
2901
|
var configCmd = program.command("config").description("manage skillwiki configuration");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.2.1-beta.
|
|
3
|
+
"version": "0.2.1-beta.12",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 15 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|