skillwiki 0.10.25 → 0.10.26
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.
|
@@ -787,20 +787,75 @@ function extractBodyWikilinks(body) {
|
|
|
787
787
|
return out;
|
|
788
788
|
}
|
|
789
789
|
|
|
790
|
+
// src/utils/wikilink-resolver.ts
|
|
791
|
+
var EXCLUDED_PREFIXES = [
|
|
792
|
+
".git/",
|
|
793
|
+
".skillwiki/",
|
|
794
|
+
"_archive/",
|
|
795
|
+
"drafts/",
|
|
796
|
+
"tmp/"
|
|
797
|
+
];
|
|
798
|
+
var RESOLVABLE_PROJECT_ARTIFACT_RE = /^projects\/[^/]+\/(?:knowledge\.md|README\.md|(?:compound|requirements|architecture|history|work)\/(?:[^/]+\/)*[^/]+\.md)$/i;
|
|
799
|
+
function normalizeTarget(target) {
|
|
800
|
+
return target.trim().replace(/\\/g, "/").replace(/^\.\//, "").replace(/\.md$/i, "").replace(/^\/+/, "").replace(/\/+/g, "/").toLowerCase();
|
|
801
|
+
}
|
|
802
|
+
function isResolvableProjectArtifact(relPath) {
|
|
803
|
+
return RESOLVABLE_PROJECT_ARTIFACT_RE.test(relPath);
|
|
804
|
+
}
|
|
805
|
+
function isResolvablePath(relPath) {
|
|
806
|
+
const normalized = relPath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
807
|
+
if (EXCLUDED_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return false;
|
|
808
|
+
return !normalized.startsWith("projects/") || isResolvableProjectArtifact(normalized);
|
|
809
|
+
}
|
|
810
|
+
function buildWikilinkResolver(pages) {
|
|
811
|
+
const exact = /* @__PURE__ */ new Map();
|
|
812
|
+
const byBasename = /* @__PURE__ */ new Map();
|
|
813
|
+
for (const page of pages) {
|
|
814
|
+
if (!isResolvablePath(page.relPath)) continue;
|
|
815
|
+
const normalizedPath = normalizeTarget(page.relPath);
|
|
816
|
+
exact.set(normalizedPath, page.relPath);
|
|
817
|
+
const basename3 = normalizedPath.split("/").pop();
|
|
818
|
+
const candidates = byBasename.get(basename3) ?? [];
|
|
819
|
+
candidates.push(page.relPath);
|
|
820
|
+
byBasename.set(basename3, candidates);
|
|
821
|
+
}
|
|
822
|
+
for (const candidates of byBasename.values()) candidates.sort();
|
|
823
|
+
return {
|
|
824
|
+
resolve(target) {
|
|
825
|
+
const normalized = normalizeTarget(target);
|
|
826
|
+
const exactPath = exact.get(normalized);
|
|
827
|
+
if (exactPath) return { target, path: exactPath, ambiguous: false };
|
|
828
|
+
if (normalized.startsWith("projects/")) {
|
|
829
|
+
return {
|
|
830
|
+
target,
|
|
831
|
+
ambiguous: false,
|
|
832
|
+
reason: isResolvablePath(`${normalized}.md`) ? "missing" : "unsupported"
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
const candidates = byBasename.get(normalized) ?? [];
|
|
836
|
+
if (candidates.length === 1) {
|
|
837
|
+
return { target, path: candidates[0], ambiguous: false };
|
|
838
|
+
}
|
|
839
|
+
if (candidates.length > 1) {
|
|
840
|
+
return { target, ambiguous: true, reason: "ambiguous" };
|
|
841
|
+
}
|
|
842
|
+
return { target, ambiguous: false, reason: "missing" };
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
790
847
|
// src/utils/community.ts
|
|
791
|
-
async function buildWikilinkAdjacency(typedKnowledge, pageTextCache) {
|
|
848
|
+
async function buildWikilinkAdjacency(typedKnowledge, pageTextCache, allPages = typedKnowledge) {
|
|
792
849
|
const adjacency = {};
|
|
793
|
-
const
|
|
794
|
-
for (const p of typedKnowledge) {
|
|
795
|
-
const slug = p.relPath.replace(/\.md$/, "").split("/").pop();
|
|
796
|
-
slugToPath[slug] = p.relPath;
|
|
797
|
-
}
|
|
850
|
+
const resolver = buildWikilinkResolver(allPages);
|
|
798
851
|
await mapWithConcurrency(typedKnowledge, vaultIoConcurrency(), async (p) => {
|
|
799
852
|
const text = await readPageCached(p, pageTextCache);
|
|
800
853
|
const split = splitFrontmatter(text);
|
|
801
854
|
const body = split.ok ? split.data.body : text;
|
|
802
855
|
const links = extractBodyWikilinks(body);
|
|
803
|
-
|
|
856
|
+
const targets = links.map((target) => resolver.resolve(target).path).filter((x) => Boolean(x));
|
|
857
|
+
adjacency[p.relPath] = targets;
|
|
858
|
+
for (const target of targets) adjacency[target] ??= [];
|
|
804
859
|
});
|
|
805
860
|
return adjacency;
|
|
806
861
|
}
|
|
@@ -917,7 +972,7 @@ function findSparseCommunities(adj, opts = {}) {
|
|
|
917
972
|
async function runGraphBuild(input) {
|
|
918
973
|
const scan = await scanVault(input.vault);
|
|
919
974
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
920
|
-
const adjacency = await buildWikilinkAdjacency(scan.data.typedKnowledge);
|
|
975
|
+
const adjacency = await buildWikilinkAdjacency(scan.data.typedKnowledge, void 0, scan.data.allMarkdown);
|
|
921
976
|
const adamicAdar = computeAdamicAdar(adjacency);
|
|
922
977
|
const edge_count = Object.values(adjacency).reduce((acc, arr) => acc + arr.length, 0);
|
|
923
978
|
try {
|
|
@@ -926,9 +981,10 @@ async function runGraphBuild(input) {
|
|
|
926
981
|
} catch (e) {
|
|
927
982
|
return { exitCode: ExitCode.WRITE_FAILED, result: err("WRITE_FAILED", { message: String(e) }) };
|
|
928
983
|
}
|
|
984
|
+
const node_count = Object.keys(adjacency).length;
|
|
929
985
|
return {
|
|
930
986
|
exitCode: ExitCode.OK,
|
|
931
|
-
result: ok({ out_path: input.out, node_count
|
|
987
|
+
result: ok({ out_path: input.out, node_count, edge_count, humanHint: `nodes: ${node_count}, edges: ${edge_count}
|
|
932
988
|
written: ${input.out}` })
|
|
933
989
|
};
|
|
934
990
|
}
|
|
@@ -1282,23 +1338,12 @@ async function validateCompoundReferences(vault, existingScan, pageTextCache) {
|
|
|
1282
1338
|
return ok(findings);
|
|
1283
1339
|
}
|
|
1284
1340
|
|
|
1285
|
-
// src/utils/slug.ts
|
|
1286
|
-
function buildSlugMap(pages) {
|
|
1287
|
-
const map = /* @__PURE__ */ new Map();
|
|
1288
|
-
for (const p of pages) {
|
|
1289
|
-
const slug = p.relPath.replace(/\.md$/, "").split("/").pop();
|
|
1290
|
-
map.set(slug.toLowerCase(), slug);
|
|
1291
|
-
}
|
|
1292
|
-
return map;
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
1341
|
// src/commands/links.ts
|
|
1296
1342
|
async function runLinks(input) {
|
|
1297
1343
|
const scanResult = input.scan ? ok(input.scan) : await scanVault(input.vault);
|
|
1298
1344
|
if (!scanResult.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scanResult };
|
|
1299
1345
|
const scan = scanResult.data;
|
|
1300
|
-
const
|
|
1301
|
-
const slugs = buildSlugMap(allPages);
|
|
1346
|
+
const resolver = buildWikilinkResolver(scan.allMarkdown);
|
|
1302
1347
|
const perPage = await mapWithConcurrency(scan.typedKnowledge, vaultIoConcurrency(), async (p) => {
|
|
1303
1348
|
const text = await readPageCached(p, input.pageTextCache);
|
|
1304
1349
|
const split = splitFrontmatter(text);
|
|
@@ -1306,8 +1351,8 @@ async function runLinks(input) {
|
|
|
1306
1351
|
const lines = body.split("\n");
|
|
1307
1352
|
const broken2 = [];
|
|
1308
1353
|
for (const slug of extractBodyWikilinks(body)) {
|
|
1309
|
-
const
|
|
1310
|
-
if (!
|
|
1354
|
+
const resolution = resolver.resolve(slug);
|
|
1355
|
+
if (!resolution.path) {
|
|
1311
1356
|
const line = lines.findIndex((l) => l.includes(`[[${slug}`));
|
|
1312
1357
|
broken2.push({ page: p.relPath, slug, line: line >= 0 ? line + 1 : 0 });
|
|
1313
1358
|
}
|
|
@@ -1738,8 +1783,7 @@ Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
|
1738
1783
|
|
|
1739
1784
|
`;
|
|
1740
1785
|
for (const item of items) {
|
|
1741
|
-
|
|
1742
|
-
body += `- [[${pageRef}]] \u2014 ${item.title}
|
|
1786
|
+
body += `- [[${item.page.replace(/\.md$/, "")}]] \u2014 ${item.title}
|
|
1743
1787
|
`;
|
|
1744
1788
|
}
|
|
1745
1789
|
body += "\n";
|
|
@@ -3077,7 +3121,11 @@ import { join as join16, relative as relative2, sep as sep2 } from "path";
|
|
|
3077
3121
|
async function runSparseCommunity(input) {
|
|
3078
3122
|
const scan = input.scan ? ok(input.scan) : await scanVault(input.vault);
|
|
3079
3123
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
3080
|
-
const adjacency = await buildWikilinkAdjacency(
|
|
3124
|
+
const adjacency = await buildWikilinkAdjacency(
|
|
3125
|
+
scan.data.typedKnowledge,
|
|
3126
|
+
input.pageTextCache,
|
|
3127
|
+
scan.data.allMarkdown
|
|
3128
|
+
);
|
|
3081
3129
|
const communities = findSparseCommunities(adjacency, {
|
|
3082
3130
|
minSize: input.minSize,
|
|
3083
3131
|
maxCohesion: input.maxCohesion
|
|
@@ -4092,8 +4140,7 @@ async function runLint(input) {
|
|
|
4092
4140
|
if (compoundRefs.ok && compoundRefs.data.length > 0) buckets.compound_refs = compoundRefs.data;
|
|
4093
4141
|
const pathCheck = await runPathTooLong({ vault: lintVault, scan });
|
|
4094
4142
|
if (pathCheck.result.ok && pathCheck.result.data.violations.length > 0) buckets.path_too_long = pathCheck.result.data.violations;
|
|
4095
|
-
const
|
|
4096
|
-
const slugs = buildSlugMap(allPages);
|
|
4143
|
+
const wikilinkResolver = buildWikilinkResolver(scan.allMarkdown);
|
|
4097
4144
|
{
|
|
4098
4145
|
const allPageResults = await mapWithConcurrency(scan.allMarkdown, vaultIoConcurrency(), async (page) => {
|
|
4099
4146
|
const sensitiveFlags2 = [];
|
|
@@ -4196,8 +4243,7 @@ async function runLint(input) {
|
|
|
4196
4243
|
const fmLinks = rawFm.match(/\[\[([^\[\]|]+)(?:\|[^\[\]]*)?\]\]/g) ?? [];
|
|
4197
4244
|
for (const link of fmLinks) {
|
|
4198
4245
|
const target = link.replace(/^\[\[/, "").replace(/(?:\|[^\[\]]*)?\]\]$/, "").trim();
|
|
4199
|
-
|
|
4200
|
-
if (!slugs.has(tail.toLowerCase())) {
|
|
4246
|
+
if (!wikilinkResolver.resolve(target).path) {
|
|
4201
4247
|
result.fmWikilinkFlags.push(`${page.relPath}: [[${target}]] does not resolve`);
|
|
4202
4248
|
}
|
|
4203
4249
|
}
|
|
@@ -7211,8 +7257,9 @@ async function vaultMetrics(resolvedPath) {
|
|
|
7211
7257
|
const scan = await scanVault(scanRoot);
|
|
7212
7258
|
if (!scan.ok) return noVault();
|
|
7213
7259
|
const tk = scan.data.typedKnowledge;
|
|
7260
|
+
const typedCount = tk.length;
|
|
7214
7261
|
const perType = METRIC_TYPES.map((d) => `${d} ${tk.filter((p) => p.relPath.startsWith(d + "/")).length}`).join(", ");
|
|
7215
|
-
const adj = await buildWikilinkAdjacency(tk);
|
|
7262
|
+
const adj = await buildWikilinkAdjacency(tk, void 0, scan.data.allMarkdown);
|
|
7216
7263
|
const g = toUndirectedWeighted(adj);
|
|
7217
7264
|
const nodes = [...g.keys()];
|
|
7218
7265
|
const total = nodes.length;
|
|
@@ -7239,7 +7286,7 @@ async function vaultMetrics(resolvedPath) {
|
|
|
7239
7286
|
} catch {
|
|
7240
7287
|
}
|
|
7241
7288
|
return [
|
|
7242
|
-
check("info", "vault_metric_pages", "Vault pages by type", `${total}
|
|
7289
|
+
check("info", "vault_metric_pages", "Vault pages by type", `${total} graph node(s) (${typedCount} typed; ${perType})`),
|
|
7243
7290
|
check("info", "vault_metric_orphans", "Vault orphan rate", `${orphanRate}% (${orphanCount}/${total} degree-0)`),
|
|
7244
7291
|
check("info", "vault_metric_bridges", "Vault bridge count", `${bridges} page(s) link >= 3 communities`),
|
|
7245
7292
|
check("info", "vault_metric_cohesion", "Mean community cohesion", `${meanCohesion} across ${cohesions.length} communities (size >= 2)`),
|
|
@@ -8957,6 +9004,8 @@ var W_WIKILINK = 3;
|
|
|
8957
9004
|
var W_ADAMIC_ADAR = 1.5;
|
|
8958
9005
|
var W_TYPE_AFFINITY = 1;
|
|
8959
9006
|
var NON_SEED_FACTOR = 0.4;
|
|
9007
|
+
var HISTORICAL_CYCLE_FACTOR = 0.55;
|
|
9008
|
+
var HISTORICAL_CYCLE_RE = /(?:^|\/)(?:\d{4}-\d{2}-\d{2}-)?.*\b(?:daily|deep|maintenance|research|office[- ]hours|sleep)\b.*\b(?:cycle|run|review|research)\b/i;
|
|
8960
9009
|
var CONCEPT_INDICATORS = /* @__PURE__ */ new Set([
|
|
8961
9010
|
"what",
|
|
8962
9011
|
"how",
|
|
@@ -8996,11 +9045,26 @@ async function runQuery(input) {
|
|
|
8996
9045
|
const split = splitFrontmatter(text);
|
|
8997
9046
|
const body = split.ok ? split.data.body : text;
|
|
8998
9047
|
const keywordScore = computeKeywordScore(queryTerms, title, tags, body);
|
|
8999
|
-
pages.push({
|
|
9048
|
+
pages.push({
|
|
9049
|
+
relPath: p.relPath,
|
|
9050
|
+
title,
|
|
9051
|
+
type,
|
|
9052
|
+
tags,
|
|
9053
|
+
sources,
|
|
9054
|
+
keywordScore,
|
|
9055
|
+
historicalCycle: isHistoricalCyclePage(p.relPath, title)
|
|
9056
|
+
});
|
|
9000
9057
|
}
|
|
9001
|
-
const seedPaths = new Set(
|
|
9002
|
-
|
|
9003
|
-
|
|
9058
|
+
const seedPaths = /* @__PURE__ */ new Set();
|
|
9059
|
+
let historicalCyclePageCount = 0;
|
|
9060
|
+
let hasDirectOperationalSeed = false;
|
|
9061
|
+
for (const page of pages) {
|
|
9062
|
+
if (page.historicalCycle) historicalCyclePageCount += 1;
|
|
9063
|
+
if (page.keywordScore <= 0) continue;
|
|
9064
|
+
seedPaths.add(page.relPath);
|
|
9065
|
+
if (!page.historicalCycle) hasDirectOperationalSeed = true;
|
|
9066
|
+
}
|
|
9067
|
+
const suppressRepetitiveHistoricalCycles = historicalCyclePageCount >= 3 && hasDirectOperationalSeed;
|
|
9004
9068
|
const results = pages.map((page) => {
|
|
9005
9069
|
const sourceOverlap = scoreSourceOverlap(page, pages, seedPaths);
|
|
9006
9070
|
const wikilink = scoreWikilink(page.relPath, seedPaths, graph);
|
|
@@ -9009,9 +9073,10 @@ async function runQuery(input) {
|
|
|
9009
9073
|
const isSeed = page.keywordScore > 0;
|
|
9010
9074
|
const structuralBoost = sourceOverlap * W_SOURCE_OVERLAP + wikilink * W_WIKILINK + aa * W_ADAMIC_ADAR;
|
|
9011
9075
|
const composite = isSeed ? page.keywordScore * W_KEYWORD + structuralBoost + typeAffinity * W_TYPE_AFFINITY : structuralBoost * NON_SEED_FACTOR + typeAffinity * W_TYPE_AFFINITY;
|
|
9076
|
+
const guardedComposite = suppressRepetitiveHistoricalCycles && page.historicalCycle ? composite * HISTORICAL_CYCLE_FACTOR : composite;
|
|
9012
9077
|
return {
|
|
9013
9078
|
path: page.relPath,
|
|
9014
|
-
score: Math.round(
|
|
9079
|
+
score: Math.round(guardedComposite * 1e3) / 1e3,
|
|
9015
9080
|
title: page.title,
|
|
9016
9081
|
type: page.type
|
|
9017
9082
|
};
|
|
@@ -9028,9 +9093,18 @@ async function runQuery(input) {
|
|
|
9028
9093
|
}
|
|
9029
9094
|
const humanHint = results.length === 0 ? pendingSources && pendingSources.length > 0 ? `no matching typed pages found
|
|
9030
9095
|
${pendingSources.length} matching pending source(s)` : "no matching pages found" : results.map((r) => `${r.path} (score: ${r.score})`).join("\n");
|
|
9096
|
+
const rankingGuardrails = suppressRepetitiveHistoricalCycles ? {
|
|
9097
|
+
repetitive_historical_cycles_suppressed: true,
|
|
9098
|
+
historical_cycle_page_count: historicalCyclePageCount
|
|
9099
|
+
} : void 0;
|
|
9031
9100
|
return {
|
|
9032
9101
|
exitCode: ExitCode.OK,
|
|
9033
|
-
result: ok({
|
|
9102
|
+
result: ok({
|
|
9103
|
+
results,
|
|
9104
|
+
...pendingSources ? { pending_sources: pendingSources } : {},
|
|
9105
|
+
...rankingGuardrails ? { ranking_guardrails: rankingGuardrails } : {},
|
|
9106
|
+
humanHint
|
|
9107
|
+
})
|
|
9034
9108
|
};
|
|
9035
9109
|
}
|
|
9036
9110
|
function scoreSourceOverlap(page, allPages, seedPaths) {
|
|
@@ -9069,6 +9143,9 @@ function scoreTypeAffinity(pageType, queryTerms) {
|
|
|
9069
9143
|
if (!hasConceptIntent && pageType === "entity") return 0.5;
|
|
9070
9144
|
return 0;
|
|
9071
9145
|
}
|
|
9146
|
+
function isHistoricalCyclePage(relPath, title) {
|
|
9147
|
+
return HISTORICAL_CYCLE_RE.test(`${relPath} ${title}`);
|
|
9148
|
+
}
|
|
9072
9149
|
function tokenize(text) {
|
|
9073
9150
|
return text.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
9074
9151
|
}
|
package/dist/cli.js
CHANGED
|
@@ -81,7 +81,7 @@ import {
|
|
|
81
81
|
snapshotterHealthChecks,
|
|
82
82
|
taxonomyCommentForPage,
|
|
83
83
|
upsertIndexEntry
|
|
84
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-VXMHHXVP.js";
|
|
85
85
|
import {
|
|
86
86
|
normalizeDistTag,
|
|
87
87
|
readCache,
|
|
@@ -4873,14 +4873,14 @@ function dateFromPath(path) {
|
|
|
4873
4873
|
}
|
|
4874
4874
|
|
|
4875
4875
|
// src/commands/ingest.ts
|
|
4876
|
-
import { readFile as readFile13, open, unlink as unlink4, mkdir as
|
|
4876
|
+
import { readFile as readFile13, open, unlink as unlink4, mkdir as mkdir10 } from "fs/promises";
|
|
4877
4877
|
import { join as join26 } from "path";
|
|
4878
4878
|
import { createHash as createHash4 } from "crypto";
|
|
4879
4879
|
|
|
4880
4880
|
// src/commands/page-publish.ts
|
|
4881
4881
|
import { readFileSync as readFileSync12, realpathSync } from "fs";
|
|
4882
|
-
import { readFile as readFile12 } from "fs/promises";
|
|
4883
|
-
import { join as join25, resolve as resolve3 } from "path";
|
|
4882
|
+
import { mkdir as mkdir9, readFile as readFile12 } from "fs/promises";
|
|
4883
|
+
import { dirname as dirname6, join as join25, resolve as resolve3 } from "path";
|
|
4884
4884
|
|
|
4885
4885
|
// src/utils/publication-approval.ts
|
|
4886
4886
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -5140,6 +5140,8 @@ function errorExitCode(error) {
|
|
|
5140
5140
|
return ExitCode.APPROVAL_MISMATCH;
|
|
5141
5141
|
case "TARGET_DRIFT":
|
|
5142
5142
|
return ExitCode.TARGET_DRIFT;
|
|
5143
|
+
case "PROJECT_NOT_FOUND":
|
|
5144
|
+
return ExitCode.PROJECT_NOT_FOUND;
|
|
5143
5145
|
case "USAGE":
|
|
5144
5146
|
return ExitCode.USAGE;
|
|
5145
5147
|
case "EVENT_IDENTITY_COLLISION":
|
|
@@ -5149,6 +5151,48 @@ function errorExitCode(error) {
|
|
|
5149
5151
|
return ExitCode.INVALID_FRONTMATTER;
|
|
5150
5152
|
}
|
|
5151
5153
|
}
|
|
5154
|
+
function projectSlugForTarget(target) {
|
|
5155
|
+
const match = target.match(/^projects\/([^/]+)\/(?:requirements|work|architecture|history|compound)\//);
|
|
5156
|
+
return match?.[1];
|
|
5157
|
+
}
|
|
5158
|
+
function projectSlugsForPublication(target, content) {
|
|
5159
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
5160
|
+
const pathSlug = projectSlugForTarget(target);
|
|
5161
|
+
if (pathSlug) slugs.add(pathSlug);
|
|
5162
|
+
const frontmatter = extractFrontmatter(content);
|
|
5163
|
+
if (frontmatter.ok && Array.isArray(frontmatter.data.provenance_projects)) {
|
|
5164
|
+
for (const entry of frontmatter.data.provenance_projects) {
|
|
5165
|
+
const match = String(entry).match(/^\[\[([^\]]+)\]\]$/);
|
|
5166
|
+
if (match && /^[a-z0-9][a-z0-9-]*$/i.test(match[1])) slugs.add(match[1]);
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
return [...slugs].sort();
|
|
5170
|
+
}
|
|
5171
|
+
async function refreshProjectIndexForTarget(vault, projectSlugs, today) {
|
|
5172
|
+
const paths = [];
|
|
5173
|
+
for (const slug of projectSlugs) {
|
|
5174
|
+
const rendered = await renderProjectIndex(vault, slug, { today });
|
|
5175
|
+
if (!rendered.ok) return rendered;
|
|
5176
|
+
const indexPath = join25(vault, rendered.data.index_path);
|
|
5177
|
+
try {
|
|
5178
|
+
await mkdir9(dirname6(indexPath), { recursive: true });
|
|
5179
|
+
} catch (error) {
|
|
5180
|
+
return err("WRITE_FAILED", { path: indexPath, message: String(error) });
|
|
5181
|
+
}
|
|
5182
|
+
const written = await atomicWriteText(indexPath, rendered.data.text);
|
|
5183
|
+
if (!written.ok) return written;
|
|
5184
|
+
try {
|
|
5185
|
+
const visible = await readFile12(indexPath, "utf8");
|
|
5186
|
+
if (visible !== rendered.data.text) {
|
|
5187
|
+
return err("WRITE_FAILED", { path: indexPath, message: "project index verification failed" });
|
|
5188
|
+
}
|
|
5189
|
+
} catch (error) {
|
|
5190
|
+
return err("WRITE_FAILED", { path: indexPath, message: String(error) });
|
|
5191
|
+
}
|
|
5192
|
+
if (written.data.changed) paths.push(rendered.data.index_path);
|
|
5193
|
+
}
|
|
5194
|
+
return ok({ changed: paths.length > 0, paths });
|
|
5195
|
+
}
|
|
5152
5196
|
function resolveRootAggregateMode(env = process.env) {
|
|
5153
5197
|
if (env.SKILLWIKI_ROOT_AGGREGATE_MODE === "source-only") return "source-only";
|
|
5154
5198
|
return "dual";
|
|
@@ -5248,6 +5292,7 @@ function emptyLockedState() {
|
|
|
5248
5292
|
taxonomyAdded: [],
|
|
5249
5293
|
pageChanged: false,
|
|
5250
5294
|
indexUpdated: false,
|
|
5295
|
+
projectIndexUpdated: false,
|
|
5251
5296
|
published: false,
|
|
5252
5297
|
changed: /* @__PURE__ */ new Set()
|
|
5253
5298
|
};
|
|
@@ -5335,6 +5380,15 @@ async function runLockedPrimaryStages(input, vault, deps) {
|
|
|
5335
5380
|
}
|
|
5336
5381
|
const verifyHook = await observeStage(deps, "verify");
|
|
5337
5382
|
if (verifyHook) return lockedFailure("verify", state, verifyHook);
|
|
5383
|
+
const projectSlugs = projectSlugsForPublication(input.page.target, input.page.content);
|
|
5384
|
+
if (projectSlugs.length > 0) {
|
|
5385
|
+
const projectIndex = await refreshProjectIndexForTarget(vault, projectSlugs, input.date);
|
|
5386
|
+
if (!projectIndex.ok) return lockedFailure("project-index", state, projectIndex);
|
|
5387
|
+
state.projectIndexUpdated = projectIndex.data.changed;
|
|
5388
|
+
for (const path of projectIndex.data.paths) state.changed.add(path);
|
|
5389
|
+
const projectIndexHook = await observeStage(deps, "project-index");
|
|
5390
|
+
if (projectIndexHook) return lockedFailure("project-index", state, projectIndexHook);
|
|
5391
|
+
}
|
|
5338
5392
|
const index = await upsertIndexEntry({
|
|
5339
5393
|
vault,
|
|
5340
5394
|
target: input.page.target,
|
|
@@ -5372,7 +5426,7 @@ function phaseFailure(stage, input, published, cause, context = {}, exitCode = E
|
|
|
5372
5426
|
})
|
|
5373
5427
|
};
|
|
5374
5428
|
}
|
|
5375
|
-
function successReceipt(input, taxonomyAdded, pageChanged, indexUpdated, logAppended, filesChanged, dryRun = false, receipt = null, approvalToken) {
|
|
5429
|
+
function successReceipt(input, taxonomyAdded, pageChanged, indexUpdated, projectIndexUpdated, logAppended, filesChanged, dryRun = false, receipt = null, approvalToken) {
|
|
5376
5430
|
return {
|
|
5377
5431
|
exitCode: ExitCode.OK,
|
|
5378
5432
|
result: ok({
|
|
@@ -5382,6 +5436,7 @@ function successReceipt(input, taxonomyAdded, pageChanged, indexUpdated, logAppe
|
|
|
5382
5436
|
taxonomy_added: [...taxonomyAdded],
|
|
5383
5437
|
page_changed: pageChanged,
|
|
5384
5438
|
index_updated: indexUpdated,
|
|
5439
|
+
project_index_updated: projectIndexUpdated,
|
|
5385
5440
|
log_appended: logAppended,
|
|
5386
5441
|
operation_id: input.operationId,
|
|
5387
5442
|
dry_run: dryRun,
|
|
@@ -5449,6 +5504,21 @@ async function previewPreparedPagePublication(input, vault) {
|
|
|
5449
5504
|
type: input.page.type
|
|
5450
5505
|
});
|
|
5451
5506
|
if (!index.ok) return { exitCode: errorExitCode(index.error), result: index };
|
|
5507
|
+
let projectIndexUpdated = false;
|
|
5508
|
+
const projectIndexPaths = [];
|
|
5509
|
+
for (const projectSlug of projectSlugsForPublication(input.page.target, input.page.content)) {
|
|
5510
|
+
const renderedProject = await renderProjectIndex(vault, projectSlug, { today: input.date });
|
|
5511
|
+
if (!renderedProject.ok) return { exitCode: errorExitCode(renderedProject.error), result: renderedProject };
|
|
5512
|
+
const projectIndexPath = join25(vault, renderedProject.data.index_path);
|
|
5513
|
+
const projectIndexChanged = await readPageChanged(projectIndexPath, renderedProject.data.text);
|
|
5514
|
+
if (!projectIndexChanged.ok) {
|
|
5515
|
+
return { exitCode: errorExitCode(projectIndexChanged.error), result: projectIndexChanged };
|
|
5516
|
+
}
|
|
5517
|
+
if (projectIndexChanged.data || pageChanged.data) {
|
|
5518
|
+
projectIndexUpdated = true;
|
|
5519
|
+
projectIndexPaths.push(renderedProject.data.index_path);
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5452
5522
|
const logPath = join25(vault, "log.md");
|
|
5453
5523
|
let logText;
|
|
5454
5524
|
try {
|
|
@@ -5461,6 +5531,7 @@ async function previewPreparedPagePublication(input, vault) {
|
|
|
5461
5531
|
const filesChanged = [
|
|
5462
5532
|
...reconciled.data.changed ? ["SCHEMA.md"] : [],
|
|
5463
5533
|
...pageChanged.data ? [input.page.target] : [],
|
|
5534
|
+
...projectIndexPaths,
|
|
5464
5535
|
...index.data.changed ? ["index.md"] : [],
|
|
5465
5536
|
...logAppended ? ["log.md"] : []
|
|
5466
5537
|
];
|
|
@@ -5471,6 +5542,7 @@ async function previewPreparedPagePublication(input, vault) {
|
|
|
5471
5542
|
reconciled.data.added,
|
|
5472
5543
|
pageChanged.data,
|
|
5473
5544
|
index.data.changed,
|
|
5545
|
+
projectIndexUpdated,
|
|
5474
5546
|
logAppended,
|
|
5475
5547
|
filesChanged,
|
|
5476
5548
|
true,
|
|
@@ -5622,6 +5694,7 @@ async function publishPreparedPageWithReceipt(input, vault, writeReceipt, deps =
|
|
|
5622
5694
|
state.taxonomyAdded,
|
|
5623
5695
|
state.pageChanged,
|
|
5624
5696
|
state.indexUpdated,
|
|
5697
|
+
state.projectIndexUpdated,
|
|
5625
5698
|
logAppended,
|
|
5626
5699
|
[...state.changed],
|
|
5627
5700
|
false,
|
|
@@ -5951,7 +6024,7 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
5951
6024
|
);
|
|
5952
6025
|
if (!input.dryRun) {
|
|
5953
6026
|
try {
|
|
5954
|
-
await
|
|
6027
|
+
await mkdir10(join26(input.vault, typedDir), { recursive: true });
|
|
5955
6028
|
} catch (error) {
|
|
5956
6029
|
return {
|
|
5957
6030
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6009,7 +6082,7 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
6009
6082
|
preflight: pagePublishDeps.preflight,
|
|
6010
6083
|
mutate: async (receipt) => {
|
|
6011
6084
|
try {
|
|
6012
|
-
await
|
|
6085
|
+
await mkdir10(join26(input.vault, "raw", "articles"), { recursive: true });
|
|
6013
6086
|
} catch (error) {
|
|
6014
6087
|
return {
|
|
6015
6088
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -6443,12 +6516,12 @@ async function runTagReconcile(input) {
|
|
|
6443
6516
|
|
|
6444
6517
|
// src/commands/project-page-publish.ts
|
|
6445
6518
|
import { realpathSync as realpathSync4 } from "fs";
|
|
6446
|
-
import { mkdir as
|
|
6447
|
-
import { dirname as
|
|
6519
|
+
import { mkdir as mkdir11, readFile as readFile15 } from "fs/promises";
|
|
6520
|
+
import { dirname as dirname9, join as join29, resolve as resolve5 } from "path";
|
|
6448
6521
|
|
|
6449
6522
|
// src/utils/architecture-page.ts
|
|
6450
6523
|
import { lstatSync, realpathSync as realpathSync2 } from "fs";
|
|
6451
|
-
import { dirname as
|
|
6524
|
+
import { dirname as dirname7, posix as posix2, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
|
|
6452
6525
|
var PROJECT_SLUG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
6453
6526
|
var ARCH_TARGET_RE = /^projects\/([a-z0-9][a-z0-9-]*)\/architecture\/([a-z0-9][a-z0-9._-]*\.md)$/;
|
|
6454
6527
|
function invalidFrontmatter(target, issues) {
|
|
@@ -6495,7 +6568,7 @@ function assertArchitectureTargetInsideVault(vault, target, project) {
|
|
|
6495
6568
|
return err("VAULT_PATH_INVALID", { target, message: "vault realpath failed" });
|
|
6496
6569
|
}
|
|
6497
6570
|
const absolutePath = resolve4(vaultReal, target);
|
|
6498
|
-
const parent =
|
|
6571
|
+
const parent = dirname7(absolutePath);
|
|
6499
6572
|
let parentReal;
|
|
6500
6573
|
try {
|
|
6501
6574
|
parentReal = realpathSync2(parent);
|
|
@@ -6605,7 +6678,7 @@ import {
|
|
|
6605
6678
|
writeFileSync as writeFileSync5
|
|
6606
6679
|
} from "fs";
|
|
6607
6680
|
import { homedir } from "os";
|
|
6608
|
-
import { dirname as
|
|
6681
|
+
import { dirname as dirname8, join as join28 } from "path";
|
|
6609
6682
|
var PUBLICATION_JOURNAL_SCHEMA = "skillwiki-publication-operation-v1";
|
|
6610
6683
|
var PHASE_ORDER2 = [
|
|
6611
6684
|
"locked",
|
|
@@ -6654,7 +6727,7 @@ function ensurePrivateDir(dir) {
|
|
|
6654
6727
|
}
|
|
6655
6728
|
}
|
|
6656
6729
|
function atomicWriteJson(path, body) {
|
|
6657
|
-
ensurePrivateDir(
|
|
6730
|
+
ensurePrivateDir(dirname8(path));
|
|
6658
6731
|
const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
6659
6732
|
writeFileSync5(tmp, body, { encoding: "utf8", mode: 384 });
|
|
6660
6733
|
try {
|
|
@@ -7276,7 +7349,7 @@ async function runLockedStages(prepared, vault, deps, journalHome) {
|
|
|
7276
7349
|
}
|
|
7277
7350
|
if (!phaseAtLeast(currentPhase, "page")) {
|
|
7278
7351
|
try {
|
|
7279
|
-
await
|
|
7352
|
+
await mkdir11(dirname9(prepared.targetPath), { recursive: true });
|
|
7280
7353
|
} catch (error) {
|
|
7281
7354
|
return {
|
|
7282
7355
|
ok: false,
|
|
@@ -7372,7 +7445,7 @@ async function runLockedStages(prepared, vault, deps, journalHome) {
|
|
|
7372
7445
|
}
|
|
7373
7446
|
const knowledgePath = join29(vault, rendered.data.index_path);
|
|
7374
7447
|
try {
|
|
7375
|
-
await
|
|
7448
|
+
await mkdir11(dirname9(knowledgePath), { recursive: true });
|
|
7376
7449
|
} catch (error) {
|
|
7377
7450
|
return {
|
|
7378
7451
|
ok: false,
|
|
@@ -8018,22 +8091,22 @@ async function runSyncPush(input) {
|
|
|
8018
8091
|
})
|
|
8019
8092
|
};
|
|
8020
8093
|
}
|
|
8021
|
-
function enumerateStashes(vault) {
|
|
8022
|
-
const output = git(vault, ["log", "--format=%gd%x09%s%x09%ct", "-g", "stash"]);
|
|
8094
|
+
function enumerateStashes(vault, nowMs = Date.now()) {
|
|
8095
|
+
const output = git(vault, ["log", "--format=%gd%x09%H%x09%s%x09%ct", "-g", "stash"]);
|
|
8023
8096
|
if (!output) return [];
|
|
8024
|
-
const now = Date.now();
|
|
8025
8097
|
const stashes = [];
|
|
8026
8098
|
const lines = output.split("\n").filter((l) => l.trim().length > 0);
|
|
8027
8099
|
for (const line of lines) {
|
|
8028
8100
|
const parts = line.split(" ");
|
|
8029
|
-
if (parts.length <
|
|
8101
|
+
if (parts.length < 4) continue;
|
|
8030
8102
|
const ref = parts[0];
|
|
8031
|
-
const
|
|
8032
|
-
const
|
|
8103
|
+
const oid = parts[1];
|
|
8104
|
+
const message = parts[2];
|
|
8105
|
+
const ctStr = parts[3];
|
|
8033
8106
|
const ct = parseInt(ctStr, 10);
|
|
8034
8107
|
if (isNaN(ct)) continue;
|
|
8035
|
-
const age_minutes = Math.floor((
|
|
8036
|
-
stashes.push({ ref, message, age_minutes });
|
|
8108
|
+
const age_minutes = Math.floor((nowMs - ct * 1e3) / (60 * 1e3));
|
|
8109
|
+
stashes.push({ ref, oid, message, age_minutes });
|
|
8037
8110
|
}
|
|
8038
8111
|
return stashes;
|
|
8039
8112
|
}
|
|
@@ -8112,16 +8185,73 @@ async function runSyncPull(input) {
|
|
|
8112
8185
|
})
|
|
8113
8186
|
};
|
|
8114
8187
|
}
|
|
8188
|
+
var STASH_RECENT_MINUTES = 120;
|
|
8189
|
+
var MANAGED_WRITER_PATTERNS = [
|
|
8190
|
+
["wiki-push", /(?:^|[\s/\\])wiki-push(?:\.[a-z0-9_-]+)?(?:\s|$)/i],
|
|
8191
|
+
["rclone", /(?:^|[\s/\\])rclone(?:\.[a-z0-9_-]+)?(?:\s|$)/i],
|
|
8192
|
+
["vault-sync", /(?:^|[\s/\\])vault-sync(?:\.[a-z0-9_-]+)?(?:\s|$)/i]
|
|
8193
|
+
];
|
|
8194
|
+
function classifyStash(ageMinutes, sessionId, currentSession) {
|
|
8195
|
+
if (sessionId && sessionId === currentSession) return "self_or_local_recovery_stash";
|
|
8196
|
+
if (sessionId && ageMinutes <= STASH_RECENT_MINUTES) return "recent_known_peer_stash";
|
|
8197
|
+
if (ageMinutes > STASH_RECENT_MINUTES) return "stale_stash_backlog";
|
|
8198
|
+
return "unknown_stash_ownership";
|
|
8199
|
+
}
|
|
8200
|
+
function classifyManagedWriterProcesses(snapshot, currentPid = process.pid) {
|
|
8201
|
+
const kinds = /* @__PURE__ */ new Set();
|
|
8202
|
+
let count2 = 0;
|
|
8203
|
+
for (const line of snapshot.split(/\r?\n/)) {
|
|
8204
|
+
const trimmed = line.trim();
|
|
8205
|
+
if (!trimmed) continue;
|
|
8206
|
+
let pid;
|
|
8207
|
+
let processName = "";
|
|
8208
|
+
const csvMatch = trimmed.match(/^"([^"]*)","(\d+)"(?:,|$)/);
|
|
8209
|
+
if (csvMatch) {
|
|
8210
|
+
processName = csvMatch[1];
|
|
8211
|
+
pid = Number(csvMatch[2]);
|
|
8212
|
+
} else {
|
|
8213
|
+
const pidMatch = trimmed.match(/^(\d+)\s+(.*)$/);
|
|
8214
|
+
if (!pidMatch) continue;
|
|
8215
|
+
pid = Number(pidMatch[1]);
|
|
8216
|
+
processName = pidMatch[2];
|
|
8217
|
+
}
|
|
8218
|
+
if (pid === currentPid) continue;
|
|
8219
|
+
const kind = MANAGED_WRITER_PATTERNS.find(([, pattern]) => pattern.test(processName))?.[0];
|
|
8220
|
+
if (!kind) continue;
|
|
8221
|
+
count2 += 1;
|
|
8222
|
+
kinds.add(kind);
|
|
8223
|
+
}
|
|
8224
|
+
return { count: count2, kinds: [...kinds].sort(), blocking: count2 > 0 };
|
|
8225
|
+
}
|
|
8226
|
+
function managedWriterSnapshot() {
|
|
8227
|
+
try {
|
|
8228
|
+
if (process.platform === "win32") {
|
|
8229
|
+
return execFileSync3("tasklist", ["/FO", "CSV", "/NH"], {
|
|
8230
|
+
encoding: "utf8",
|
|
8231
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8232
|
+
});
|
|
8233
|
+
}
|
|
8234
|
+
return execFileSync3("ps", ["-axo", "pid=,command="], {
|
|
8235
|
+
encoding: "utf8",
|
|
8236
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8237
|
+
});
|
|
8238
|
+
} catch {
|
|
8239
|
+
return "";
|
|
8240
|
+
}
|
|
8241
|
+
}
|
|
8115
8242
|
function runSyncPeers(input) {
|
|
8116
8243
|
const vault = input.vault;
|
|
8244
|
+
const currentSession = input.sessionId ?? getSessionId();
|
|
8117
8245
|
const locks = [];
|
|
8118
8246
|
const existingLock = readLock(vault);
|
|
8119
8247
|
if (existingLock) {
|
|
8120
|
-
const self = existingLock.session_id ===
|
|
8248
|
+
const self = existingLock.session_id === currentSession;
|
|
8121
8249
|
locks.push({ ...existingLock, is_self: self });
|
|
8122
8250
|
}
|
|
8123
|
-
const allStashes = enumerateStashes(vault);
|
|
8251
|
+
const allStashes = enumerateStashes(vault, input.nowMs);
|
|
8124
8252
|
const stashes = [];
|
|
8253
|
+
const wikiSyncAudit = [];
|
|
8254
|
+
const otherStashAudit = [];
|
|
8125
8255
|
for (const stash of allStashes) {
|
|
8126
8256
|
let actualMessage = stash.message;
|
|
8127
8257
|
const prefixMatch = stash.message.match(/^On [^:]+:\s*(.*)/);
|
|
@@ -8129,29 +8259,63 @@ function runSyncPeers(input) {
|
|
|
8129
8259
|
actualMessage = prefixMatch[1];
|
|
8130
8260
|
}
|
|
8131
8261
|
const match = actualMessage.match(/^wiki-sync:([^:]+):([^:]+):(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z):(.*)$/);
|
|
8132
|
-
if (
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8262
|
+
if (match) {
|
|
8263
|
+
const session_id = match[1];
|
|
8264
|
+
const cwd_hash = match[2];
|
|
8265
|
+
const timestamp = match[3];
|
|
8266
|
+
const summary = match[4];
|
|
8267
|
+
stashes.push({
|
|
8268
|
+
ref: stash.ref,
|
|
8269
|
+
oid: stash.oid,
|
|
8270
|
+
session_id,
|
|
8271
|
+
cwd_hash,
|
|
8272
|
+
timestamp,
|
|
8273
|
+
summary,
|
|
8274
|
+
age_minutes: stash.age_minutes
|
|
8275
|
+
});
|
|
8276
|
+
wikiSyncAudit.push({
|
|
8277
|
+
ref: stash.ref,
|
|
8278
|
+
oid: stash.oid,
|
|
8279
|
+
age_minutes: stash.age_minutes,
|
|
8280
|
+
classification: classifyStash(stash.age_minutes, session_id, currentSession),
|
|
8281
|
+
format: "wiki-sync",
|
|
8282
|
+
session_id
|
|
8283
|
+
});
|
|
8284
|
+
continue;
|
|
8285
|
+
}
|
|
8286
|
+
const vaultSyncMatch = actualMessage.match(/^vault-sync\s+op=([^\s]+)(?:\s+.*)?$/i);
|
|
8287
|
+
const operationId2 = vaultSyncMatch?.[1];
|
|
8288
|
+
const manualPeer = /^peer$/i.test(actualMessage.trim());
|
|
8289
|
+
const format = operationId2 ? "vault-sync" : manualPeer ? "manual" : "unknown";
|
|
8290
|
+
const classification = operationId2 ? stash.age_minutes <= STASH_RECENT_MINUTES ? "recent_known_peer_stash" : "stale_stash_backlog" : "unknown_stash_ownership";
|
|
8291
|
+
otherStashAudit.push({
|
|
8138
8292
|
ref: stash.ref,
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8143
|
-
|
|
8293
|
+
oid: stash.oid,
|
|
8294
|
+
age_minutes: stash.age_minutes,
|
|
8295
|
+
classification,
|
|
8296
|
+
format,
|
|
8297
|
+
...operationId2 ? { operation_id: operationId2 } : {}
|
|
8144
8298
|
});
|
|
8145
8299
|
}
|
|
8300
|
+
const stashAudit = [...wikiSyncAudit, ...otherStashAudit];
|
|
8301
|
+
const managedWriters = classifyManagedWriterProcesses(
|
|
8302
|
+
input.processSnapshot ?? managedWriterSnapshot()
|
|
8303
|
+
);
|
|
8146
8304
|
const hintParts = [];
|
|
8147
8305
|
if (locks.length > 0) hintParts.push(`${locks.length} lock(s)`);
|
|
8148
8306
|
if (stashes.length > 0) hintParts.push(`${stashes.length} wiki-sync stash(es)`);
|
|
8307
|
+
if (managedWriters.count > 0) hintParts.push(`${managedWriters.count} live writer overlap(s)`);
|
|
8308
|
+
if (stashAudit.length > 0) hintParts.push(`${stashAudit.length} stash audit item(s)`);
|
|
8309
|
+
const blocking = locks.some((lock) => !lock.is_self) || managedWriters.blocking || stashAudit.some((entry) => entry.classification === "recent_known_peer_stash");
|
|
8149
8310
|
const humanHint = hintParts.length > 0 ? hintParts.join(", ") : "no peers detected";
|
|
8150
8311
|
return {
|
|
8151
8312
|
exitCode: ExitCode.OK,
|
|
8152
8313
|
result: ok({
|
|
8153
8314
|
locks,
|
|
8154
8315
|
stashes,
|
|
8316
|
+
stash_audit: stashAudit,
|
|
8317
|
+
managed_writers: managedWriters,
|
|
8318
|
+
blocking,
|
|
8155
8319
|
humanHint
|
|
8156
8320
|
})
|
|
8157
8321
|
};
|
|
@@ -8585,7 +8749,7 @@ function releaseSnapshotFlock(handle) {
|
|
|
8585
8749
|
|
|
8586
8750
|
// src/commands/backup.ts
|
|
8587
8751
|
import { statSync as statSync2, readdirSync as readdirSync2, readFileSync as readFileSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
8588
|
-
import { join as join32, relative as relative3, dirname as
|
|
8752
|
+
import { join as join32, relative as relative3, dirname as dirname10 } from "path";
|
|
8589
8753
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
8590
8754
|
|
|
8591
8755
|
// src/utils/s3-client.ts
|
|
@@ -8721,7 +8885,7 @@ async function runBackupRestore(input) {
|
|
|
8721
8885
|
const resp = await client.send(new GetObjectCommand({ Bucket: input.bucket, Key: obj.Key }));
|
|
8722
8886
|
const body = await resp.Body?.transformToByteArray();
|
|
8723
8887
|
if (body) {
|
|
8724
|
-
mkdirSync5(
|
|
8888
|
+
mkdirSync5(dirname10(localPath), { recursive: true });
|
|
8725
8889
|
writeFileSync6(localPath, Buffer.from(body));
|
|
8726
8890
|
downloaded++;
|
|
8727
8891
|
}
|
|
@@ -8843,7 +9007,7 @@ async function runStatus(input) {
|
|
|
8843
9007
|
}
|
|
8844
9008
|
|
|
8845
9009
|
// src/commands/seed.ts
|
|
8846
|
-
import { mkdir as
|
|
9010
|
+
import { mkdir as mkdir12, writeFile as writeFile8, stat as stat4 } from "fs/promises";
|
|
8847
9011
|
import { join as join34 } from "path";
|
|
8848
9012
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8849
9013
|
var EXAMPLE_PAGES = {
|
|
@@ -8925,7 +9089,7 @@ async function runSeed(input) {
|
|
|
8925
9089
|
await stat4(absPath);
|
|
8926
9090
|
skipped.push(relPath);
|
|
8927
9091
|
} catch {
|
|
8928
|
-
await
|
|
9092
|
+
await mkdir12(join34(absPath, ".."), { recursive: true });
|
|
8929
9093
|
await writeFile8(absPath, content, "utf8");
|
|
8930
9094
|
created.push(relPath);
|
|
8931
9095
|
}
|
|
@@ -8935,7 +9099,7 @@ async function runSeed(input) {
|
|
|
8935
9099
|
await stat4(rawPath);
|
|
8936
9100
|
skipped.push("raw/articles/example-source.md");
|
|
8937
9101
|
} catch {
|
|
8938
|
-
await
|
|
9102
|
+
await mkdir12(join34(rawPath, ".."), { recursive: true });
|
|
8939
9103
|
await writeFile8(rawPath, EXAMPLE_RAW, "utf8");
|
|
8940
9104
|
created.push("raw/articles/example-source.md");
|
|
8941
9105
|
}
|
package/dist/skillwiki-mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.26",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 19 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|