skillwiki 0.10.1 → 0.10.3
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/{chunk-SCGC7YNM.js → chunk-QH5MAV74.js} +79 -12
- package/dist/{chunk-IZABIE44.js → chunk-U34B2XQJ.js} +126 -88
- package/dist/{chunk-XSTXPA34.js → chunk-Z3WS45PL.js} +15 -14
- package/dist/cli.js +13 -10
- package/dist/{index-projection-ERFX76U5.js → index-projection-VNJ3TLEK.js} +1 -1
- package/dist/{managed-write-preflight-PW4OOOMV.js → managed-write-preflight-DTNXZBLI.js} +1 -1
- package/dist/skillwiki-mcp.js +2 -2
- package/dist/vault-sync/scripts/wiki-snapshot.sh +28 -2
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
FLEET_REL_PATH,
|
|
3
4
|
findReviewRequiredOp,
|
|
4
5
|
git,
|
|
5
6
|
hasActiveGitSequencer,
|
|
@@ -14,6 +15,10 @@ import {
|
|
|
14
15
|
ok
|
|
15
16
|
} from "./chunk-C5OLZRRM.js";
|
|
16
17
|
|
|
18
|
+
// src/utils/managed-write-preflight.ts
|
|
19
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
20
|
+
import { join as join2, resolve } from "path";
|
|
21
|
+
|
|
17
22
|
// src/utils/managed-write-lock.ts
|
|
18
23
|
import { randomBytes } from "crypto";
|
|
19
24
|
import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -78,19 +83,52 @@ function preflightBlocker(vault) {
|
|
|
78
83
|
if (op) return { reason: "review-required", operation_id: op };
|
|
79
84
|
return null;
|
|
80
85
|
}
|
|
86
|
+
function fleetManifestBytes(vault) {
|
|
87
|
+
const path = join2(vault, FLEET_REL_PATH);
|
|
88
|
+
if (!existsSync(path)) return null;
|
|
89
|
+
return readFileSync2(path);
|
|
90
|
+
}
|
|
91
|
+
function isGitVault(vault) {
|
|
92
|
+
return Boolean(git(vault, ["rev-parse", "--absolute-git-dir"]));
|
|
93
|
+
}
|
|
81
94
|
async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
82
95
|
const vault = input.vault;
|
|
83
|
-
const
|
|
84
|
-
|
|
96
|
+
const convergenceVault = input.convergenceVault && resolve(input.convergenceVault) !== resolve(vault) ? resolve(input.convergenceVault) : void 0;
|
|
97
|
+
const gitVault = convergenceVault ?? vault;
|
|
98
|
+
const mutationBlocker = preflightBlocker(vault);
|
|
99
|
+
if (mutationBlocker) {
|
|
85
100
|
return {
|
|
86
101
|
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
87
102
|
result: err("PREFLIGHT_FAILED", {
|
|
88
|
-
reason:
|
|
89
|
-
operation_id:
|
|
90
|
-
unmerged_paths:
|
|
103
|
+
reason: mutationBlocker.reason,
|
|
104
|
+
operation_id: mutationBlocker.operation_id,
|
|
105
|
+
unmerged_paths: mutationBlocker.unmerged_paths
|
|
91
106
|
})
|
|
92
107
|
};
|
|
93
108
|
}
|
|
109
|
+
if (convergenceVault) {
|
|
110
|
+
if (!isGitVault(convergenceVault)) {
|
|
111
|
+
return {
|
|
112
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
113
|
+
result: err("PREFLIGHT_FAILED", {
|
|
114
|
+
reason: "convergence-vault-not-git",
|
|
115
|
+
convergence_vault: convergenceVault
|
|
116
|
+
})
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const gitBlocker = preflightBlocker(convergenceVault);
|
|
120
|
+
if (gitBlocker) {
|
|
121
|
+
return {
|
|
122
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
123
|
+
result: err("PREFLIGHT_FAILED", {
|
|
124
|
+
reason: gitBlocker.reason,
|
|
125
|
+
operation_id: gitBlocker.operation_id,
|
|
126
|
+
unmerged_paths: gitBlocker.unmerged_paths,
|
|
127
|
+
convergence_vault: convergenceVault
|
|
128
|
+
})
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
94
132
|
const fleet = await loadFleetManifestAndHost({
|
|
95
133
|
vault,
|
|
96
134
|
hostId: input.hostId,
|
|
@@ -98,14 +136,16 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
98
136
|
home: input.home,
|
|
99
137
|
osHostname: input.osHostname
|
|
100
138
|
});
|
|
139
|
+
const dualPathMeta = convergenceVault ? { convergence_vault: convergenceVault } : {};
|
|
101
140
|
if (!fleet) {
|
|
102
|
-
const head = git(
|
|
141
|
+
const head = git(gitVault, ["rev-parse", "HEAD"]) || null;
|
|
103
142
|
return {
|
|
104
143
|
exitCode: ExitCode.OK,
|
|
105
144
|
result: ok({
|
|
106
145
|
mode: "standalone",
|
|
107
146
|
base_oid: head,
|
|
108
|
-
converged: false
|
|
147
|
+
converged: false,
|
|
148
|
+
...dualPathMeta
|
|
109
149
|
})
|
|
110
150
|
};
|
|
111
151
|
}
|
|
@@ -119,6 +159,27 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
119
159
|
})
|
|
120
160
|
};
|
|
121
161
|
}
|
|
162
|
+
if (convergenceVault && fleetManifestBytes(convergenceVault)) {
|
|
163
|
+
const convergeFleetCtx = await loadFleetManifestAndHost({
|
|
164
|
+
vault: convergenceVault,
|
|
165
|
+
hostId: input.hostId ?? fleet.hostId,
|
|
166
|
+
env: input.env,
|
|
167
|
+
home: input.home,
|
|
168
|
+
osHostname: input.osHostname
|
|
169
|
+
});
|
|
170
|
+
if (!convergeFleetCtx || convergeFleetCtx.identityStatus !== "known" || convergeFleetCtx.hostId !== fleet.hostId) {
|
|
171
|
+
return {
|
|
172
|
+
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
173
|
+
result: err("PREFLIGHT_FAILED", {
|
|
174
|
+
reason: "convergence-vault-identity-mismatch",
|
|
175
|
+
host_id: fleet.hostId,
|
|
176
|
+
convergence_host_id: convergeFleetCtx?.hostId,
|
|
177
|
+
convergence_identity_status: convergeFleetCtx?.identityStatus,
|
|
178
|
+
convergence_vault: convergenceVault
|
|
179
|
+
})
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
122
183
|
const host = fleet.manifest.hosts[fleet.hostId];
|
|
123
184
|
if (!host) {
|
|
124
185
|
return {
|
|
@@ -134,12 +195,13 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
134
195
|
mode: "immutable-record",
|
|
135
196
|
host_id: fleet.hostId,
|
|
136
197
|
base_oid: null,
|
|
137
|
-
converged: false
|
|
198
|
+
converged: false,
|
|
199
|
+
...dualPathMeta
|
|
138
200
|
})
|
|
139
201
|
};
|
|
140
202
|
}
|
|
141
203
|
const converge = await deps.converge({
|
|
142
|
-
vault,
|
|
204
|
+
vault: gitVault,
|
|
143
205
|
lockToken: input.lockToken,
|
|
144
206
|
env: input.env,
|
|
145
207
|
home: input.home
|
|
@@ -148,11 +210,14 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
148
210
|
const exitCode = converge.error === "PREFLIGHT_FAILED" ? ExitCode.PREFLIGHT_FAILED : ExitCode.SYNC_PULL_FAILED;
|
|
149
211
|
return { exitCode, result: converge };
|
|
150
212
|
}
|
|
151
|
-
const baseOid = git(
|
|
213
|
+
const baseOid = git(gitVault, ["rev-parse", "HEAD"]);
|
|
152
214
|
if (!baseOid) {
|
|
153
215
|
return {
|
|
154
216
|
exitCode: ExitCode.PREFLIGHT_FAILED,
|
|
155
|
-
result: err("PREFLIGHT_FAILED", {
|
|
217
|
+
result: err("PREFLIGHT_FAILED", {
|
|
218
|
+
reason: "missing-head-after-converge",
|
|
219
|
+
...dualPathMeta
|
|
220
|
+
})
|
|
156
221
|
};
|
|
157
222
|
}
|
|
158
223
|
return {
|
|
@@ -162,7 +227,8 @@ async function runManagedWritePreflight(input, deps = DEFAULT_DEPS) {
|
|
|
162
227
|
host_id: fleet.hostId,
|
|
163
228
|
base_oid: baseOid,
|
|
164
229
|
converged: true,
|
|
165
|
-
helper_path: converge.data.helper_path
|
|
230
|
+
helper_path: converge.data.helper_path,
|
|
231
|
+
...dualPathMeta
|
|
166
232
|
})
|
|
167
233
|
};
|
|
168
234
|
}
|
|
@@ -177,6 +243,7 @@ async function runManagedWriteTransaction(input, deps = DEFAULT_DEPS) {
|
|
|
177
243
|
{
|
|
178
244
|
vault: input.vault,
|
|
179
245
|
command: input.command,
|
|
246
|
+
convergenceVault: input.convergenceVault,
|
|
180
247
|
hostId: input.hostId,
|
|
181
248
|
lockToken: handle.ownerToken,
|
|
182
249
|
env: input.env,
|
|
@@ -9,8 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-C5OLZRRM.js";
|
|
10
10
|
|
|
11
11
|
// src/utils/index-projection.ts
|
|
12
|
-
import {
|
|
13
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
12
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
14
13
|
import { join as join3 } from "path";
|
|
15
14
|
|
|
16
15
|
// src/utils/atomic-write.ts
|
|
@@ -59,6 +58,9 @@ async function atomicWriteText(path, text) {
|
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
|
|
61
|
+
// src/utils/index-universe.ts
|
|
62
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
63
|
+
|
|
62
64
|
// src/utils/typed-page.ts
|
|
63
65
|
import { lstatSync, realpathSync } from "fs";
|
|
64
66
|
import { dirname as dirname2, posix, relative, resolve, sep } from "path";
|
|
@@ -440,8 +442,15 @@ async function readPageCached(p, cache) {
|
|
|
440
442
|
return pending;
|
|
441
443
|
}
|
|
442
444
|
|
|
443
|
-
// src/utils/index-
|
|
444
|
-
var
|
|
445
|
+
// src/utils/index-universe.ts
|
|
446
|
+
var ROOT_INDEX_SECTION_ORDER = [
|
|
447
|
+
"Entities",
|
|
448
|
+
"Concepts",
|
|
449
|
+
"Comparisons",
|
|
450
|
+
"Queries",
|
|
451
|
+
"Meta",
|
|
452
|
+
"Projects"
|
|
453
|
+
];
|
|
445
454
|
var TYPE_SECTION = {
|
|
446
455
|
entity: "Entities",
|
|
447
456
|
concept: "Concepts",
|
|
@@ -450,6 +459,106 @@ var TYPE_SECTION = {
|
|
|
450
459
|
meta: "Meta"
|
|
451
460
|
};
|
|
452
461
|
var compareText = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
462
|
+
function projectTitleFromReadme(text, slug) {
|
|
463
|
+
const match = text.match(/^#\s+Project:\s+(.+)$/m);
|
|
464
|
+
return match?.[1]?.trim() || slug;
|
|
465
|
+
}
|
|
466
|
+
async function buildRootIndexUniverse(input) {
|
|
467
|
+
const scanned = input.scan ? ok(input.scan) : await scanVault(input.vault);
|
|
468
|
+
if (!scanned.ok) return scanned;
|
|
469
|
+
const required = [];
|
|
470
|
+
const rejectedTyped = [];
|
|
471
|
+
const seen = /* @__PURE__ */ new Map();
|
|
472
|
+
let duplicatesRemoved = 0;
|
|
473
|
+
const typedPages = [...scanned.data.typedKnowledge].sort((a, b) => compareText(a.relPath, b.relPath));
|
|
474
|
+
for (const page of typedPages) {
|
|
475
|
+
let text;
|
|
476
|
+
try {
|
|
477
|
+
text = await readFile3(page.absPath, "utf8");
|
|
478
|
+
} catch {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const prepared = prepareTypedPage(text, page.relPath);
|
|
482
|
+
if (!prepared.ok) {
|
|
483
|
+
rejectedTyped.push({
|
|
484
|
+
relPath: page.relPath,
|
|
485
|
+
error: prepared.error,
|
|
486
|
+
detail: prepared.detail
|
|
487
|
+
});
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
const section = TYPE_SECTION[prepared.data.type];
|
|
491
|
+
if (!section) {
|
|
492
|
+
rejectedTyped.push({
|
|
493
|
+
relPath: page.relPath,
|
|
494
|
+
error: "SCHEME_REJECTED",
|
|
495
|
+
detail: { type: prepared.data.type }
|
|
496
|
+
});
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const target = prepared.data.target.replace(/\.md$/, "");
|
|
500
|
+
const previousTitle = seen.get(target);
|
|
501
|
+
if (previousTitle !== void 0) {
|
|
502
|
+
if (previousTitle !== prepared.data.title) {
|
|
503
|
+
return err("SCHEME_REJECTED", {
|
|
504
|
+
message: `duplicate index target ${target} with differing titles`,
|
|
505
|
+
titles: [previousTitle, prepared.data.title]
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
duplicatesRemoved += 1;
|
|
509
|
+
continue;
|
|
510
|
+
}
|
|
511
|
+
seen.set(target, prepared.data.title);
|
|
512
|
+
required.push({
|
|
513
|
+
section,
|
|
514
|
+
target,
|
|
515
|
+
title: prepared.data.title,
|
|
516
|
+
source: "typed"
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
const projectReadmes = scanned.data.allMarkdown.filter((page) => /^projects\/[^/]+\/README\.md$/.test(page.relPath)).sort((a, b) => compareText(a.relPath, b.relPath));
|
|
520
|
+
for (const readme of projectReadmes) {
|
|
521
|
+
let text;
|
|
522
|
+
try {
|
|
523
|
+
text = await readFile3(readme.absPath, "utf8");
|
|
524
|
+
} catch {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const projectSlug = readme.relPath.split("/")[1];
|
|
528
|
+
const target = `projects/${projectSlug}/README`;
|
|
529
|
+
const title = projectTitleFromReadme(text, projectSlug);
|
|
530
|
+
const previousTitle = seen.get(target);
|
|
531
|
+
if (previousTitle !== void 0) {
|
|
532
|
+
if (previousTitle !== title) {
|
|
533
|
+
return err("SCHEME_REJECTED", {
|
|
534
|
+
message: `duplicate index target ${target} with differing titles`
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
duplicatesRemoved += 1;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
seen.set(target, title);
|
|
541
|
+
required.push({ section: "Projects", target, title, source: "project" });
|
|
542
|
+
}
|
|
543
|
+
required.sort((a, b) => {
|
|
544
|
+
const sectionA = ROOT_INDEX_SECTION_ORDER.indexOf(a.section);
|
|
545
|
+
const sectionB = ROOT_INDEX_SECTION_ORDER.indexOf(b.section);
|
|
546
|
+
if (sectionA !== sectionB) return sectionA - sectionB;
|
|
547
|
+
return compareText(a.target, b.target);
|
|
548
|
+
});
|
|
549
|
+
const knownTargets = new Set(required.map((entry) => entry.target));
|
|
550
|
+
for (const page of [...scanned.data.compound].sort((a, b) => compareText(a.relPath, b.relPath))) {
|
|
551
|
+
knownTargets.add(page.relPath.replace(/\.md$/, ""));
|
|
552
|
+
}
|
|
553
|
+
return ok({
|
|
554
|
+
required,
|
|
555
|
+
knownTargets,
|
|
556
|
+
rejectedTyped,
|
|
557
|
+
duplicatesRemoved
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/utils/index-projection.ts
|
|
453
562
|
var UNMANAGED_START = "<!-- skillwiki:index-unmanaged:start -->";
|
|
454
563
|
var UNMANAGED_END = "<!-- skillwiki:index-unmanaged:end -->";
|
|
455
564
|
function extractUnmanaged(currentText) {
|
|
@@ -480,94 +589,27 @@ function priorWikilinkTargets(text) {
|
|
|
480
589
|
}
|
|
481
590
|
return out;
|
|
482
591
|
}
|
|
483
|
-
function projectTitleFromReadme(text, slug) {
|
|
484
|
-
const m = text.match(/^#\s+Project:\s+(.+)$/m);
|
|
485
|
-
return m?.[1]?.trim() || slug;
|
|
486
|
-
}
|
|
487
592
|
async function renderRootIndex(input) {
|
|
488
593
|
const vault = input.vault;
|
|
489
594
|
let currentText = input.currentText;
|
|
490
595
|
if (currentText === void 0) {
|
|
491
596
|
try {
|
|
492
|
-
currentText = await
|
|
597
|
+
currentText = await readFile4(join3(vault, "index.md"), "utf8");
|
|
493
598
|
} catch {
|
|
494
599
|
currentText = "";
|
|
495
600
|
}
|
|
496
601
|
}
|
|
497
602
|
const unmanaged = extractUnmanaged(currentText);
|
|
498
603
|
if (!unmanaged.ok) return unmanaged;
|
|
499
|
-
const
|
|
500
|
-
if (!
|
|
501
|
-
const entries =
|
|
502
|
-
|
|
503
|
-
let duplicatesRemoved = 0;
|
|
504
|
-
for (const page of scan.data.typedKnowledge) {
|
|
505
|
-
let text;
|
|
506
|
-
try {
|
|
507
|
-
text = await readFile3(join3(vault, page.relPath), "utf8");
|
|
508
|
-
} catch {
|
|
509
|
-
continue;
|
|
510
|
-
}
|
|
511
|
-
const prepared = prepareTypedPage(text, page.relPath);
|
|
512
|
-
if (!prepared.ok) continue;
|
|
513
|
-
const section = TYPE_SECTION[prepared.data.type];
|
|
514
|
-
if (!section) continue;
|
|
515
|
-
const target = prepared.data.target.replace(/\.md$/, "");
|
|
516
|
-
const prev = seen.get(target);
|
|
517
|
-
if (prev !== void 0) {
|
|
518
|
-
if (prev !== prepared.data.title) {
|
|
519
|
-
return err("SCHEME_REJECTED", {
|
|
520
|
-
message: `duplicate index target ${target} with differing titles`,
|
|
521
|
-
titles: [prev, prepared.data.title]
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
duplicatesRemoved += 1;
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
seen.set(target, prepared.data.title);
|
|
528
|
-
entries.push({
|
|
529
|
-
section,
|
|
530
|
-
target,
|
|
531
|
-
title: prepared.data.title,
|
|
532
|
-
source: "typed"
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
try {
|
|
536
|
-
const projectsRoot = join3(vault, "projects");
|
|
537
|
-
for (const slug of readdirSync(projectsRoot, { withFileTypes: true })) {
|
|
538
|
-
if (!slug.isDirectory()) continue;
|
|
539
|
-
const readmePath = join3(projectsRoot, slug.name, "README.md");
|
|
540
|
-
let text;
|
|
541
|
-
try {
|
|
542
|
-
text = readFileSync2(readmePath, "utf8");
|
|
543
|
-
} catch {
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
const target = `projects/${slug.name}/README`;
|
|
547
|
-
const title = projectTitleFromReadme(text, slug.name);
|
|
548
|
-
const prev = seen.get(target);
|
|
549
|
-
if (prev !== void 0) {
|
|
550
|
-
if (prev !== title) {
|
|
551
|
-
return err("SCHEME_REJECTED", {
|
|
552
|
-
message: `duplicate index target ${target} with differing titles`
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
duplicatesRemoved += 1;
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
seen.set(target, title);
|
|
559
|
-
entries.push({ section: "Projects", target, title, source: "project" });
|
|
560
|
-
}
|
|
561
|
-
} catch {
|
|
562
|
-
}
|
|
563
|
-
entries.sort((a, b) => {
|
|
564
|
-
const sa = SECTION_ORDER.indexOf(a.section);
|
|
565
|
-
const sb = SECTION_ORDER.indexOf(b.section);
|
|
566
|
-
if (sa !== sb) return sa - sb;
|
|
567
|
-
return compareText(a.target, b.target);
|
|
568
|
-
});
|
|
604
|
+
const universe = await buildRootIndexUniverse({ vault });
|
|
605
|
+
if (!universe.ok) return universe;
|
|
606
|
+
const entries = universe.data.required;
|
|
607
|
+
let duplicatesRemoved = universe.data.duplicatesRemoved;
|
|
569
608
|
const prior = priorWikilinkTargets(currentText);
|
|
570
|
-
const
|
|
609
|
+
const knownTargets = universe.data.knownTargets;
|
|
610
|
+
const knownBasenames = new Set(
|
|
611
|
+
[...knownTargets].map((target) => target.split("/").pop().toLowerCase())
|
|
612
|
+
);
|
|
571
613
|
const priorLower = /* @__PURE__ */ new Map();
|
|
572
614
|
for (const t of prior) {
|
|
573
615
|
const k = t.toLowerCase();
|
|
@@ -585,15 +627,10 @@ async function renderRootIndex(input) {
|
|
|
585
627
|
const ghostsRemoved = [
|
|
586
628
|
...new Set(
|
|
587
629
|
prior.filter((t) => {
|
|
588
|
-
const bare = t.includes("/") ? t : null;
|
|
589
|
-
if (bare && !generatedTargets.has(bare) && !generatedTargets.has(t)) {
|
|
590
|
-
if (!seen.has(t)) return true;
|
|
591
|
-
}
|
|
592
630
|
if (!t.includes("/")) {
|
|
593
|
-
|
|
594
|
-
return matches.length === 0;
|
|
631
|
+
return !knownBasenames.has(t.toLowerCase());
|
|
595
632
|
}
|
|
596
|
-
return !
|
|
633
|
+
return !knownTargets.has(t);
|
|
597
634
|
})
|
|
598
635
|
)
|
|
599
636
|
];
|
|
@@ -603,7 +640,7 @@ async function renderRootIndex(input) {
|
|
|
603
640
|
"Generated by `skillwiki index rebuild`. Generated sections are derived from typed-page frontmatter and project manifests.",
|
|
604
641
|
""
|
|
605
642
|
];
|
|
606
|
-
for (const section of
|
|
643
|
+
for (const section of ROOT_INDEX_SECTION_ORDER) {
|
|
607
644
|
lines.push(`## ${section}`, "");
|
|
608
645
|
const sectionEntries = entries.filter((e) => e.section === section);
|
|
609
646
|
for (const e of sectionEntries) {
|
|
@@ -642,6 +679,7 @@ export {
|
|
|
642
679
|
scanVault,
|
|
643
680
|
readPage,
|
|
644
681
|
readPageCached,
|
|
682
|
+
buildRootIndexUniverse,
|
|
645
683
|
renderRootIndex,
|
|
646
684
|
writeRootIndexProjection
|
|
647
685
|
};
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-7I2TPIV5.js";
|
|
6
6
|
import {
|
|
7
7
|
atomicWriteText,
|
|
8
|
+
buildRootIndexUniverse,
|
|
8
9
|
extractFrontmatter,
|
|
9
10
|
mapWithConcurrency,
|
|
10
11
|
prepareTypedPage,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
splitFrontmatter,
|
|
19
20
|
vaultIoConcurrency,
|
|
20
21
|
writeRootIndexProjection
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-U34B2XQJ.js";
|
|
22
23
|
import {
|
|
23
24
|
CONFIG_KEYS,
|
|
24
25
|
isValidWikiProfileKey,
|
|
@@ -1561,8 +1562,11 @@ function normalizeIndexTarget(raw) {
|
|
|
1561
1562
|
return raw.replace(/\.md$/, "").replace(/^\.?\//, "");
|
|
1562
1563
|
}
|
|
1563
1564
|
async function runIndexCheck(input) {
|
|
1564
|
-
const
|
|
1565
|
-
if (!
|
|
1565
|
+
const universe = await buildRootIndexUniverse({ vault: input.vault, scan: input.scan });
|
|
1566
|
+
if (!universe.ok) {
|
|
1567
|
+
const exitCode = universe.error === "VAULT_PATH_INVALID" ? ExitCode.VAULT_PATH_INVALID : ExitCode.SCHEME_REJECTED;
|
|
1568
|
+
return { exitCode, result: universe };
|
|
1569
|
+
}
|
|
1566
1570
|
let indexText = "";
|
|
1567
1571
|
try {
|
|
1568
1572
|
indexText = await readFile6(join10(input.vault, "index.md"), "utf8");
|
|
@@ -1579,25 +1583,22 @@ async function runIndexCheck(input) {
|
|
|
1579
1583
|
indexBare.set(bare, list);
|
|
1580
1584
|
}
|
|
1581
1585
|
const required = /* @__PURE__ */ new Map();
|
|
1582
|
-
const
|
|
1583
|
-
|
|
1584
|
-
const target = p.relPath.replace(/\.md$/, "");
|
|
1585
|
-
required.set(target, p.relPath);
|
|
1586
|
-
known.add(target);
|
|
1586
|
+
for (const entry of universe.data.required) {
|
|
1587
|
+
required.set(entry.target, `${entry.target}.md`);
|
|
1587
1588
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1589
|
+
const requiredBasenameCounts = /* @__PURE__ */ new Map();
|
|
1590
|
+
for (const target of required.keys()) {
|
|
1591
|
+
const basename3 = target.split("/").pop().toLowerCase();
|
|
1592
|
+
requiredBasenameCounts.set(basename3, (requiredBasenameCounts.get(basename3) ?? 0) + 1);
|
|
1590
1593
|
}
|
|
1594
|
+
const known = universe.data.knownTargets;
|
|
1591
1595
|
const missing_from_index = [];
|
|
1592
1596
|
for (const [target, relPath] of required.entries()) {
|
|
1593
1597
|
if (indexTargets.has(target)) continue;
|
|
1594
1598
|
const bare = target.split("/").pop().toLowerCase();
|
|
1595
1599
|
const bareHits = indexBare.get(bare) ?? [];
|
|
1596
1600
|
const basenameOnly = bareHits.filter((t) => !t.includes("/"));
|
|
1597
|
-
|
|
1598
|
-
(t) => t.split("/").pop().toLowerCase() === bare
|
|
1599
|
-
);
|
|
1600
|
-
if (basenameOnly.length === 1 && sameNameRequired.length === 1) continue;
|
|
1601
|
+
if (basenameOnly.length === 1 && requiredBasenameCounts.get(bare) === 1) continue;
|
|
1601
1602
|
missing_from_index.push(relPath);
|
|
1602
1603
|
}
|
|
1603
1604
|
const ghost_entries = [];
|
package/dist/cli.js
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
satelliteLatestRunPath,
|
|
69
69
|
taxonomyCommentForPage,
|
|
70
70
|
upsertIndexEntry
|
|
71
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-Z3WS45PL.js";
|
|
72
72
|
import {
|
|
73
73
|
normalizeDistTag,
|
|
74
74
|
readCache,
|
|
@@ -88,13 +88,13 @@ import {
|
|
|
88
88
|
scanVault,
|
|
89
89
|
splitFrontmatter,
|
|
90
90
|
writeRootIndexProjection
|
|
91
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-U34B2XQJ.js";
|
|
92
92
|
import {
|
|
93
93
|
acquireManagedWriteLock,
|
|
94
94
|
releaseManagedWriteLock,
|
|
95
95
|
runManagedWritePreflight,
|
|
96
96
|
runManagedWriteTransaction
|
|
97
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-QH5MAV74.js";
|
|
98
98
|
import {
|
|
99
99
|
FLEET_REL_PATH,
|
|
100
100
|
git,
|
|
@@ -2533,7 +2533,7 @@ ${fmRewritten}
|
|
|
2533
2533
|
await rename2(join17(input.vault, relPath), join17(input.vault, archivePath));
|
|
2534
2534
|
let indexUpdated = false;
|
|
2535
2535
|
if (!isRaw) {
|
|
2536
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
2536
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-VNJ3TLEK.js");
|
|
2537
2537
|
const before = await readFile7(join17(input.vault, "index.md"), "utf8").catch(() => "");
|
|
2538
2538
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
2539
2539
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
@@ -2644,7 +2644,7 @@ async function runRemove(input) {
|
|
|
2644
2644
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
2645
2645
|
const { readFile: readFile17 } = await import("fs/promises");
|
|
2646
2646
|
const { join: pathJoin } = await import("path");
|
|
2647
|
-
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-
|
|
2647
|
+
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-VNJ3TLEK.js");
|
|
2648
2648
|
const before = await readFile17(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
2649
2649
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
2650
2650
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
@@ -6749,11 +6749,12 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
6749
6749
|
if (guard.blocked) {
|
|
6750
6750
|
return emit({ exitCode: guard.exitCode, result: guard.result }, void 0, { postCommit: false });
|
|
6751
6751
|
}
|
|
6752
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
6752
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-DTNXZBLI.js");
|
|
6753
6753
|
const run = await runManagedWriteTransaction2({
|
|
6754
6754
|
vault,
|
|
6755
6755
|
command,
|
|
6756
6756
|
allowImmutableRecord: opts?.allowImmutableRecord === true,
|
|
6757
|
+
convergenceVault: opts?.convergenceVault,
|
|
6757
6758
|
mutate: async (receipt) => await mutate(receipt)
|
|
6758
6759
|
});
|
|
6759
6760
|
return emit(run, vault, opts);
|
|
@@ -6982,26 +6983,28 @@ logCmd.command("materialize [vault]").description("preview or write root log.md
|
|
|
6982
6983
|
);
|
|
6983
6984
|
} else emit(await runLogMaterialize({ vault: v.vault, write: false }), v.vault);
|
|
6984
6985
|
});
|
|
6985
|
-
logCmd.command("migrate-legacy [vault]").description("backfill immutable events from historical root log.md blocks").option("--write", "write events", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
6986
|
+
logCmd.command("migrate-legacy [vault]").description("backfill immutable events from historical root log.md blocks").option("--write", "write events", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
6986
6987
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
6987
6988
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
6988
6989
|
else if (opts.write) {
|
|
6989
6990
|
return emitManagedVaultWrite(
|
|
6990
6991
|
v.vault,
|
|
6991
6992
|
"log migrate-legacy",
|
|
6992
|
-
() => runLogMigrateLegacy({ vault: v.vault, write: true })
|
|
6993
|
+
() => runLogMigrateLegacy({ vault: v.vault, write: true }),
|
|
6994
|
+
{ convergenceVault: opts.convergeVault }
|
|
6993
6995
|
);
|
|
6994
6996
|
} else emit(await runLogMigrateLegacy({ vault: v.vault, write: false }), v.vault);
|
|
6995
6997
|
});
|
|
6996
6998
|
var projectionsCmd = program.command("projections").description("root index/log projection materialization");
|
|
6997
|
-
projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
6999
|
+
projectionsCmd.command("materialize [vault]").description("preview or write root index.md and log.md as a pair").option("--write", "write projections", false).option("--converge-vault <dir>", "Git vault used for managed pull and base-OID proof").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
6998
7000
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
6999
7001
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
7000
7002
|
else if (opts.write) {
|
|
7001
7003
|
return emitManagedVaultWrite(
|
|
7002
7004
|
v.vault,
|
|
7003
7005
|
"projections materialize",
|
|
7004
|
-
() => runProjectionsMaterialize({ vault: v.vault, write: true })
|
|
7006
|
+
() => runProjectionsMaterialize({ vault: v.vault, write: true }),
|
|
7007
|
+
{ convergenceVault: opts.convergeVault }
|
|
7005
7008
|
);
|
|
7006
7009
|
} else emit(await runProjectionsMaterialize({ vault: v.vault, write: false }), v.vault);
|
|
7007
7010
|
});
|
package/dist/skillwiki-mcp.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runSkillwikiMcpStdio
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Z3WS45PL.js";
|
|
5
5
|
import "./chunk-7I2TPIV5.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-U34B2XQJ.js";
|
|
7
7
|
import "./chunk-R6BKJWVC.js";
|
|
8
8
|
import "./chunk-C5OLZRRM.js";
|
|
9
9
|
|
|
@@ -308,9 +308,35 @@ fi
|
|
|
308
308
|
|
|
309
309
|
refresh_git_baseline
|
|
310
310
|
|
|
311
|
+
# Dual-path requires distinct live mutation vs Git convergence roots.
|
|
312
|
+
# Same-path would materialize projections then immediately rclone-overwrite them.
|
|
313
|
+
if [ "$(cd "$WIKI_DIR" 2>/dev/null && pwd -P)" = "$(cd "$SNAPSHOT_WORKTREE" 2>/dev/null && pwd -P)" ]; then
|
|
314
|
+
log "ERROR: WIKI_DIR and WIKI_GIT_WORKTREE must be distinct paths for dual-path projection (got: $WIKI_DIR)"
|
|
315
|
+
exit 1
|
|
316
|
+
fi
|
|
317
|
+
|
|
311
318
|
# Single-authority root projections before FUSE/S3 pull promotion.
|
|
312
|
-
|
|
313
|
-
|
|
319
|
+
# Mutation target is the live vault ($WIKI_DIR); Git pull/base-OID use
|
|
320
|
+
# $SNAPSHOT_WORKTREE so FUSE/S3 hosts without a local Git HEAD still work.
|
|
321
|
+
SKILLWIKI_BIN="${WIKI_SNAPSHOT_SKILLWIKI_BIN:-skillwiki}"
|
|
322
|
+
if command -v "$SKILLWIKI_BIN" >/dev/null 2>&1; then
|
|
323
|
+
case "${WIKI_SNAPSHOT_MIGRATE_LEGACY:-0}" in
|
|
324
|
+
0) ;;
|
|
325
|
+
1)
|
|
326
|
+
if ! "$SKILLWIKI_BIN" log migrate-legacy "$WIKI_DIR" --write \
|
|
327
|
+
--converge-vault "$SNAPSHOT_WORKTREE" >>"$LOG_FILE" 2>&1; then
|
|
328
|
+
log "FAIL legacy log migration; snapshot promotion refused"
|
|
329
|
+
exit 1
|
|
330
|
+
fi
|
|
331
|
+
log "OK legacy log migration before projection (attended one-run)"
|
|
332
|
+
;;
|
|
333
|
+
*)
|
|
334
|
+
log "ERROR: WIKI_SNAPSHOT_MIGRATE_LEGACY must be 0 or 1 (got: ${WIKI_SNAPSHOT_MIGRATE_LEGACY})"
|
|
335
|
+
exit 1
|
|
336
|
+
;;
|
|
337
|
+
esac
|
|
338
|
+
if ! "$SKILLWIKI_BIN" projections materialize "$WIKI_DIR" --write \
|
|
339
|
+
--converge-vault "$SNAPSHOT_WORKTREE" >>"$LOG_FILE" 2>&1; then
|
|
314
340
|
log "FAIL root projection materialization; snapshot promotion refused"
|
|
315
341
|
exit 1
|
|
316
342
|
fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
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": {
|