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