skillwiki 0.10.100 → 0.10.103
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-3QT3OFAL.js → chunk-2BPOEEMW.js} +321 -192
- package/dist/{chunk-B4QTKCEK.js → chunk-PVIOU7BN.js} +7 -2
- package/dist/cli.js +579 -289
- package/dist/{managed-write-preflight-32523BPV.js → managed-write-preflight-ATDFVLJV.js} +1 -1
- package/dist/skillwiki-mcp.js +2 -2
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +2 -2
- package/skills/.codex-plugin/plugin.json +3 -3
- package/skills/.cursor-plugin/plugin.json +2 -2
- package/skills/README.md +2 -2
- package/skills/package.json +2 -2
- package/skills/skills/skillwiki-connect/SKILL.md +75 -0
- package/skills/skills/skillwiki-mcp/SKILL.md +4 -4
- package/skills/skills/using-skillwiki/SKILL.md +5 -3
- package/skills/skills/using-skillwiki/activation.md +1 -0
- package/skills/skillwiki-connect/SKILL.md +75 -0
- package/skills/skillwiki-mcp/SKILL.md +4 -4
- package/skills/using-skillwiki/SKILL.md +5 -3
- package/skills/using-skillwiki/activation.md +1 -0
package/dist/cli.js
CHANGED
|
@@ -3,8 +3,12 @@ import {
|
|
|
3
3
|
CAPTURE_HYGIENE_CONTRACT,
|
|
4
4
|
DEFAULT_CAPTURE_BUDGET,
|
|
5
5
|
DEFAULT_DIRTY_VOLUME_THRESHOLD,
|
|
6
|
+
DEFAULT_MCP_URL,
|
|
6
7
|
DEFAULT_NO_DECISION_STREAK,
|
|
7
8
|
GateError,
|
|
9
|
+
MCP_HOST_ID_ENV,
|
|
10
|
+
MCP_TOKEN_ENV,
|
|
11
|
+
MCP_URL_ENV,
|
|
8
12
|
PUSH_NOT_IN_PROFILE,
|
|
9
13
|
SATELLITE_STALE_MS,
|
|
10
14
|
buildVectorIndex,
|
|
@@ -12,17 +16,21 @@ import {
|
|
|
12
16
|
countEventLedgerFiles,
|
|
13
17
|
detectFuseMount,
|
|
14
18
|
evaluateDirtyVolumeGate,
|
|
15
|
-
|
|
19
|
+
evaluateSatelliteRunHealthState,
|
|
16
20
|
findPlugin,
|
|
17
21
|
guardProtectedVaultWrite,
|
|
18
22
|
isEventLedgerPath,
|
|
19
23
|
isFailedRunStatus,
|
|
20
24
|
measureDirtyVolume,
|
|
21
25
|
memoryAuthorityTiersRank,
|
|
26
|
+
mergeMcpAuthIntoEnv,
|
|
27
|
+
nonEmptyEnvValue,
|
|
22
28
|
pruneVectorIndex,
|
|
23
29
|
readCliPackageJson,
|
|
30
|
+
readSatelliteLatestRunAt,
|
|
24
31
|
readSatelliteLatestRunFromText,
|
|
25
32
|
readVaultSyncConfig,
|
|
33
|
+
redactMcpSecret,
|
|
26
34
|
reindexPageInVectorIndex,
|
|
27
35
|
renderIndexUpsert,
|
|
28
36
|
renderProjectIndex,
|
|
@@ -55,7 +63,7 @@ import {
|
|
|
55
63
|
snapshotterHealthChecks,
|
|
56
64
|
upsertIndexEntry,
|
|
57
65
|
vectorIndexStatus
|
|
58
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-2BPOEEMW.js";
|
|
59
67
|
import {
|
|
60
68
|
normalizeDistTag,
|
|
61
69
|
readCache,
|
|
@@ -160,7 +168,7 @@ import {
|
|
|
160
168
|
supersedeStaleReviewRequiredJournals,
|
|
161
169
|
taxonomyCommentForPage,
|
|
162
170
|
writeDotenv
|
|
163
|
-
} from "./chunk-
|
|
171
|
+
} from "./chunk-PVIOU7BN.js";
|
|
164
172
|
import {
|
|
165
173
|
assertTargetInsideVault,
|
|
166
174
|
atomicWriteText,
|
|
@@ -214,7 +222,7 @@ import {
|
|
|
214
222
|
} from "./chunk-QM64LUEY.js";
|
|
215
223
|
|
|
216
224
|
// src/cli.ts
|
|
217
|
-
import { join as
|
|
225
|
+
import { join as join45 } from "path";
|
|
218
226
|
import { Command } from "commander";
|
|
219
227
|
|
|
220
228
|
// src/utils/output.ts
|
|
@@ -3576,13 +3584,334 @@ async function runHealth(input) {
|
|
|
3576
3584
|
return { exitCode, result: ok(report) };
|
|
3577
3585
|
}
|
|
3578
3586
|
|
|
3587
|
+
// src/commands/connect.ts
|
|
3588
|
+
import { chmod, mkdir as mkdir5, readFile as readFile6, stat as stat3 } from "fs/promises";
|
|
3589
|
+
import { join as join18 } from "path";
|
|
3590
|
+
|
|
3591
|
+
// src/utils/mcp-token-map.ts
|
|
3592
|
+
import { createHash as createHash2, randomBytes } from "crypto";
|
|
3593
|
+
import yaml from "js-yaml";
|
|
3594
|
+
var HOST_ID_RE = /^[a-z][a-z0-9-]{1,62}$/;
|
|
3595
|
+
function writerIdFromValue(value) {
|
|
3596
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
3597
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
3598
|
+
const rec = value;
|
|
3599
|
+
if (typeof rec.writer_id === "string" && rec.writer_id.length > 0) return rec.writer_id;
|
|
3600
|
+
if (typeof rec.host_id === "string" && rec.host_id.length > 0) return rec.host_id;
|
|
3601
|
+
}
|
|
3602
|
+
return void 0;
|
|
3603
|
+
}
|
|
3604
|
+
function parseAllowedVaultIds(raw) {
|
|
3605
|
+
if (!raw || raw.length === 0) return { vaults: [] };
|
|
3606
|
+
const vaults = [];
|
|
3607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3608
|
+
for (const item of raw) {
|
|
3609
|
+
const id = item.trim();
|
|
3610
|
+
if (!id) continue;
|
|
3611
|
+
if (id.includes("*") || id.includes("/") || !HOST_ID_RE.test(id)) {
|
|
3612
|
+
return { error: "INVALID_ALLOWED_VAULTS" };
|
|
3613
|
+
}
|
|
3614
|
+
if (seen.has(id)) continue;
|
|
3615
|
+
seen.add(id);
|
|
3616
|
+
vaults.push(id);
|
|
3617
|
+
}
|
|
3618
|
+
return { vaults };
|
|
3619
|
+
}
|
|
3620
|
+
function parseMcpTokenMap(yamlText) {
|
|
3621
|
+
const parsed = yaml.load(yamlText);
|
|
3622
|
+
const map = /* @__PURE__ */ new Map();
|
|
3623
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3624
|
+
return map;
|
|
3625
|
+
}
|
|
3626
|
+
const records = parsed;
|
|
3627
|
+
const nested = records.tokens;
|
|
3628
|
+
const source = nested && typeof nested === "object" && !Array.isArray(nested) ? nested : records;
|
|
3629
|
+
for (const [hash, value] of Object.entries(source)) {
|
|
3630
|
+
if (hash === "tokens") continue;
|
|
3631
|
+
const writerId = writerIdFromValue(value);
|
|
3632
|
+
if (!writerId) continue;
|
|
3633
|
+
const normalized = hash.trim().toLowerCase();
|
|
3634
|
+
if (!/^[0-9a-f]{64}$/.test(normalized)) continue;
|
|
3635
|
+
map.set(normalized, writerId);
|
|
3636
|
+
}
|
|
3637
|
+
return map;
|
|
3638
|
+
}
|
|
3639
|
+
function generateHostBearer(rng) {
|
|
3640
|
+
const bytes = (rng ?? (() => randomBytes(32)))();
|
|
3641
|
+
const raw = bytes.toString("base64url");
|
|
3642
|
+
const hashHex = createHash2("sha256").update(raw, "utf8").digest("hex");
|
|
3643
|
+
return { raw, hashHex };
|
|
3644
|
+
}
|
|
3645
|
+
function formatHostRow(hashHex, hostId, allowedVaults) {
|
|
3646
|
+
if (allowedVaults.length === 0) {
|
|
3647
|
+
return `${hashHex}: ${hostId}
|
|
3648
|
+
`;
|
|
3649
|
+
}
|
|
3650
|
+
return `${hashHex}:
|
|
3651
|
+
writer_id: ${hostId}
|
|
3652
|
+
allowed_vaults: [${allowedVaults.join(", ")}]
|
|
3653
|
+
`;
|
|
3654
|
+
}
|
|
3655
|
+
function appendHostHash(yamlText, hashHex, hostId, allowedVaults) {
|
|
3656
|
+
if (!HOST_ID_RE.test(hostId)) {
|
|
3657
|
+
return { error: "INVALID_HOST_ID" };
|
|
3658
|
+
}
|
|
3659
|
+
const granted = parseAllowedVaultIds(allowedVaults);
|
|
3660
|
+
if ("error" in granted) return granted;
|
|
3661
|
+
const map = parseMcpTokenMap(yamlText);
|
|
3662
|
+
const normalizedHash = hashHex.trim().toLowerCase();
|
|
3663
|
+
if ([...map.values()].includes(hostId)) {
|
|
3664
|
+
return { error: "DUPLICATE_HOST_ID" };
|
|
3665
|
+
}
|
|
3666
|
+
if (map.has(normalizedHash)) {
|
|
3667
|
+
return { error: "DUPLICATE_HASH" };
|
|
3668
|
+
}
|
|
3669
|
+
return { yaml: yamlText.replace(/\s*$/, "") + `
|
|
3670
|
+
${formatHostRow(normalizedHash, hostId, granted.vaults)}` };
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
// src/commands/connect.ts
|
|
3674
|
+
var CONNECT_DESCRIPTION = "HTTP MCP leaf connect: ingest a chat-attached env file into ~/.skillwiki/.env (mode 0600). Never runs skillwiki init. Cloud Drive / \u4E91\u76D8 is unsupported.";
|
|
3675
|
+
var RESERVED_HOST_IDS = ["msi", "doubao-linux-agent", "macos-dev"];
|
|
3676
|
+
var DEST_MODE = 384;
|
|
3677
|
+
function isUnsupportedSecretPath(filePath) {
|
|
3678
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
3679
|
+
return /(?:^|\/)(?:Google Drive|OneDrive|iCloud Drive|CloudStorage|云盘|百度网盘|坚果云|Dropbox)(?:\/|$)/i.test(normalized) || /clouddrive|cloud-disk/i.test(normalized);
|
|
3680
|
+
}
|
|
3681
|
+
function fail(code, error, detail) {
|
|
3682
|
+
return { exitCode: code, result: err(error, detail) };
|
|
3683
|
+
}
|
|
3684
|
+
async function defaultReadStdin() {
|
|
3685
|
+
const chunks = [];
|
|
3686
|
+
for await (const chunk of process.stdin) {
|
|
3687
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3688
|
+
}
|
|
3689
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3690
|
+
}
|
|
3691
|
+
function looksLikeVaultInit(text) {
|
|
3692
|
+
return /^#\s*Vault Schema/im.test(text) || /^\s*---\s*$/m.test(text) && /taxonomy:/i.test(text);
|
|
3693
|
+
}
|
|
3694
|
+
function tokenPresence(token) {
|
|
3695
|
+
return token ? "TOKEN_SET" : "EMPTY";
|
|
3696
|
+
}
|
|
3697
|
+
function summaryHint(data) {
|
|
3698
|
+
return [
|
|
3699
|
+
data.token,
|
|
3700
|
+
`auth=${data.auth}`,
|
|
3701
|
+
`writer_id=${data.writer_id ?? "n/a"}`,
|
|
3702
|
+
`ok=${data.ok ?? "n/a"}`,
|
|
3703
|
+
`reconcile_ready=${data.reconcile_ready ?? "n/a"}`,
|
|
3704
|
+
data.dry_run ? "dry-run" : data.written ? "wrote ~/.skillwiki/.env 0600" : "not written"
|
|
3705
|
+
].join(" ");
|
|
3706
|
+
}
|
|
3707
|
+
function redactConnectPayload(data, secret) {
|
|
3708
|
+
return {
|
|
3709
|
+
...data,
|
|
3710
|
+
handshake: data.handshake ? redactMcpSecret(data.handshake, secret) : data.handshake,
|
|
3711
|
+
humanHint: redactMcpSecret(data.humanHint, secret)
|
|
3712
|
+
};
|
|
3713
|
+
}
|
|
3714
|
+
async function readWikiStatus(fetchFn, url, token) {
|
|
3715
|
+
const headers = {
|
|
3716
|
+
Authorization: `Bearer ${token}`,
|
|
3717
|
+
"Content-Type": "application/json",
|
|
3718
|
+
Accept: "application/json, text/event-stream"
|
|
3719
|
+
};
|
|
3720
|
+
try {
|
|
3721
|
+
const initRes = await fetchFn(url, {
|
|
3722
|
+
method: "POST",
|
|
3723
|
+
headers,
|
|
3724
|
+
body: JSON.stringify({
|
|
3725
|
+
jsonrpc: "2.0",
|
|
3726
|
+
id: 1,
|
|
3727
|
+
method: "initialize",
|
|
3728
|
+
params: {
|
|
3729
|
+
protocolVersion: "2025-11-25",
|
|
3730
|
+
capabilities: {},
|
|
3731
|
+
clientInfo: { name: "skillwiki-connect", version: "0" }
|
|
3732
|
+
}
|
|
3733
|
+
})
|
|
3734
|
+
});
|
|
3735
|
+
const session = initRes.headers.get("mcp-session-id");
|
|
3736
|
+
if (session) headers["mcp-session-id"] = session;
|
|
3737
|
+
const statusRes = await fetchFn(url, {
|
|
3738
|
+
method: "POST",
|
|
3739
|
+
headers,
|
|
3740
|
+
body: JSON.stringify({
|
|
3741
|
+
jsonrpc: "2.0",
|
|
3742
|
+
id: 2,
|
|
3743
|
+
method: "tools/call",
|
|
3744
|
+
params: { name: "wiki_status", arguments: {} }
|
|
3745
|
+
})
|
|
3746
|
+
});
|
|
3747
|
+
const raw = await statusRes.text();
|
|
3748
|
+
const parsed = JSON.parse(raw.startsWith("data:") ? raw.replace(/^data:\s*/m, "") : raw);
|
|
3749
|
+
const structured = parsed.result?.structuredContent;
|
|
3750
|
+
let body = structured;
|
|
3751
|
+
if (!body) {
|
|
3752
|
+
const text = parsed.result?.content?.find((c) => typeof c.text === "string")?.text;
|
|
3753
|
+
if (text) {
|
|
3754
|
+
try {
|
|
3755
|
+
body = JSON.parse(text);
|
|
3756
|
+
} catch {
|
|
3757
|
+
body = void 0;
|
|
3758
|
+
}
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
if (!body) return { writer_id: null, ok: null, reconcile_ready: null };
|
|
3762
|
+
return {
|
|
3763
|
+
writer_id: typeof body.writer_id === "string" ? body.writer_id : null,
|
|
3764
|
+
ok: typeof body.ok === "boolean" ? body.ok : null,
|
|
3765
|
+
reconcile_ready: typeof body.reconcile_ready === "boolean" ? body.reconcile_ready : null
|
|
3766
|
+
};
|
|
3767
|
+
} catch {
|
|
3768
|
+
return { writer_id: null, ok: null, reconcile_ready: null };
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
async function runConnect(input) {
|
|
3772
|
+
const dest = join18(input.home, ".skillwiki", ".env");
|
|
3773
|
+
const fromFile = input.fromFile?.trim();
|
|
3774
|
+
const fromStdin = Boolean(input.fromStdin);
|
|
3775
|
+
if (fromFile && fromStdin) {
|
|
3776
|
+
return fail(ExitCode.USAGE, "USAGE", { reason: "pass --from-file or --from-stdin, not both" });
|
|
3777
|
+
}
|
|
3778
|
+
if (!fromFile && !fromStdin) {
|
|
3779
|
+
return fail(ExitCode.USAGE, "USAGE", {
|
|
3780
|
+
reason: "pass --from-file <chat-attachment> or --from-stdin (Cloud Drive / \u4E91\u76D8 unsupported)"
|
|
3781
|
+
});
|
|
3782
|
+
}
|
|
3783
|
+
if (fromFile && isUnsupportedSecretPath(fromFile)) {
|
|
3784
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "DRIVE_UNSUPPORTED", {
|
|
3785
|
+
reason: "Cloud Drive / \u4E91\u76D8 is not a secret path. Attach the env file in chat, then skillwiki connect --from-file <attachment>."
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
let raw;
|
|
3789
|
+
try {
|
|
3790
|
+
raw = fromStdin ? await (input.readStdin ?? defaultReadStdin)() : await readFile6(fromFile, "utf8");
|
|
3791
|
+
} catch (e) {
|
|
3792
|
+
const code = e.code;
|
|
3793
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
3794
|
+
return fail(ExitCode.FILE_NOT_FOUND, "FILE_NOT_FOUND", { path: fromFile });
|
|
3795
|
+
}
|
|
3796
|
+
if (code === "EISDIR") {
|
|
3797
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "NOT_ENV_FILE", { reason: "source is a directory, not an env file" });
|
|
3798
|
+
}
|
|
3799
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "READ_FAILED", { reason: "could not read env source" });
|
|
3800
|
+
}
|
|
3801
|
+
if (looksLikeVaultInit(raw)) {
|
|
3802
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "REFUSE_INIT", {
|
|
3803
|
+
reason: "source looks like a vault init; skillwiki connect never runs skillwiki init"
|
|
3804
|
+
});
|
|
3805
|
+
}
|
|
3806
|
+
const parsed = parseDotenvText(raw);
|
|
3807
|
+
const incomingToken = nonEmptyEnvValue(parsed[MCP_TOKEN_ENV]);
|
|
3808
|
+
const incomingHost = nonEmptyEnvValue(parsed[MCP_HOST_ID_ENV]);
|
|
3809
|
+
const incomingUrl = nonEmptyEnvValue(parsed[MCP_URL_ENV]);
|
|
3810
|
+
if (!incomingToken) {
|
|
3811
|
+
return fail(ExitCode.INVALID_CONFIG_VALUE, "TOKEN_EMPTY", {
|
|
3812
|
+
reason: "SKILLWIKI_MCP_TOKEN missing or empty (value not shown)"
|
|
3813
|
+
});
|
|
3814
|
+
}
|
|
3815
|
+
if (!incomingHost) {
|
|
3816
|
+
return fail(ExitCode.INVALID_CONFIG_VALUE, "HOST_ID_REQUIRED", {
|
|
3817
|
+
reason: "SKILLWIKI_HOST_ID missing; each machine needs a unique host-id"
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3820
|
+
if (!HOST_ID_RE.test(incomingHost)) {
|
|
3821
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "INVALID_HOST_ID", { reason: "host-id must match [a-z][a-z0-9-]{1,62}" });
|
|
3822
|
+
}
|
|
3823
|
+
if (RESERVED_HOST_IDS.includes(incomingHost) && !input.force) {
|
|
3824
|
+
return fail(ExitCode.PREFLIGHT_FAILED, "RESERVED_HOST_ID", {
|
|
3825
|
+
reason: `refuse copying reserved host-id ${incomingHost}; issue a unique id on metal or pass --force`
|
|
3826
|
+
});
|
|
3827
|
+
}
|
|
3828
|
+
const existing = await parseDotenvFile(dest);
|
|
3829
|
+
const existingToken = nonEmptyEnvValue(existing[MCP_TOKEN_ENV]);
|
|
3830
|
+
if (existingToken && existingToken !== incomingToken && !input.force) {
|
|
3831
|
+
return fail(ExitCode.ENV_WRITE_CONFLICT, "ENV_WRITE_CONFLICT", {
|
|
3832
|
+
reason: "existing ~/.skillwiki/.env has a different token; pass --force to overwrite (value not shown)"
|
|
3833
|
+
});
|
|
3834
|
+
}
|
|
3835
|
+
const entries = {
|
|
3836
|
+
[MCP_TOKEN_ENV]: incomingToken,
|
|
3837
|
+
[MCP_HOST_ID_ENV]: incomingHost
|
|
3838
|
+
};
|
|
3839
|
+
if (incomingUrl) entries[MCP_URL_ENV] = incomingUrl;
|
|
3840
|
+
const dryRun = Boolean(input.dryRun);
|
|
3841
|
+
if (!dryRun) {
|
|
3842
|
+
await mkdir5(join18(input.home, ".skillwiki"), { recursive: true });
|
|
3843
|
+
let original;
|
|
3844
|
+
try {
|
|
3845
|
+
original = await readFile6(dest, "utf8");
|
|
3846
|
+
} catch {
|
|
3847
|
+
original = void 0;
|
|
3848
|
+
}
|
|
3849
|
+
await writeDotenv(dest, entries, original);
|
|
3850
|
+
await chmod(dest, DEST_MODE);
|
|
3851
|
+
try {
|
|
3852
|
+
const st = await stat3(dest);
|
|
3853
|
+
if ((st.mode & 511) !== DEST_MODE) await chmod(dest, DEST_MODE);
|
|
3854
|
+
} catch {
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
let writerId = null;
|
|
3858
|
+
let statusOk = null;
|
|
3859
|
+
let reconcileReady = null;
|
|
3860
|
+
let handshake = null;
|
|
3861
|
+
let doctorExit = ExitCode.OK;
|
|
3862
|
+
const checkMcp = input.checkMcp ?? !dryRun;
|
|
3863
|
+
if (checkMcp) {
|
|
3864
|
+
const env = await mergeMcpAuthIntoEnv(input.home, input.env ?? {});
|
|
3865
|
+
if (!nonEmptyEnvValue(env[MCP_TOKEN_ENV])) env[MCP_TOKEN_ENV] = incomingToken;
|
|
3866
|
+
if (!nonEmptyEnvValue(env[MCP_HOST_ID_ENV])) env[MCP_HOST_ID_ENV] = incomingHost;
|
|
3867
|
+
if (incomingUrl && !nonEmptyEnvValue(env[MCP_URL_ENV])) env[MCP_URL_ENV] = incomingUrl;
|
|
3868
|
+
const doctor = await runDoctor({
|
|
3869
|
+
home: input.home,
|
|
3870
|
+
envValue: void 0,
|
|
3871
|
+
argv: ["node", "skillwiki", "connect"],
|
|
3872
|
+
currentVersion: input.currentVersion,
|
|
3873
|
+
checkMcp: true,
|
|
3874
|
+
env,
|
|
3875
|
+
mcpFetch: input.mcpFetch
|
|
3876
|
+
});
|
|
3877
|
+
doctorExit = doctor.exitCode;
|
|
3878
|
+
if (doctor.result.ok) {
|
|
3879
|
+
handshake = doctor.result.data.checks.find((c) => c.id === "mcp_handshake")?.detail ?? null;
|
|
3880
|
+
}
|
|
3881
|
+
const url = incomingUrl ?? DEFAULT_MCP_URL;
|
|
3882
|
+
const status = await readWikiStatus(input.mcpFetch ?? globalThis.fetch, url, incomingToken);
|
|
3883
|
+
writerId = status.writer_id;
|
|
3884
|
+
statusOk = status.ok;
|
|
3885
|
+
reconcileReady = status.reconcile_ready;
|
|
3886
|
+
}
|
|
3887
|
+
const payload = {
|
|
3888
|
+
dest,
|
|
3889
|
+
written: !dryRun,
|
|
3890
|
+
dry_run: dryRun,
|
|
3891
|
+
mode: "0600",
|
|
3892
|
+
token: tokenPresence(incomingToken),
|
|
3893
|
+
auth: incomingToken ? "present" : "not set",
|
|
3894
|
+
writer_id: writerId,
|
|
3895
|
+
ok: statusOk,
|
|
3896
|
+
reconcile_ready: reconcileReady,
|
|
3897
|
+
handshake,
|
|
3898
|
+
humanHint: ""
|
|
3899
|
+
};
|
|
3900
|
+
payload.humanHint = summaryHint(payload);
|
|
3901
|
+
const safe = redactConnectPayload(payload, incomingToken);
|
|
3902
|
+
if (!dryRun && checkMcp && doctorExit !== ExitCode.OK && doctorExit !== ExitCode.DOCTOR_HAS_WARNINGS) {
|
|
3903
|
+
return { exitCode: doctorExit, result: ok(safe) };
|
|
3904
|
+
}
|
|
3905
|
+
return { exitCode: ExitCode.OK, result: ok(safe) };
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3579
3908
|
// src/commands/archive.ts
|
|
3580
|
-
import { rename as rename2, mkdir as
|
|
3581
|
-
import { join as
|
|
3909
|
+
import { rename as rename2, mkdir as mkdir7, readFile as readFile8 } from "fs/promises";
|
|
3910
|
+
import { join as join20, dirname as dirname5 } from "path";
|
|
3582
3911
|
|
|
3583
3912
|
// src/utils/delete-intent.ts
|
|
3584
|
-
import { mkdir as
|
|
3585
|
-
import { join as
|
|
3913
|
+
import { mkdir as mkdir6, writeFile as writeFile4, readdir as readdir4, readFile as readFile7 } from "fs/promises";
|
|
3914
|
+
import { join as join19 } from "path";
|
|
3586
3915
|
var DELETE_INTENT_SCHEMA = "vault-delete-intent/v1";
|
|
3587
3916
|
var DELETE_INTENT_DIR = "meta/delete-intents";
|
|
3588
3917
|
function normalizeVaultRelPath(path) {
|
|
@@ -3613,10 +3942,10 @@ function buildDeleteIntent(input) {
|
|
|
3613
3942
|
};
|
|
3614
3943
|
}
|
|
3615
3944
|
async function writeDeleteIntent(vault, intent) {
|
|
3616
|
-
const dir =
|
|
3617
|
-
await
|
|
3945
|
+
const dir = join19(vault, DELETE_INTENT_DIR);
|
|
3946
|
+
await mkdir6(dir, { recursive: true });
|
|
3618
3947
|
const rel = `${DELETE_INTENT_DIR}/${pathToIntentFilename(intent.path)}`;
|
|
3619
|
-
await writeFile4(
|
|
3948
|
+
await writeFile4(join19(vault, rel), JSON.stringify(intent, null, 2) + "\n", "utf8");
|
|
3620
3949
|
return rel;
|
|
3621
3950
|
}
|
|
3622
3951
|
|
|
@@ -3656,7 +3985,7 @@ async function runArchive(input) {
|
|
|
3656
3985
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
3657
3986
|
const rawArchive = isRaw ? lifecycleDestination(relPath, "archive") : void 0;
|
|
3658
3987
|
if (rawArchive && !rawArchive.ok) return { exitCode: ExitCode.USAGE, result: rawArchive };
|
|
3659
|
-
const archivePath = isRaw ? rawArchive.data :
|
|
3988
|
+
const archivePath = isRaw ? rawArchive.data : join20("_archive", relPath).replace(/\\/g, "/");
|
|
3660
3989
|
const remoteRoot = normalizeRemoteRoot(input.remote);
|
|
3661
3990
|
const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
|
|
3662
3991
|
let cascade;
|
|
@@ -3686,7 +4015,7 @@ async function runArchive(input) {
|
|
|
3686
4015
|
const indexRefs = [];
|
|
3687
4016
|
if (!isRaw) {
|
|
3688
4017
|
try {
|
|
3689
|
-
const idx = await
|
|
4018
|
+
const idx = await readFile8(join20(input.vault, "index.md"), "utf8");
|
|
3690
4019
|
idx.split("\n").forEach((line, i) => {
|
|
3691
4020
|
if (line.includes(`[[${slug}]]`)) indexRefs.push({ line: i + 1, text: line });
|
|
3692
4021
|
});
|
|
@@ -3744,8 +4073,8 @@ async function runArchive(input) {
|
|
|
3744
4073
|
const rewrittenCascadePages = [];
|
|
3745
4074
|
if ((input.cascade || isRaw) && input.apply && cascade) {
|
|
3746
4075
|
for (const ref of cascade.source_array_refs) {
|
|
3747
|
-
const absPath =
|
|
3748
|
-
const text = await
|
|
4076
|
+
const absPath = join20(input.vault, ref.page);
|
|
4077
|
+
const text = await readFile8(absPath, "utf8");
|
|
3749
4078
|
if (!arraysEqual(ref.sources_after, ref.sources_before)) {
|
|
3750
4079
|
let updated;
|
|
3751
4080
|
if (isRaw) {
|
|
@@ -3771,8 +4100,8 @@ ${fmRewritten}
|
|
|
3771
4100
|
if (isRaw) {
|
|
3772
4101
|
for (const ref of cascade.citation_refs) {
|
|
3773
4102
|
if (cascade.source_array_refs.some((sourceRef) => sourceRef.page === ref.page)) continue;
|
|
3774
|
-
const absPath =
|
|
3775
|
-
const original = await
|
|
4103
|
+
const absPath = join20(input.vault, ref.page);
|
|
4104
|
+
const original = await readFile8(absPath, "utf8");
|
|
3776
4105
|
const updated = rewriteRawSourceReferences(original, relPath, archivePath).text;
|
|
3777
4106
|
if (updated !== original) {
|
|
3778
4107
|
const write = await safeWritePage(absPath, updated);
|
|
@@ -3783,13 +4112,13 @@ ${fmRewritten}
|
|
|
3783
4112
|
}
|
|
3784
4113
|
}
|
|
3785
4114
|
if (!isRaw) {
|
|
3786
|
-
await
|
|
3787
|
-
await rename2(
|
|
4115
|
+
await mkdir7(dirname5(join20(input.vault, archivePath)), { recursive: true });
|
|
4116
|
+
await rename2(join20(input.vault, relPath), join20(input.vault, archivePath));
|
|
3788
4117
|
}
|
|
3789
4118
|
let indexUpdated = false;
|
|
3790
4119
|
if (!isRaw) {
|
|
3791
4120
|
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-6OHDINHT.js");
|
|
3792
|
-
const before = await
|
|
4121
|
+
const before = await readFile8(join20(input.vault, "index.md"), "utf8").catch(() => "");
|
|
3793
4122
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
3794
4123
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
3795
4124
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -3843,7 +4172,7 @@ ${fmRewritten}
|
|
|
3843
4172
|
|
|
3844
4173
|
// src/commands/remove.ts
|
|
3845
4174
|
import { unlink as unlink2, access } from "fs/promises";
|
|
3846
|
-
import { join as
|
|
4175
|
+
import { join as join21 } from "path";
|
|
3847
4176
|
async function pathExists(abs) {
|
|
3848
4177
|
try {
|
|
3849
4178
|
await access(abs);
|
|
@@ -3869,7 +4198,7 @@ async function runRemove(input) {
|
|
|
3869
4198
|
if (!relPath) {
|
|
3870
4199
|
try {
|
|
3871
4200
|
const candidate = normalizeVaultRelPath(input.page);
|
|
3872
|
-
if (await pathExists(
|
|
4201
|
+
if (await pathExists(join21(input.vault, candidate))) {
|
|
3873
4202
|
relPath = candidate;
|
|
3874
4203
|
}
|
|
3875
4204
|
} catch {
|
|
@@ -3907,12 +4236,12 @@ async function runRemove(input) {
|
|
|
3907
4236
|
reason: input.reason
|
|
3908
4237
|
});
|
|
3909
4238
|
const tombstonePath = await writeDeleteIntent(input.vault, intent);
|
|
3910
|
-
await unlink2(
|
|
4239
|
+
await unlink2(join21(input.vault, relPath));
|
|
3911
4240
|
if (relPath.endsWith(".md") && !relPath.startsWith("raw/")) {
|
|
3912
|
-
const { readFile:
|
|
4241
|
+
const { readFile: readFile24 } = await import("fs/promises");
|
|
3913
4242
|
const { join: pathJoin } = await import("path");
|
|
3914
4243
|
const { renderRootIndex: renderRootIndex2, writeRootIndexProjection: writeRootIndexProjection2 } = await import("./index-projection-6OHDINHT.js");
|
|
3915
|
-
const before = await
|
|
4244
|
+
const before = await readFile24(pathJoin(input.vault, "index.md"), "utf8").catch(() => "");
|
|
3916
4245
|
const fullTarget = relPath.replace(/\.md$/, "");
|
|
3917
4246
|
const bare = fullTarget.split("/").pop() ?? fullTarget;
|
|
3918
4247
|
const hadIndexEntry = before.includes(`[[${fullTarget}]]`) || before.includes(`[[${bare}]]`);
|
|
@@ -3950,7 +4279,7 @@ async function runRemove(input) {
|
|
|
3950
4279
|
}
|
|
3951
4280
|
|
|
3952
4281
|
// src/commands/drift.ts
|
|
3953
|
-
import { createHash as
|
|
4282
|
+
import { createHash as createHash3 } from "crypto";
|
|
3954
4283
|
|
|
3955
4284
|
// src/utils/fetch.ts
|
|
3956
4285
|
async function controlledFetch(url, opts) {
|
|
@@ -4029,7 +4358,7 @@ async function runDrift(input) {
|
|
|
4029
4358
|
});
|
|
4030
4359
|
continue;
|
|
4031
4360
|
}
|
|
4032
|
-
const currentHash =
|
|
4361
|
+
const currentHash = createHash3("sha256").update(Buffer.from(resp.data.body, "utf8")).digest("hex");
|
|
4033
4362
|
const identity = assessSourceIdentity({
|
|
4034
4363
|
rawPath: raw.relPath,
|
|
4035
4364
|
sourceUrl,
|
|
@@ -4261,7 +4590,7 @@ ${migratedBody}${newFooter}`;
|
|
|
4261
4590
|
|
|
4262
4591
|
// src/commands/update.ts
|
|
4263
4592
|
import { execSync } from "child_process";
|
|
4264
|
-
import { join as
|
|
4593
|
+
import { join as join22 } from "path";
|
|
4265
4594
|
function parseCoreSemver(version) {
|
|
4266
4595
|
const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
4267
4596
|
if (!m) return null;
|
|
@@ -4313,7 +4642,7 @@ function resolveGlobalSkillsRoot() {
|
|
|
4313
4642
|
encoding: "utf8",
|
|
4314
4643
|
timeout: 5e3
|
|
4315
4644
|
}).trim();
|
|
4316
|
-
return
|
|
4645
|
+
return join22(globalRoot, "skillwiki", "skills");
|
|
4317
4646
|
} catch {
|
|
4318
4647
|
return null;
|
|
4319
4648
|
}
|
|
@@ -4341,7 +4670,7 @@ async function runUpdate(input) {
|
|
|
4341
4670
|
const pkg2 = readCliPackageJson();
|
|
4342
4671
|
const currentVersion = pkg2.version;
|
|
4343
4672
|
const tag = normalizeDistTag(input.distTag);
|
|
4344
|
-
const target =
|
|
4673
|
+
const target = join22(input.home, ".claude", "skills");
|
|
4345
4674
|
let latest;
|
|
4346
4675
|
try {
|
|
4347
4676
|
latest = execSync(`npm view skillwiki@${tag} version`, {
|
|
@@ -4422,13 +4751,13 @@ async function runUpdate(input) {
|
|
|
4422
4751
|
// src/commands/self-update.ts
|
|
4423
4752
|
import { execSync as execSync2 } from "child_process";
|
|
4424
4753
|
import { existsSync as existsSync8, readFileSync as readFileSync11 } from "fs";
|
|
4425
|
-
import { join as
|
|
4754
|
+
import { join as join23 } from "path";
|
|
4426
4755
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
4427
4756
|
async function runSelfUpdate(input) {
|
|
4428
4757
|
const currentVersion = readCliPackageJson().version;
|
|
4429
4758
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
4430
4759
|
const distTag = normalizeDistTag(input.distTag);
|
|
4431
|
-
const localPkgPath =
|
|
4760
|
+
const localPkgPath = join23(sourceRoot, "packages", "cli", "package.json");
|
|
4432
4761
|
const hasLocalSource = existsSync8(localPkgPath);
|
|
4433
4762
|
if (input.check) {
|
|
4434
4763
|
let availableVersion = null;
|
|
@@ -4560,10 +4889,10 @@ async function runSelfUpdate(input) {
|
|
|
4560
4889
|
}
|
|
4561
4890
|
|
|
4562
4891
|
// src/commands/transcripts.ts
|
|
4563
|
-
import { readdir as readdir5, stat as
|
|
4564
|
-
import { join as
|
|
4892
|
+
import { readdir as readdir5, stat as stat4, readFile as readFile10 } from "fs/promises";
|
|
4893
|
+
import { join as join24 } from "path";
|
|
4565
4894
|
async function runTranscripts(input) {
|
|
4566
|
-
const dir =
|
|
4895
|
+
const dir = join24(input.vault, "raw", "transcripts");
|
|
4567
4896
|
let entries;
|
|
4568
4897
|
try {
|
|
4569
4898
|
entries = await readdir5(dir, { withFileTypes: true });
|
|
@@ -4573,13 +4902,13 @@ async function runTranscripts(input) {
|
|
|
4573
4902
|
const transcripts = [];
|
|
4574
4903
|
for (const entry of entries) {
|
|
4575
4904
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
4576
|
-
const filePath =
|
|
4577
|
-
const content = await
|
|
4905
|
+
const filePath = join24(dir, entry.name);
|
|
4906
|
+
const content = await readFile10(filePath, "utf8");
|
|
4578
4907
|
const fm = extractFrontmatter(content);
|
|
4579
4908
|
if (!fm.ok) continue;
|
|
4580
4909
|
const ingested = typeof fm.data.ingested === "string" ? fm.data.ingested : "";
|
|
4581
4910
|
if (input.since && ingested && ingested < input.since) continue;
|
|
4582
|
-
const s = await
|
|
4911
|
+
const s = await stat4(filePath);
|
|
4583
4912
|
transcripts.push({
|
|
4584
4913
|
file: `raw/transcripts/${entry.name}`,
|
|
4585
4914
|
ingested,
|
|
@@ -4591,8 +4920,8 @@ async function runTranscripts(input) {
|
|
|
4591
4920
|
}
|
|
4592
4921
|
|
|
4593
4922
|
// src/commands/claims-audit.ts
|
|
4594
|
-
import { readFile as
|
|
4595
|
-
import { join as
|
|
4923
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
4924
|
+
import { join as join25 } from "path";
|
|
4596
4925
|
function safeProjectPresentation(raw) {
|
|
4597
4926
|
if (safeDiagnosticValue(raw) === REDACTED_MALFORMED_REFERENCE) {
|
|
4598
4927
|
return REDACTED_MALFORMED_REFERENCE;
|
|
@@ -4632,9 +4961,9 @@ async function runClaimsAudit(input) {
|
|
|
4632
4961
|
return !input.project || slugByDir.get(relDir) === input.project;
|
|
4633
4962
|
});
|
|
4634
4963
|
const claimSourceResults = await mapWithConcurrency(scopedSpecDirs, vaultIoConcurrency(), async (relDir) => {
|
|
4635
|
-
const specPath =
|
|
4964
|
+
const specPath = join25(input.vault, relDir, "spec.md");
|
|
4636
4965
|
try {
|
|
4637
|
-
const specText = await
|
|
4966
|
+
const specText = await readFile11(specPath, "utf8");
|
|
4638
4967
|
const fm = extractFrontmatter(specText);
|
|
4639
4968
|
if (!fm.ok) return null;
|
|
4640
4969
|
return {
|
|
@@ -4728,10 +5057,10 @@ async function runClaimsAudit(input) {
|
|
|
4728
5057
|
}
|
|
4729
5058
|
|
|
4730
5059
|
// src/commands/compound.ts
|
|
4731
|
-
import { writeFile as writeFile6, mkdir as
|
|
4732
|
-
import { join as
|
|
5060
|
+
import { writeFile as writeFile6, mkdir as mkdir8, readdir as readdir6, unlink as unlink3 } from "fs/promises";
|
|
5061
|
+
import { join as join26 } from "path";
|
|
4733
5062
|
import { existsSync as existsSync9 } from "fs";
|
|
4734
|
-
import { readFile as
|
|
5063
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
4735
5064
|
var RETRO_HEADING_RE = /^## \[(\d{4}-\d{2}-\d{2})(?:\s+[^\]]+)?\] retro \| loop cycle(?: (\d+))?: (.+)$/;
|
|
4736
5065
|
var FIELD_RE = {
|
|
4737
5066
|
improve: /^-\s+\*?\*?Improve:?\*?\*?\s*(.+)$/m,
|
|
@@ -4829,17 +5158,17 @@ function extractRetroFields(date, cycleName, block) {
|
|
|
4829
5158
|
};
|
|
4830
5159
|
}
|
|
4831
5160
|
async function runCompound(input) {
|
|
4832
|
-
const logPath =
|
|
5161
|
+
const logPath = join26(input.vault, "log.md");
|
|
4833
5162
|
let logText;
|
|
4834
5163
|
try {
|
|
4835
|
-
logText = await
|
|
5164
|
+
logText = await readFile12(logPath, "utf8");
|
|
4836
5165
|
} catch {
|
|
4837
5166
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: logPath }) };
|
|
4838
5167
|
}
|
|
4839
5168
|
const entries = parseRetroEntries(logText);
|
|
4840
5169
|
const promoted = [];
|
|
4841
5170
|
const skipped = [];
|
|
4842
|
-
const compoundDir =
|
|
5171
|
+
const compoundDir = join26(input.vault, "projects", input.project, "compound");
|
|
4843
5172
|
for (const entry of entries) {
|
|
4844
5173
|
const generalizeValue = entry.generalize.trim();
|
|
4845
5174
|
if (!/^yes/i.test(generalizeValue)) {
|
|
@@ -4847,7 +5176,7 @@ async function runCompound(input) {
|
|
|
4847
5176
|
continue;
|
|
4848
5177
|
}
|
|
4849
5178
|
const slug = slugify(entry.cycleName);
|
|
4850
|
-
const compoundPath =
|
|
5179
|
+
const compoundPath = join26(compoundDir, `${slug}.md`);
|
|
4851
5180
|
if (existsSync9(compoundPath)) {
|
|
4852
5181
|
skipped.push(entry.date);
|
|
4853
5182
|
continue;
|
|
@@ -4887,7 +5216,7 @@ async function runCompound(input) {
|
|
|
4887
5216
|
const content = frontmatter + "\n" + body;
|
|
4888
5217
|
if (!input.dryRun) {
|
|
4889
5218
|
if (!existsSync9(compoundDir)) {
|
|
4890
|
-
await
|
|
5219
|
+
await mkdir8(compoundDir, { recursive: true });
|
|
4891
5220
|
}
|
|
4892
5221
|
await writeFile6(compoundPath, content, "utf8");
|
|
4893
5222
|
}
|
|
@@ -4908,7 +5237,7 @@ async function runCompound(input) {
|
|
|
4908
5237
|
};
|
|
4909
5238
|
}
|
|
4910
5239
|
async function runCompoundDelete(input) {
|
|
4911
|
-
const projectDir =
|
|
5240
|
+
const projectDir = join26(input.vault, "projects", input.project);
|
|
4912
5241
|
if (!existsSync9(projectDir)) {
|
|
4913
5242
|
return {
|
|
4914
5243
|
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
@@ -4916,7 +5245,7 @@ async function runCompoundDelete(input) {
|
|
|
4916
5245
|
};
|
|
4917
5246
|
}
|
|
4918
5247
|
const entryName = input.entry.replace(/\.md$/, "");
|
|
4919
|
-
const compoundPath =
|
|
5248
|
+
const compoundPath = join26(projectDir, "compound", `${entryName}.md`);
|
|
4920
5249
|
if (!existsSync9(compoundPath)) {
|
|
4921
5250
|
return {
|
|
4922
5251
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -4950,7 +5279,7 @@ knowledge.md regenerated`
|
|
|
4950
5279
|
};
|
|
4951
5280
|
}
|
|
4952
5281
|
async function runCompoundList(input) {
|
|
4953
|
-
const compoundDir =
|
|
5282
|
+
const compoundDir = join26(input.vault, "projects", input.project, "compound");
|
|
4954
5283
|
if (!existsSync9(compoundDir)) {
|
|
4955
5284
|
return {
|
|
4956
5285
|
exitCode: ExitCode.OK,
|
|
@@ -4981,10 +5310,10 @@ could not read compound directory`
|
|
|
4981
5310
|
const entries = [];
|
|
4982
5311
|
for (const dirent of dirents) {
|
|
4983
5312
|
if (!dirent.isFile() || !dirent.name.endsWith(".md")) continue;
|
|
4984
|
-
const filePath =
|
|
5313
|
+
const filePath = join26(compoundDir, dirent.name);
|
|
4985
5314
|
let text;
|
|
4986
5315
|
try {
|
|
4987
|
-
text = await
|
|
5316
|
+
text = await readFile12(filePath, "utf8");
|
|
4988
5317
|
} catch {
|
|
4989
5318
|
continue;
|
|
4990
5319
|
}
|
|
@@ -5013,8 +5342,8 @@ no compound entries found`;
|
|
|
5013
5342
|
}
|
|
5014
5343
|
|
|
5015
5344
|
// src/commands/session-brief.ts
|
|
5016
|
-
import { mkdir as
|
|
5017
|
-
import { join as
|
|
5345
|
+
import { mkdir as mkdir9, readFile as readFile13, writeFile as writeFile7 } from "fs/promises";
|
|
5346
|
+
import { join as join27, relative, sep } from "path";
|
|
5018
5347
|
var MAX_WORDS = 900;
|
|
5019
5348
|
async function runSessionBrief(input) {
|
|
5020
5349
|
const { root: scanRoot } = resolveReadOnlyVaultRoot(input.vault);
|
|
@@ -5026,29 +5355,33 @@ async function runSessionBrief(input) {
|
|
|
5026
5355
|
const generatedAt = now.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
5027
5356
|
const today = generatedAt.slice(0, 10);
|
|
5028
5357
|
const project = await resolveProject(input);
|
|
5358
|
+
const collectorRunPath = input.agentMemoryRunState ?? satelliteLatestRunPath(input.vault);
|
|
5359
|
+
const collectorRun = readSatelliteLatestRunAt(collectorRunPath);
|
|
5360
|
+
const collectorHealth = evaluateSatelliteRunHealthState(collectorRun, now);
|
|
5029
5361
|
try {
|
|
5362
|
+
const expectedPacketPath = collectorRun?.runDate != null ? `queries/${collectorRun.runDate}-agent-memory-trends-packet.md` : void 0;
|
|
5363
|
+
const packetExists = expectedPacketPath != null && scan.data.typedKnowledge.some((page) => page.relPath === expectedPacketPath);
|
|
5030
5364
|
const [
|
|
5031
5365
|
transcripts,
|
|
5032
5366
|
workItems,
|
|
5033
5367
|
digests,
|
|
5034
5368
|
baseHealthWarnings,
|
|
5035
|
-
satelliteHealth,
|
|
5036
5369
|
sessionPins,
|
|
5037
5370
|
memoryTopics,
|
|
5038
5371
|
pendingSources
|
|
5039
5372
|
] = await Promise.all([
|
|
5040
5373
|
loadTranscriptInfo(scan.data.raw),
|
|
5041
5374
|
loadWorkItems(scan.data.workItems),
|
|
5042
|
-
|
|
5375
|
+
loadTrendPages(scan.data.typedKnowledge, /^queries\/\d{4}-\d{2}-\d{2}-agent-memory-trends-digest\.md$/),
|
|
5043
5376
|
loadHealthWarnings(input.vault),
|
|
5044
|
-
loadSatelliteHealth(input.vault),
|
|
5045
5377
|
loadSessionPins(input.vault, project),
|
|
5046
5378
|
project ? loadMemoryTopics(input.vault, project) : Promise.resolve([]),
|
|
5047
5379
|
loadPendingSources(scanRoot, today)
|
|
5048
5380
|
]);
|
|
5381
|
+
const collectorReceipt = buildCollectorReceipt(collectorRun, collectorHealth, packetExists);
|
|
5049
5382
|
const healthWarnings = [
|
|
5050
5383
|
...baseHealthWarnings,
|
|
5051
|
-
...
|
|
5384
|
+
...collectorReceipt.healthWarnings
|
|
5052
5385
|
];
|
|
5053
5386
|
const claimed = collectClaimedTranscripts(
|
|
5054
5387
|
workItems.map((w) => ({ relDir: w.path, source: w.source, sources: w.sources, closes: w.closes }))
|
|
@@ -5073,6 +5406,7 @@ async function runSessionBrief(input) {
|
|
|
5073
5406
|
unclaimedCaptures,
|
|
5074
5407
|
pendingSources,
|
|
5075
5408
|
activeWork,
|
|
5409
|
+
collectorReceipt: collectorReceipt.lines,
|
|
5076
5410
|
latestDigest,
|
|
5077
5411
|
projectLogs,
|
|
5078
5412
|
memoryTopics,
|
|
@@ -5123,7 +5457,7 @@ async function resolveProject(input) {
|
|
|
5123
5457
|
const envProject = input.env?.SKILLWIKI_PROJECT;
|
|
5124
5458
|
if (envProject) return envProject;
|
|
5125
5459
|
const cwd = input.cwd ?? process.cwd();
|
|
5126
|
-
const projectDotenv = await readProjectSlug(
|
|
5460
|
+
const projectDotenv = await readProjectSlug(join27(cwd, ".skillwiki", ".env"));
|
|
5127
5461
|
if (projectDotenv) return projectDotenv;
|
|
5128
5462
|
const inferred = inferProjectFromPath(input.vault, cwd);
|
|
5129
5463
|
if (inferred) return inferred;
|
|
@@ -5132,7 +5466,7 @@ async function resolveProject(input) {
|
|
|
5132
5466
|
async function readProjectSlug(file) {
|
|
5133
5467
|
let text;
|
|
5134
5468
|
try {
|
|
5135
|
-
text = await
|
|
5469
|
+
text = await readFile13(file, "utf8");
|
|
5136
5470
|
} catch {
|
|
5137
5471
|
return void 0;
|
|
5138
5472
|
}
|
|
@@ -5205,9 +5539,9 @@ async function loadWorkItems(workItemPages) {
|
|
|
5205
5539
|
}
|
|
5206
5540
|
return out;
|
|
5207
5541
|
}
|
|
5208
|
-
async function
|
|
5542
|
+
async function loadTrendPages(typedPages, pathPattern) {
|
|
5209
5543
|
const out = [];
|
|
5210
|
-
for (const page of typedPages.filter((p) =>
|
|
5544
|
+
for (const page of typedPages.filter((p) => pathPattern.test(p.relPath))) {
|
|
5211
5545
|
const text = await readPage(page);
|
|
5212
5546
|
const fm = extractFrontmatter(text);
|
|
5213
5547
|
if (!fm.ok) continue;
|
|
@@ -5223,7 +5557,7 @@ async function loadTrendDigests(typedPages) {
|
|
|
5223
5557
|
return out;
|
|
5224
5558
|
}
|
|
5225
5559
|
async function loadSessionPins(vault, project) {
|
|
5226
|
-
const text = await readIfExists(
|
|
5560
|
+
const text = await readIfExists(join27(vault, "meta", "session-pins.md"));
|
|
5227
5561
|
if (!text) return [];
|
|
5228
5562
|
const fm = extractFrontmatter(text);
|
|
5229
5563
|
if (!fm.ok) return [];
|
|
@@ -5258,7 +5592,8 @@ function renderBrief(input) {
|
|
|
5258
5592
|
appendSection(lines, "Unclaimed Captures", input.unclaimedCaptures, "No unclaimed task or bug captures found.");
|
|
5259
5593
|
appendSection(lines, "Recent Pending Sources", input.pendingSources, "No pending articles or papers found.");
|
|
5260
5594
|
appendSection(lines, "Recent Session Logs", input.project ? input.projectLogs : input.latestLogs, "No recent session logs found.");
|
|
5261
|
-
|
|
5595
|
+
appendTextSection(lines, "Latest Collector Run", input.collectorReceipt, "No collector receipt is available.");
|
|
5596
|
+
appendSection(lines, "Latest Judged Agent Memory Trends", input.latestDigest, "No judged agent memory trends digest found.");
|
|
5262
5597
|
appendMemoryTopicsSection(lines, input.project, input.memoryTopics);
|
|
5263
5598
|
appendTextSection(lines, "Health Warnings", input.healthWarnings, "No high-level health warnings found.");
|
|
5264
5599
|
lines.push(
|
|
@@ -5316,13 +5651,42 @@ function appendTextSection(lines, title, items, empty) {
|
|
|
5316
5651
|
}
|
|
5317
5652
|
lines.push("");
|
|
5318
5653
|
}
|
|
5319
|
-
|
|
5320
|
-
const
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5654
|
+
function buildCollectorReceipt(run, health, packetExists) {
|
|
5655
|
+
const healthWarning = satelliteHealthWarning(health);
|
|
5656
|
+
const healthWarnings = healthWarning ? [healthWarning] : [];
|
|
5657
|
+
if (!run) return { lines: [], healthWarnings };
|
|
5658
|
+
const finished = run.finishedAt ? ` at ${run.finishedAt}` : "";
|
|
5659
|
+
if (health.failed) {
|
|
5660
|
+
return { lines: [`Collector run failed${finished}.`], healthWarnings };
|
|
5661
|
+
}
|
|
5662
|
+
if (health.stale) {
|
|
5663
|
+
return { lines: [`Collector receipt is stale${finished}.`], healthWarnings };
|
|
5325
5664
|
}
|
|
5665
|
+
if (run.status !== "success" || run.selectedCandidateCount === void 0 || !run.runDate) {
|
|
5666
|
+
return { lines: [], healthWarnings };
|
|
5667
|
+
}
|
|
5668
|
+
if (run.selectedCandidateCount === 0) {
|
|
5669
|
+
return {
|
|
5670
|
+
lines: [`${run.runDate} Quiet night \u2014 collector succeeded, 0 selected; no packet published${finished}.`],
|
|
5671
|
+
healthWarnings
|
|
5672
|
+
};
|
|
5673
|
+
}
|
|
5674
|
+
const packetPath = `queries/${run.runDate}-agent-memory-trends-packet.md`;
|
|
5675
|
+
if (packetExists) {
|
|
5676
|
+
return {
|
|
5677
|
+
lines: [`${run.runDate} Collector succeeded; ${run.selectedCandidateCount} selected; packet: [[${packetPath.replace(/\.md$/, "")}]]${finished}.`],
|
|
5678
|
+
healthWarnings
|
|
5679
|
+
};
|
|
5680
|
+
}
|
|
5681
|
+
return {
|
|
5682
|
+
lines: [`${run.runDate} Collector succeeded with ${run.selectedCandidateCount} selected, but the packet is missing${finished}.`],
|
|
5683
|
+
healthWarnings: [
|
|
5684
|
+
...healthWarnings,
|
|
5685
|
+
`agent-memory-trends: collector selected ${run.selectedCandidateCount} candidate(s) but ${packetPath} is missing`
|
|
5686
|
+
]
|
|
5687
|
+
};
|
|
5688
|
+
}
|
|
5689
|
+
function satelliteHealthWarning(health) {
|
|
5326
5690
|
if (health.failed) {
|
|
5327
5691
|
const fc = health.failureClass && health.failureClass.length > 0 ? health.failureClass : "unknown";
|
|
5328
5692
|
return `agent-memory-trends: last run failed (${fc})`;
|
|
@@ -5338,11 +5702,8 @@ async function loadSatelliteHealth(vaultPath) {
|
|
|
5338
5702
|
}
|
|
5339
5703
|
return null;
|
|
5340
5704
|
}
|
|
5341
|
-
function satelliteHealthWarnings(warning) {
|
|
5342
|
-
return warning ? [warning] : [];
|
|
5343
|
-
}
|
|
5344
5705
|
async function loadHealthWarnings(vault) {
|
|
5345
|
-
const text = await readIfExists(
|
|
5706
|
+
const text = await readIfExists(join27(vault, ".skillwiki", "health.json"));
|
|
5346
5707
|
if (!text) return [];
|
|
5347
5708
|
try {
|
|
5348
5709
|
const parsed = JSON.parse(text);
|
|
@@ -5355,7 +5716,7 @@ async function loadHealthWarnings(vault) {
|
|
|
5355
5716
|
}
|
|
5356
5717
|
}
|
|
5357
5718
|
async function loadMemoryTopics(vault, project) {
|
|
5358
|
-
const text = await readIfExists(
|
|
5719
|
+
const text = await readIfExists(join27(vault, ".skillwiki", "memory", project, "topics.json"));
|
|
5359
5720
|
if (!text) return [];
|
|
5360
5721
|
try {
|
|
5361
5722
|
const parsed = JSON.parse(text);
|
|
@@ -5380,11 +5741,11 @@ async function loadMemoryTopics(vault, project) {
|
|
|
5380
5741
|
}
|
|
5381
5742
|
}
|
|
5382
5743
|
async function writeBriefArtifacts(vault, input) {
|
|
5383
|
-
const metaPath =
|
|
5384
|
-
const cacheMdPath =
|
|
5385
|
-
const cacheJsonPath =
|
|
5386
|
-
await
|
|
5387
|
-
await
|
|
5744
|
+
const metaPath = join27(vault, "meta", "latest-session-brief.md");
|
|
5745
|
+
const cacheMdPath = join27(vault, ".skillwiki", "session-brief.md");
|
|
5746
|
+
const cacheJsonPath = join27(vault, ".skillwiki", "session-brief.json");
|
|
5747
|
+
await mkdir9(join27(vault, "meta"), { recursive: true });
|
|
5748
|
+
await mkdir9(join27(vault, ".skillwiki"), { recursive: true });
|
|
5388
5749
|
const committed = renderCommittedBrief(input);
|
|
5389
5750
|
const previousComparable = comparableBrief(await readIfExists(metaPath));
|
|
5390
5751
|
const nextComparable = comparableBrief(committed);
|
|
@@ -5457,7 +5818,7 @@ function renderCommittedBrief(input) {
|
|
|
5457
5818
|
].filter((line) => line !== "").join("\n");
|
|
5458
5819
|
}
|
|
5459
5820
|
async function rebuildRootIndexProjection(vault) {
|
|
5460
|
-
const before = await readIfExists(
|
|
5821
|
+
const before = await readIfExists(join27(vault, "index.md"));
|
|
5461
5822
|
if (!before) return false;
|
|
5462
5823
|
const projection = await renderRootIndex({ vault, currentText: before });
|
|
5463
5824
|
if (!projection.ok) return false;
|
|
@@ -5467,7 +5828,7 @@ async function rebuildRootIndexProjection(vault) {
|
|
|
5467
5828
|
}
|
|
5468
5829
|
async function readIfExists(path) {
|
|
5469
5830
|
try {
|
|
5470
|
-
return await
|
|
5831
|
+
return await readFile13(path, "utf8");
|
|
5471
5832
|
} catch {
|
|
5472
5833
|
return "";
|
|
5473
5834
|
}
|
|
@@ -5509,23 +5870,23 @@ function dateFromPath(path) {
|
|
|
5509
5870
|
}
|
|
5510
5871
|
|
|
5511
5872
|
// src/commands/ingest.ts
|
|
5512
|
-
import { readFile as
|
|
5513
|
-
import { join as
|
|
5514
|
-
import { createHash as
|
|
5873
|
+
import { readFile as readFile16, open, unlink as unlink4, mkdir as mkdir11 } from "fs/promises";
|
|
5874
|
+
import { join as join32 } from "path";
|
|
5875
|
+
import { createHash as createHash5 } from "crypto";
|
|
5515
5876
|
|
|
5516
5877
|
// src/commands/page-publish.ts
|
|
5517
5878
|
import { readFileSync as readFileSync13, realpathSync as realpathSync2 } from "fs";
|
|
5518
|
-
import { mkdir as
|
|
5519
|
-
import { dirname as dirname8, join as
|
|
5879
|
+
import { mkdir as mkdir10, readFile as readFile15 } from "fs/promises";
|
|
5880
|
+
import { dirname as dirname8, join as join31, resolve as resolve4 } from "path";
|
|
5520
5881
|
|
|
5521
5882
|
// src/utils/publication-approval.ts
|
|
5522
|
-
import { createHash as
|
|
5883
|
+
import { createHash as createHash4 } from "crypto";
|
|
5523
5884
|
var APPROVAL_CONTRACT = "skillwiki-publication-approval-v1";
|
|
5524
5885
|
var APPROVAL_TOKEN_VERSION = "swpub1";
|
|
5525
5886
|
var SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
5526
5887
|
var PUBLISHERS = /* @__PURE__ */ new Set(["page", "project-page"]);
|
|
5527
5888
|
function sha256Hex(bytes) {
|
|
5528
|
-
return
|
|
5889
|
+
return createHash4("sha256").update(bytes).digest("hex");
|
|
5529
5890
|
}
|
|
5530
5891
|
function normalizeLogNote(note) {
|
|
5531
5892
|
if (note === void 0) return "";
|
|
@@ -5744,8 +6105,8 @@ function redactApprovalTokens(text) {
|
|
|
5744
6105
|
}
|
|
5745
6106
|
|
|
5746
6107
|
// src/publish/pipeline.ts
|
|
5747
|
-
import { readFile as
|
|
5748
|
-
import { dirname as dirname7, join as
|
|
6108
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
6109
|
+
import { dirname as dirname7, join as join30, resolve as resolve3 } from "path";
|
|
5749
6110
|
|
|
5750
6111
|
// src/utils/publication-operation-journal.ts
|
|
5751
6112
|
import {
|
|
@@ -5759,7 +6120,7 @@ import {
|
|
|
5759
6120
|
writeFileSync as writeFileSync5
|
|
5760
6121
|
} from "fs";
|
|
5761
6122
|
import { homedir } from "os";
|
|
5762
|
-
import { dirname as dirname6, join as
|
|
6123
|
+
import { dirname as dirname6, join as join28 } from "path";
|
|
5763
6124
|
var PUBLICATION_JOURNAL_SCHEMA = "skillwiki-publication-operation-v1";
|
|
5764
6125
|
var PHASE_ORDER2 = [
|
|
5765
6126
|
"locked",
|
|
@@ -5788,16 +6149,16 @@ function vaultIdentity(vaultPath) {
|
|
|
5788
6149
|
return sha256Hex(real).slice(0, 16);
|
|
5789
6150
|
}
|
|
5790
6151
|
function publicationJournalRoot(home = process.env.HOME || homedir()) {
|
|
5791
|
-
return
|
|
6152
|
+
return join28(home, ".skillwiki", "publication-operations");
|
|
5792
6153
|
}
|
|
5793
6154
|
function resolveJournalPaths(vaultPath, operationId2, home = process.env.HOME || homedir()) {
|
|
5794
6155
|
const identity = vaultIdentity(vaultPath);
|
|
5795
6156
|
const rootDir = publicationJournalRoot(home);
|
|
5796
|
-
const vaultDir =
|
|
6157
|
+
const vaultDir = join28(rootDir, identity);
|
|
5797
6158
|
return {
|
|
5798
6159
|
rootDir,
|
|
5799
6160
|
vaultDir,
|
|
5800
|
-
journalPath:
|
|
6161
|
+
journalPath: join28(vaultDir, `${operationId2}.json`)
|
|
5801
6162
|
};
|
|
5802
6163
|
}
|
|
5803
6164
|
function ensurePrivateDir(dir) {
|
|
@@ -6018,7 +6379,7 @@ function completePublicationJournal(input) {
|
|
|
6018
6379
|
}
|
|
6019
6380
|
|
|
6020
6381
|
// src/publish/hold-gates.ts
|
|
6021
|
-
import { join as
|
|
6382
|
+
import { join as join29 } from "path";
|
|
6022
6383
|
function evaluateSchemaGate(content, target, publisherKind) {
|
|
6023
6384
|
const frontmatter = extractFrontmatter(content);
|
|
6024
6385
|
if (!frontmatter.ok) return false;
|
|
@@ -6054,7 +6415,7 @@ function evaluateSchemaGate(content, target, publisherKind) {
|
|
|
6054
6415
|
return false;
|
|
6055
6416
|
}
|
|
6056
6417
|
function evaluateBrokenWikilinkGate(body, scan, ownTarget) {
|
|
6057
|
-
const draftPage = { absPath:
|
|
6418
|
+
const draftPage = { absPath: join29(scan.root, ownTarget), relPath: ownTarget };
|
|
6058
6419
|
const resolver = buildWikilinkResolver([...scan.allMarkdown, draftPage]);
|
|
6059
6420
|
const wikilinks = extractBodyWikilinks(body);
|
|
6060
6421
|
for (const link of wikilinks) {
|
|
@@ -6241,7 +6602,7 @@ function phaseAtLeast(current, needed) {
|
|
|
6241
6602
|
}
|
|
6242
6603
|
async function readPageChanged(targetPath, content) {
|
|
6243
6604
|
try {
|
|
6244
|
-
return ok(await
|
|
6605
|
+
return ok(await readFile14(targetPath, "utf8") !== content);
|
|
6245
6606
|
} catch (error) {
|
|
6246
6607
|
if (error.code === "ENOENT") return ok(true);
|
|
6247
6608
|
return err("WRITE_FAILED", { path: targetPath, message: String(error) });
|
|
@@ -6249,7 +6610,7 @@ async function readPageChanged(targetPath, content) {
|
|
|
6249
6610
|
}
|
|
6250
6611
|
async function readPriorTargetSha(targetPath) {
|
|
6251
6612
|
try {
|
|
6252
|
-
const text = await
|
|
6613
|
+
const text = await readFile14(targetPath, "utf8");
|
|
6253
6614
|
return ok(sha256Hex(text));
|
|
6254
6615
|
} catch (error) {
|
|
6255
6616
|
if (error.code === "ENOENT") return ok("absent");
|
|
@@ -6282,10 +6643,10 @@ function successReceipt(strategy, prepared, details) {
|
|
|
6282
6643
|
};
|
|
6283
6644
|
}
|
|
6284
6645
|
async function runPipelinePreview(strategy, prepared, vault) {
|
|
6285
|
-
const schemaPath =
|
|
6646
|
+
const schemaPath = join30(vault, "SCHEMA.md");
|
|
6286
6647
|
let schemaText;
|
|
6287
6648
|
try {
|
|
6288
|
-
schemaText = await
|
|
6649
|
+
schemaText = await readFile14(schemaPath, "utf8");
|
|
6289
6650
|
} catch (error) {
|
|
6290
6651
|
const result = err("FILE_NOT_FOUND", { path: schemaPath, message: String(error) });
|
|
6291
6652
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -6309,10 +6670,10 @@ async function runPipelinePreview(strategy, prepared, vault) {
|
|
|
6309
6670
|
if (!indices.ok) {
|
|
6310
6671
|
return { exitCode: indices.exitCode, result: indices.result };
|
|
6311
6672
|
}
|
|
6312
|
-
const logPath =
|
|
6673
|
+
const logPath = join30(vault, "log.md");
|
|
6313
6674
|
let logAppended;
|
|
6314
6675
|
try {
|
|
6315
|
-
const logText = await
|
|
6676
|
+
const logText = await readFile14(logPath, "utf8");
|
|
6316
6677
|
const marker = strategy.publisherKind === "page" ? `<!-- skillwiki-page-publish:${prepared.operationId} -->` : `<!-- skillwiki-project-page-publish:${prepared.operationId} -->`;
|
|
6317
6678
|
logAppended = !logText.includes(marker);
|
|
6318
6679
|
} catch (error) {
|
|
@@ -6403,10 +6764,10 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6403
6764
|
return { ok: false, exitCode: ExitCode.WRITE_FAILED, stage: "journal", state, cause: journalHook };
|
|
6404
6765
|
}
|
|
6405
6766
|
}
|
|
6406
|
-
const schemaPath =
|
|
6767
|
+
const schemaPath = join30(vault, "SCHEMA.md");
|
|
6407
6768
|
let schemaText;
|
|
6408
6769
|
try {
|
|
6409
|
-
schemaText = await
|
|
6770
|
+
schemaText = await readFile14(schemaPath, "utf8");
|
|
6410
6771
|
} catch (error) {
|
|
6411
6772
|
return {
|
|
6412
6773
|
ok: false,
|
|
@@ -6455,8 +6816,8 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6455
6816
|
if (!strategy.usesJournal || !phaseAtLeast(currentPhase, "page")) {
|
|
6456
6817
|
if (strategy.publisherKind === "project-page") {
|
|
6457
6818
|
try {
|
|
6458
|
-
const { mkdir:
|
|
6459
|
-
await
|
|
6819
|
+
const { mkdir: mkdir14 } = await import("fs/promises");
|
|
6820
|
+
await mkdir14(dirname7(prepared.targetPath), { recursive: true });
|
|
6460
6821
|
} catch (error) {
|
|
6461
6822
|
return {
|
|
6462
6823
|
ok: false,
|
|
@@ -6504,8 +6865,8 @@ async function runPipelineLockedPrimaryStages(strategy, prepared, vault, deps, j
|
|
|
6504
6865
|
let visibleSchema;
|
|
6505
6866
|
try {
|
|
6506
6867
|
[visible, visibleSchema] = await Promise.all([
|
|
6507
|
-
|
|
6508
|
-
|
|
6868
|
+
readFile14(prepared.targetPath, "utf8"),
|
|
6869
|
+
readFile14(schemaPath, "utf8")
|
|
6509
6870
|
]);
|
|
6510
6871
|
} catch (error) {
|
|
6511
6872
|
return {
|
|
@@ -6897,16 +7258,16 @@ async function refreshProjectIndexForTarget(vault, projectSlugs, today) {
|
|
|
6897
7258
|
for (const slug of projectSlugs) {
|
|
6898
7259
|
const rendered = await renderProjectIndex(vault, slug, { today });
|
|
6899
7260
|
if (!rendered.ok) return rendered;
|
|
6900
|
-
const indexPath =
|
|
7261
|
+
const indexPath = join31(vault, rendered.data.index_path);
|
|
6901
7262
|
try {
|
|
6902
|
-
await
|
|
7263
|
+
await mkdir10(dirname8(indexPath), { recursive: true });
|
|
6903
7264
|
} catch (error) {
|
|
6904
7265
|
return err("WRITE_FAILED", { path: indexPath, message: String(error) });
|
|
6905
7266
|
}
|
|
6906
7267
|
const written = await atomicWriteText(indexPath, rendered.data.text);
|
|
6907
7268
|
if (!written.ok) return written;
|
|
6908
7269
|
try {
|
|
6909
|
-
const visible = await
|
|
7270
|
+
const visible = await readFile15(indexPath, "utf8");
|
|
6910
7271
|
if (visible !== rendered.data.text) {
|
|
6911
7272
|
return err("WRITE_FAILED", { path: indexPath, message: "project index verification failed" });
|
|
6912
7273
|
}
|
|
@@ -7007,7 +7368,7 @@ function preparePagePublicationFromContent(input) {
|
|
|
7007
7368
|
async function preparePagePublication(input) {
|
|
7008
7369
|
let content;
|
|
7009
7370
|
try {
|
|
7010
|
-
content = await
|
|
7371
|
+
content = await readFile15(input.draftPath, "utf8");
|
|
7011
7372
|
} catch (error) {
|
|
7012
7373
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
7013
7374
|
}
|
|
@@ -7095,10 +7456,10 @@ var PAGE_PUBLISH_STRATEGY = {
|
|
|
7095
7456
|
return void 0;
|
|
7096
7457
|
},
|
|
7097
7458
|
async previewIndices(prepared, vault, pageChanged) {
|
|
7098
|
-
const indexPath =
|
|
7459
|
+
const indexPath = join31(vault, "index.md");
|
|
7099
7460
|
let indexText;
|
|
7100
7461
|
try {
|
|
7101
|
-
indexText = await
|
|
7462
|
+
indexText = await readFile15(indexPath, "utf8");
|
|
7102
7463
|
} catch (error) {
|
|
7103
7464
|
const result = err("FILE_NOT_FOUND", { path: indexPath, message: String(error) });
|
|
7104
7465
|
return { ok: false, exitCode: ExitCode.FILE_NOT_FOUND, result };
|
|
@@ -7116,7 +7477,7 @@ var PAGE_PUBLISH_STRATEGY = {
|
|
|
7116
7477
|
if (!renderedProject.ok) {
|
|
7117
7478
|
return { ok: false, exitCode: errorExitCode(renderedProject.error), result: renderedProject };
|
|
7118
7479
|
}
|
|
7119
|
-
const projectIndexPath =
|
|
7480
|
+
const projectIndexPath = join31(vault, renderedProject.data.index_path);
|
|
7120
7481
|
const projectIndexChanged = await readPageChanged(projectIndexPath, renderedProject.data.text);
|
|
7121
7482
|
if (!projectIndexChanged.ok) {
|
|
7122
7483
|
return { ok: false, exitCode: errorExitCode(projectIndexChanged.error), result: projectIndexChanged };
|
|
@@ -7304,7 +7665,7 @@ function buildTypedContent(title, ingested, type, tags, rawRelPath, provenance)
|
|
|
7304
7665
|
}
|
|
7305
7666
|
async function resolveRawCapture(input) {
|
|
7306
7667
|
try {
|
|
7307
|
-
const existing = await
|
|
7668
|
+
const existing = await readFile16(input.path, "utf8");
|
|
7308
7669
|
const frontmatter = extractFrontmatter(existing);
|
|
7309
7670
|
if (!frontmatter.ok) {
|
|
7310
7671
|
return err("INGEST_VALIDATION_FAILED", {
|
|
@@ -7443,7 +7804,7 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7443
7804
|
sourceContent = fetchResult.data.body;
|
|
7444
7805
|
} else {
|
|
7445
7806
|
try {
|
|
7446
|
-
sourceContent = await
|
|
7807
|
+
sourceContent = await readFile16(input.source, "utf8");
|
|
7447
7808
|
} catch {
|
|
7448
7809
|
return {
|
|
7449
7810
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -7461,14 +7822,14 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7461
7822
|
})
|
|
7462
7823
|
};
|
|
7463
7824
|
}
|
|
7464
|
-
const sha2562 =
|
|
7825
|
+
const sha2562 = createHash5("sha256").update(Buffer.from(sourceContent, "utf8")).digest("hex");
|
|
7465
7826
|
const today = todayIso();
|
|
7466
7827
|
const slug = slugify2(input.title);
|
|
7467
7828
|
const tags = input.tags && input.tags.length > 0 ? input.tags : [];
|
|
7468
7829
|
const rawRelPath = `raw/articles/${slug}.md`;
|
|
7469
7830
|
const typedDir = TYPE_DIR[input.type] ?? `${input.type}s`;
|
|
7470
7831
|
const typedRelPath = `${typedDir}/${slug}.md`;
|
|
7471
|
-
const rawAbsPath =
|
|
7832
|
+
const rawAbsPath = join32(input.vault, rawRelPath);
|
|
7472
7833
|
const identity = assessSourceIdentity({
|
|
7473
7834
|
rawPath: rawRelPath,
|
|
7474
7835
|
sourceUrl: sourceUrl ?? void 0,
|
|
@@ -7512,11 +7873,11 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7512
7873
|
);
|
|
7513
7874
|
if (!input.dryRun) {
|
|
7514
7875
|
try {
|
|
7515
|
-
await
|
|
7876
|
+
await mkdir11(join32(input.vault, typedDir), { recursive: true });
|
|
7516
7877
|
} catch (error) {
|
|
7517
7878
|
return {
|
|
7518
7879
|
exitCode: ExitCode.WRITE_FAILED,
|
|
7519
|
-
result: err("WRITE_FAILED", { path:
|
|
7880
|
+
result: err("WRITE_FAILED", { path: join32(input.vault, typedDir), message: String(error) })
|
|
7520
7881
|
};
|
|
7521
7882
|
}
|
|
7522
7883
|
}
|
|
@@ -7570,11 +7931,11 @@ async function runIngest(input, pagePublishDeps = defaultPagePublishDeps()) {
|
|
|
7570
7931
|
preflight: pagePublishDeps.preflight,
|
|
7571
7932
|
mutate: async (receipt) => {
|
|
7572
7933
|
try {
|
|
7573
|
-
await
|
|
7934
|
+
await mkdir11(join32(input.vault, "raw", "articles"), { recursive: true });
|
|
7574
7935
|
} catch (error) {
|
|
7575
7936
|
return {
|
|
7576
7937
|
exitCode: ExitCode.WRITE_FAILED,
|
|
7577
|
-
result: err("WRITE_FAILED", { path:
|
|
7938
|
+
result: err("WRITE_FAILED", { path: join32(input.vault, "raw", "articles"), message: String(error) })
|
|
7578
7939
|
};
|
|
7579
7940
|
}
|
|
7580
7941
|
const rawWrite = await writeResolvedRaw({
|
|
@@ -7810,8 +8171,8 @@ ${body}`;
|
|
|
7810
8171
|
}
|
|
7811
8172
|
|
|
7812
8173
|
// src/commands/tag-reconcile.ts
|
|
7813
|
-
import { readFile as
|
|
7814
|
-
import { join as
|
|
8174
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
8175
|
+
import { join as join33, posix } from "path";
|
|
7815
8176
|
var TYPED_TARGET_RE = /^(entities|concepts|comparisons|queries|meta)\/[a-z0-9][a-z0-9./_-]*\.md$/;
|
|
7816
8177
|
function errorExitCode2(error) {
|
|
7817
8178
|
switch (error) {
|
|
@@ -7854,7 +8215,7 @@ function asTagArray(frontmatter, path) {
|
|
|
7854
8215
|
async function readTagsFromFile(path) {
|
|
7855
8216
|
let text;
|
|
7856
8217
|
try {
|
|
7857
|
-
text = await
|
|
8218
|
+
text = await readFile17(path, "utf8");
|
|
7858
8219
|
} catch (error) {
|
|
7859
8220
|
if (error.code === "ENOENT") {
|
|
7860
8221
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path }) };
|
|
@@ -7875,7 +8236,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
7875
8236
|
result: err("INVALID_FRONTMATTER", { message: "explicit tags must be an array of strings" })
|
|
7876
8237
|
};
|
|
7877
8238
|
}
|
|
7878
|
-
const source = input.from ?? (explicit.length === 0 ?
|
|
8239
|
+
const source = input.from ?? (explicit.length === 0 ? join33(input.vault, page) : void 0);
|
|
7879
8240
|
if (!source) return { exitCode: ExitCode.OK, result: ok({ tags: [...new Set(explicit)].sort() }) };
|
|
7880
8241
|
const sourced = await readTagsFromFile(source);
|
|
7881
8242
|
if (!sourced.result.ok) return { exitCode: sourced.exitCode, result: sourced.result };
|
|
@@ -7886,7 +8247,7 @@ async function resolveRequestedTags(input, page) {
|
|
|
7886
8247
|
}
|
|
7887
8248
|
async function readSchema(schemaPath) {
|
|
7888
8249
|
try {
|
|
7889
|
-
return { exitCode: ExitCode.OK, result: ok(await
|
|
8250
|
+
return { exitCode: ExitCode.OK, result: ok(await readFile17(schemaPath, "utf8")) };
|
|
7890
8251
|
} catch (error) {
|
|
7891
8252
|
if (error.code === "ENOENT") {
|
|
7892
8253
|
return { exitCode: ExitCode.FILE_NOT_FOUND, result: err("FILE_NOT_FOUND", { path: schemaPath }) };
|
|
@@ -7914,7 +8275,7 @@ function previewResult(page, tags, reconciled, dryRun, filesChanged) {
|
|
|
7914
8275
|
};
|
|
7915
8276
|
}
|
|
7916
8277
|
async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
7917
|
-
const schemaPath =
|
|
8278
|
+
const schemaPath = join33(input.vault, "SCHEMA.md");
|
|
7918
8279
|
const current = await readSchema(schemaPath);
|
|
7919
8280
|
if (!current.result.ok) return { exitCode: current.exitCode, result: current.result };
|
|
7920
8281
|
const next = reconcileTaxonomyDocument(current.result.data, { tags, comment });
|
|
@@ -7925,7 +8286,7 @@ async function reconcileTagsWhileLocked(input, page, tags, comment) {
|
|
|
7925
8286
|
}
|
|
7926
8287
|
let verifiedText;
|
|
7927
8288
|
try {
|
|
7928
|
-
verifiedText = await
|
|
8289
|
+
verifiedText = await readFile17(schemaPath, "utf8");
|
|
7929
8290
|
} catch (error) {
|
|
7930
8291
|
return {
|
|
7931
8292
|
exitCode: ExitCode.WRITE_FAILED,
|
|
@@ -7951,7 +8312,7 @@ async function runTagReconcile(input) {
|
|
|
7951
8312
|
const comment = taxonomyCommentForPage(page.data, date, input.reason);
|
|
7952
8313
|
if (!comment.ok) return { exitCode: ExitCode.SCHEME_REJECTED, result: comment };
|
|
7953
8314
|
if (!input.write) {
|
|
7954
|
-
const schema = await readSchema(
|
|
8315
|
+
const schema = await readSchema(join33(input.vault, "SCHEMA.md"));
|
|
7955
8316
|
if (!schema.result.ok) return { exitCode: schema.exitCode, result: schema.result };
|
|
7956
8317
|
const preview = reconcileTaxonomyDocument(schema.result.data, { tags, comment: comment.data });
|
|
7957
8318
|
return previewResult(page.data, tags, preview, true, []);
|
|
@@ -8004,8 +8365,8 @@ async function runTagReconcile(input) {
|
|
|
8004
8365
|
|
|
8005
8366
|
// src/commands/project-page-publish.ts
|
|
8006
8367
|
import { realpathSync as realpathSync4 } from "fs";
|
|
8007
|
-
import { mkdir as
|
|
8008
|
-
import { dirname as dirname10, join as
|
|
8368
|
+
import { mkdir as mkdir12, readFile as readFile18 } from "fs/promises";
|
|
8369
|
+
import { dirname as dirname10, join as join34, resolve as resolve6 } from "path";
|
|
8009
8370
|
|
|
8010
8371
|
// src/utils/architecture-page.ts
|
|
8011
8372
|
import { lstatSync, realpathSync as realpathSync3 } from "fs";
|
|
@@ -8177,7 +8538,7 @@ function validateLogNote(logNote) {
|
|
|
8177
8538
|
async function prepareProjectPagePublication(input) {
|
|
8178
8539
|
let content;
|
|
8179
8540
|
try {
|
|
8180
|
-
content = await
|
|
8541
|
+
content = await readFile18(input.draftPath, "utf8");
|
|
8181
8542
|
} catch (error) {
|
|
8182
8543
|
return err("FILE_NOT_FOUND", { path: input.draftPath, message: String(error) });
|
|
8183
8544
|
}
|
|
@@ -8310,9 +8671,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8310
8671
|
async updateIndicesLocked(prepared, vault, state, observe) {
|
|
8311
8672
|
const rendered = await renderProjectIndex(vault, prepared.page.project, { today: prepared.date });
|
|
8312
8673
|
if (!rendered.ok) return rendered;
|
|
8313
|
-
const knowledgePath =
|
|
8674
|
+
const knowledgePath = join34(vault, rendered.data.index_path);
|
|
8314
8675
|
try {
|
|
8315
|
-
await
|
|
8676
|
+
await mkdir12(dirname10(knowledgePath), { recursive: true });
|
|
8316
8677
|
} catch (error) {
|
|
8317
8678
|
return err("WRITE_FAILED", { message: String(error) });
|
|
8318
8679
|
}
|
|
@@ -8321,7 +8682,7 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8321
8682
|
state.projectIndexUpdated = knowledgeWrite.data.changed;
|
|
8322
8683
|
if (state.projectIndexUpdated) state.changed.add(rendered.data.index_path);
|
|
8323
8684
|
try {
|
|
8324
|
-
const visibleIndex = await
|
|
8685
|
+
const visibleIndex = await readFile18(knowledgePath, "utf8");
|
|
8325
8686
|
if (visibleIndex !== rendered.data.text) {
|
|
8326
8687
|
return err("WRITE_FAILED", { message: "project knowledge.md verification failed" });
|
|
8327
8688
|
}
|
|
@@ -8341,9 +8702,9 @@ var PROJECT_PAGE_PUBLISH_STRATEGY = {
|
|
|
8341
8702
|
}
|
|
8342
8703
|
let projectIndexChanged = true;
|
|
8343
8704
|
if (projectIndex.ok) {
|
|
8344
|
-
const indexPath =
|
|
8705
|
+
const indexPath = join34(vault, projectIndex.data.index_path);
|
|
8345
8706
|
try {
|
|
8346
|
-
const existing = await
|
|
8707
|
+
const existing = await readFile18(indexPath, "utf8");
|
|
8347
8708
|
projectIndexChanged = existing !== projectIndex.data.text || pageChanged;
|
|
8348
8709
|
} catch {
|
|
8349
8710
|
projectIndexChanged = true;
|
|
@@ -8500,10 +8861,10 @@ function runSyncJournalClearStale(input) {
|
|
|
8500
8861
|
|
|
8501
8862
|
// src/commands/snapshot-maintenance.ts
|
|
8502
8863
|
import { existsSync as existsSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, appendFileSync } from "fs";
|
|
8503
|
-
import { createHash as
|
|
8864
|
+
import { createHash as createHash6 } from "crypto";
|
|
8504
8865
|
import { execSync as execSync3, spawn } from "child_process";
|
|
8505
8866
|
import { platform as platform2 } from "os";
|
|
8506
|
-
import { join as
|
|
8867
|
+
import { join as join35, resolve as resolvePath } from "path";
|
|
8507
8868
|
var MAINTENANCE_SCHEMA_VERSION = 1;
|
|
8508
8869
|
var MAINTENANCE_COMMAND = "snapshot-maintenance journal clear-stale";
|
|
8509
8870
|
var REPAIR_COMMAND = "snapshot-maintenance projection-conflict repair";
|
|
@@ -8527,7 +8888,7 @@ function computeApprovalId(plan) {
|
|
|
8527
8888
|
targets,
|
|
8528
8889
|
normalizeReason(plan.operator_reason)
|
|
8529
8890
|
].join("|");
|
|
8530
|
-
return "smap1-" +
|
|
8891
|
+
return "smap1-" + createHash6("sha256").update(payload).digest("hex").slice(0, 32);
|
|
8531
8892
|
}
|
|
8532
8893
|
function normalizeReason(reason) {
|
|
8533
8894
|
return reason.trim().replace(/\s+/g, " ");
|
|
@@ -8568,7 +8929,7 @@ async function runSnapshotMaintenanceDryRun(input) {
|
|
|
8568
8929
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE"));
|
|
8569
8930
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
8570
8931
|
}
|
|
8571
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
8932
|
+
if (!existsSync11(requested) || !existsSync11(join35(requested, ".git"))) {
|
|
8572
8933
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO"));
|
|
8573
8934
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
8574
8935
|
}
|
|
@@ -8648,14 +9009,14 @@ async function resolveLiveVault(input) {
|
|
|
8648
9009
|
return resolved.ok ? canonicalize(resolved.data.path) : void 0;
|
|
8649
9010
|
}
|
|
8650
9011
|
function snapshotMaintenanceAuditLogPath(home) {
|
|
8651
|
-
const stateDir = platform2() === "darwin" ?
|
|
8652
|
-
return
|
|
9012
|
+
const stateDir = platform2() === "darwin" ? join35(home, "Library", "Application Support", "vault-sync") : join35(home, ".local", "state", "vault-sync");
|
|
9013
|
+
return join35(stateDir, "snapshot-maintenance-audit.jsonl");
|
|
8653
9014
|
}
|
|
8654
9015
|
function defaultAuditSink(home) {
|
|
8655
9016
|
return (event) => {
|
|
8656
9017
|
try {
|
|
8657
9018
|
const logPath = snapshotMaintenanceAuditLogPath(home);
|
|
8658
|
-
mkdirSync4(
|
|
9019
|
+
mkdirSync4(join35(logPath, ".."), { recursive: true });
|
|
8659
9020
|
appendFileSync(logPath, JSON.stringify(event) + "\n", { encoding: "utf8" });
|
|
8660
9021
|
} catch {
|
|
8661
9022
|
}
|
|
@@ -8797,15 +9158,15 @@ function computeRepairApprovalId(plan) {
|
|
|
8797
9158
|
paths,
|
|
8798
9159
|
normalizeReason(plan.operator_reason)
|
|
8799
9160
|
].join("|");
|
|
8800
|
-
return "smap1-" +
|
|
9161
|
+
return "smap1-" + createHash6("sha256").update(payload).digest("hex").slice(0, 32);
|
|
8801
9162
|
}
|
|
8802
9163
|
function nonMergeSequencerActive(repo) {
|
|
8803
9164
|
const gitDir = git(repo, ["rev-parse", "--absolute-git-dir"]);
|
|
8804
9165
|
if (!gitDir) return false;
|
|
8805
9166
|
for (const m of ["CHERRY_PICK_HEAD", "REVERT_HEAD"]) {
|
|
8806
|
-
if (existsSync11(
|
|
9167
|
+
if (existsSync11(join35(gitDir, m))) return true;
|
|
8807
9168
|
}
|
|
8808
|
-
return existsSync11(
|
|
9169
|
+
return existsSync11(join35(gitDir, "rebase-merge")) || existsSync11(join35(gitDir, "rebase-apply"));
|
|
8809
9170
|
}
|
|
8810
9171
|
function extraUnmergedPaths(unmerged) {
|
|
8811
9172
|
const allow = new Set(PROJECTION_CONFLICT_ALLOWLIST);
|
|
@@ -8844,7 +9205,7 @@ async function authorizeRepairContext(input) {
|
|
|
8844
9205
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_WRONG_WORKTREE", void 0, REPAIR_COMMAND));
|
|
8845
9206
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_WRONG_WORKTREE", `requested path '${requested}' is not the configured snapshot worktree '${canonicalize(configuredWorktree)}'`) };
|
|
8846
9207
|
}
|
|
8847
|
-
if (!existsSync11(requested) || !existsSync11(
|
|
9208
|
+
if (!existsSync11(requested) || !existsSync11(join35(requested, ".git"))) {
|
|
8848
9209
|
audit(makeAuditEvent(input, fleetLoad.hostId, now, "refusal", "MAINTENANCE_MISSING_GIT_REPO", void 0, REPAIR_COMMAND));
|
|
8849
9210
|
return { exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED, result: refusalErr("MAINTENANCE_MISSING_GIT_REPO", `snapshot worktree is not a git repository: ${requested}`) };
|
|
8850
9211
|
}
|
|
@@ -8945,8 +9306,8 @@ async function performProjectionRepair(input, plan, audit, now) {
|
|
|
8945
9306
|
return { exitCode: ExitCode.WRITE_FAILED, result: rendered };
|
|
8946
9307
|
}
|
|
8947
9308
|
try {
|
|
8948
|
-
writeFileSync6(
|
|
8949
|
-
writeFileSync6(
|
|
9309
|
+
writeFileSync6(join35(plan.snapshot_worktree, "index.md"), rendered.data.index);
|
|
9310
|
+
writeFileSync6(join35(plan.snapshot_worktree, "log.md"), rendered.data.log);
|
|
8950
9311
|
gitStrict(plan.snapshot_worktree, ["add", "--", "index.md", "log.md"]);
|
|
8951
9312
|
gitStrict(plan.snapshot_worktree, [
|
|
8952
9313
|
"commit",
|
|
@@ -9054,7 +9415,7 @@ function releaseSnapshotFlock(handle) {
|
|
|
9054
9415
|
|
|
9055
9416
|
// src/commands/backup.ts
|
|
9056
9417
|
import { statSync as statSync3, readdirSync as readdirSync2, readFileSync as readFileSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "fs";
|
|
9057
|
-
import { join as
|
|
9418
|
+
import { join as join36, relative as relative3, dirname as dirname11 } from "path";
|
|
9058
9419
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
9059
9420
|
|
|
9060
9421
|
// src/utils/s3-client.ts
|
|
@@ -9078,7 +9439,7 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([".git", ".obsidian", "_archive", "node_
|
|
|
9078
9439
|
function* walkMarkdown(dir, base) {
|
|
9079
9440
|
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
9080
9441
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
9081
|
-
const full =
|
|
9442
|
+
const full = join36(dir, entry.name);
|
|
9082
9443
|
if (entry.isDirectory()) {
|
|
9083
9444
|
yield* walkMarkdown(full, base);
|
|
9084
9445
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -9101,7 +9462,7 @@ async function runBackupSync(input) {
|
|
|
9101
9462
|
let failed = 0;
|
|
9102
9463
|
const files = [...walkMarkdown(input.vault, input.vault)];
|
|
9103
9464
|
for (const relPath of files) {
|
|
9104
|
-
const absPath =
|
|
9465
|
+
const absPath = join36(input.vault, relPath);
|
|
9105
9466
|
const localStat = statSync3(absPath);
|
|
9106
9467
|
let needsUpload = true;
|
|
9107
9468
|
try {
|
|
@@ -9177,7 +9538,7 @@ async function runBackupRestore(input) {
|
|
|
9177
9538
|
const objects = list.Contents ?? [];
|
|
9178
9539
|
for (const obj of objects) {
|
|
9179
9540
|
if (!obj.Key) continue;
|
|
9180
|
-
const localPath =
|
|
9541
|
+
const localPath = join36(target, obj.Key);
|
|
9181
9542
|
try {
|
|
9182
9543
|
const localStat = statSync3(localPath);
|
|
9183
9544
|
if (obj.LastModified && localStat.mtime > obj.LastModified) {
|
|
@@ -9224,8 +9585,8 @@ async function runBackupRestore(input) {
|
|
|
9224
9585
|
|
|
9225
9586
|
// src/commands/status.ts
|
|
9226
9587
|
import { existsSync as existsSync12, statSync as statSync4 } from "fs";
|
|
9227
|
-
import { readFile as
|
|
9228
|
-
import { join as
|
|
9588
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
9589
|
+
import { join as join37 } from "path";
|
|
9229
9590
|
var FUSE_NO_MIRROR_NOTE = "FUSE vault with no read mirror - status scan may be slow";
|
|
9230
9591
|
async function runStatus(input) {
|
|
9231
9592
|
if (!existsSync12(input.vault)) {
|
|
@@ -9264,7 +9625,7 @@ async function runStatus(input) {
|
|
|
9264
9625
|
const compound = scan.data.compound.length;
|
|
9265
9626
|
let schemaVersion = "v1";
|
|
9266
9627
|
try {
|
|
9267
|
-
const schemaContent = await
|
|
9628
|
+
const schemaContent = await readFile19(join37(scanRoot, "SCHEMA.md"), "utf8");
|
|
9268
9629
|
const versionMatch = schemaContent.match(/version:\s*["']?([^"'\s\n]+)/i);
|
|
9269
9630
|
if (versionMatch) schemaVersion = versionMatch[1];
|
|
9270
9631
|
} catch {
|
|
@@ -9332,13 +9693,13 @@ async function runStatus(input) {
|
|
|
9332
9693
|
|
|
9333
9694
|
// src/commands/copy-status.ts
|
|
9334
9695
|
import { existsSync as existsSync13, realpathSync as realpathSync5 } from "fs";
|
|
9335
|
-
import { isAbsolute as isAbsolute2, join as
|
|
9696
|
+
import { isAbsolute as isAbsolute2, join as join39, relative as relative5, resolve as resolve8 } from "path";
|
|
9336
9697
|
|
|
9337
9698
|
// src/utils/fetch-projection.ts
|
|
9338
|
-
import { isAbsolute, join as
|
|
9699
|
+
import { isAbsolute, join as join38, resolve as resolve7 } from "path";
|
|
9339
9700
|
function inspectConfiguredFetchProjection(home) {
|
|
9340
9701
|
if (!home) return { configured: false };
|
|
9341
|
-
const config = readSkillWikiConfig(
|
|
9702
|
+
const config = readSkillWikiConfig(join38(home, ".skillwiki", ".env"));
|
|
9342
9703
|
const configured = config["vault_sync.fetch_projection"];
|
|
9343
9704
|
if (!configured || configured === "none") return { configured: false };
|
|
9344
9705
|
if (!isAbsolute(configured)) {
|
|
@@ -9348,8 +9709,8 @@ function inspectConfiguredFetchProjection(home) {
|
|
|
9348
9709
|
}
|
|
9349
9710
|
|
|
9350
9711
|
// src/utils/live-drift.ts
|
|
9351
|
-
import { createHash as
|
|
9352
|
-
import { readFile as
|
|
9712
|
+
import { createHash as createHash7 } from "crypto";
|
|
9713
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
9353
9714
|
import { relative as relative4, sep as sep3 } from "path";
|
|
9354
9715
|
|
|
9355
9716
|
// src/utils/git-promotion.ts
|
|
@@ -9370,7 +9731,7 @@ function isGitPromotablePath(path) {
|
|
|
9370
9731
|
// src/utils/live-drift.ts
|
|
9371
9732
|
function gitBlobOid(bytes) {
|
|
9372
9733
|
const header = Buffer.from(`blob ${bytes.length}\0`);
|
|
9373
|
-
return
|
|
9734
|
+
return createHash7("sha1").update(header).update(bytes).digest("hex");
|
|
9374
9735
|
}
|
|
9375
9736
|
function headBlobOids(projection) {
|
|
9376
9737
|
const output = git(projection, ["ls-tree", "-rz", "-r", "HEAD"]);
|
|
@@ -9394,7 +9755,7 @@ async function measureAuthoritativeLiveDrift(liveVault, projection) {
|
|
|
9394
9755
|
const rel = relative4(liveVault, page.absPath).split(sep3).join("/");
|
|
9395
9756
|
if (!isGitPromotablePath(rel)) continue;
|
|
9396
9757
|
livePromotable.add(rel);
|
|
9397
|
-
const bytes = await
|
|
9758
|
+
const bytes = await readFile20(page.absPath);
|
|
9398
9759
|
if (projectionBlobs.get(rel) !== gitBlobOid(bytes)) content += 1;
|
|
9399
9760
|
}
|
|
9400
9761
|
for (const rel of projectionBlobs.keys()) {
|
|
@@ -9572,7 +9933,7 @@ function defaultCopyStatusDeps(input) {
|
|
|
9572
9933
|
const projectionWarning = selection.configured ? `configured fetch projection ignored for Git status; using live vault${projectionProblem ? ` (${projectionProblem})` : ""}` : void 0;
|
|
9573
9934
|
const invalidProjectionDetail = projectionProblem ?? "configured fetch projection is not a git repository";
|
|
9574
9935
|
const gitRootProblem = () => {
|
|
9575
|
-
if (!existsSync13(
|
|
9936
|
+
if (!existsSync13(join39(gitVault, ".git"))) {
|
|
9576
9937
|
return "vault is not a git repository";
|
|
9577
9938
|
}
|
|
9578
9939
|
return void 0;
|
|
@@ -9640,7 +10001,7 @@ function defaultCopyStatusDeps(input) {
|
|
|
9640
10001
|
if (!configuredProjection) {
|
|
9641
10002
|
return { unknown: true, detail: projectionProblem ?? "live drift requires a configured fetch projection" };
|
|
9642
10003
|
}
|
|
9643
|
-
if (!existsSync13(
|
|
10004
|
+
if (!existsSync13(join39(configuredProjection, ".git"))) {
|
|
9644
10005
|
return { unknown: true, detail: invalidProjectionDetail };
|
|
9645
10006
|
}
|
|
9646
10007
|
return measureAuthoritativeLiveDrift(input.vault, configuredProjection);
|
|
@@ -9656,8 +10017,8 @@ async function runCopyStatusCommand(input) {
|
|
|
9656
10017
|
}
|
|
9657
10018
|
|
|
9658
10019
|
// src/commands/seed.ts
|
|
9659
|
-
import { mkdir as
|
|
9660
|
-
import { join as
|
|
10020
|
+
import { mkdir as mkdir13, writeFile as writeFile8, stat as stat5 } from "fs/promises";
|
|
10021
|
+
import { join as join40 } from "path";
|
|
9661
10022
|
var TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
9662
10023
|
var EXAMPLE_PAGES = {
|
|
9663
10024
|
"entities/example-project.md": `---
|
|
@@ -9726,29 +10087,29 @@ Real sources are immutable after ingestion \u2014 never edit them.
|
|
|
9726
10087
|
`;
|
|
9727
10088
|
async function runSeed(input) {
|
|
9728
10089
|
try {
|
|
9729
|
-
await
|
|
10090
|
+
await stat5(join40(input.vault, "SCHEMA.md"));
|
|
9730
10091
|
} catch {
|
|
9731
10092
|
return { exitCode: ExitCode.VAULT_PATH_INVALID, result: err("VAULT_PATH_INVALID", { root: input.vault, reason: "SCHEMA.md missing \u2014 run `skillwiki init` first" }) };
|
|
9732
10093
|
}
|
|
9733
10094
|
const created = [];
|
|
9734
10095
|
const skipped = [];
|
|
9735
10096
|
for (const [relPath, content] of Object.entries(EXAMPLE_PAGES)) {
|
|
9736
|
-
const absPath =
|
|
10097
|
+
const absPath = join40(input.vault, relPath);
|
|
9737
10098
|
try {
|
|
9738
|
-
await
|
|
10099
|
+
await stat5(absPath);
|
|
9739
10100
|
skipped.push(relPath);
|
|
9740
10101
|
} catch {
|
|
9741
|
-
await
|
|
10102
|
+
await mkdir13(join40(absPath, ".."), { recursive: true });
|
|
9742
10103
|
await writeFile8(absPath, content, "utf8");
|
|
9743
10104
|
created.push(relPath);
|
|
9744
10105
|
}
|
|
9745
10106
|
}
|
|
9746
|
-
const rawPath =
|
|
10107
|
+
const rawPath = join40(input.vault, "raw", "articles", "example-source.md");
|
|
9747
10108
|
try {
|
|
9748
|
-
await
|
|
10109
|
+
await stat5(rawPath);
|
|
9749
10110
|
skipped.push("raw/articles/example-source.md");
|
|
9750
10111
|
} catch {
|
|
9751
|
-
await
|
|
10112
|
+
await mkdir13(join40(rawPath, ".."), { recursive: true });
|
|
9752
10113
|
await writeFile8(rawPath, EXAMPLE_RAW, "utf8");
|
|
9753
10114
|
created.push("raw/articles/example-source.md");
|
|
9754
10115
|
}
|
|
@@ -9771,9 +10132,9 @@ async function runSeed(input) {
|
|
|
9771
10132
|
}
|
|
9772
10133
|
|
|
9773
10134
|
// src/commands/canvas.ts
|
|
9774
|
-
import { readFile as
|
|
10135
|
+
import { readFile as readFile21, writeFile as writeFile9 } from "fs/promises";
|
|
9775
10136
|
import { existsSync as existsSync14 } from "fs";
|
|
9776
|
-
import { join as
|
|
10137
|
+
import { join as join41 } from "path";
|
|
9777
10138
|
var NODE_WIDTH = 240;
|
|
9778
10139
|
var NODE_HEIGHT = 60;
|
|
9779
10140
|
var COLUMN_SPACING = 400;
|
|
@@ -9851,7 +10212,7 @@ function buildCanvasEdges(adjacency) {
|
|
|
9851
10212
|
return edges;
|
|
9852
10213
|
}
|
|
9853
10214
|
async function runCanvasGenerate(input) {
|
|
9854
|
-
const graphPath = input.graphPath ??
|
|
10215
|
+
const graphPath = input.graphPath ?? join41(input.vault, ".skillwiki", "graph.json");
|
|
9855
10216
|
if (!existsSync14(graphPath)) {
|
|
9856
10217
|
return {
|
|
9857
10218
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -9863,7 +10224,7 @@ async function runCanvasGenerate(input) {
|
|
|
9863
10224
|
}
|
|
9864
10225
|
let raw;
|
|
9865
10226
|
try {
|
|
9866
|
-
raw = await
|
|
10227
|
+
raw = await readFile21(graphPath, "utf8");
|
|
9867
10228
|
} catch (e) {
|
|
9868
10229
|
return {
|
|
9869
10230
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
@@ -9889,7 +10250,7 @@ async function runCanvasGenerate(input) {
|
|
|
9889
10250
|
const nodes = buildCanvasNodes(paths);
|
|
9890
10251
|
const edges = buildCanvasEdges(graph.adjacency);
|
|
9891
10252
|
const canvas = { nodes, edges };
|
|
9892
|
-
const outPath =
|
|
10253
|
+
const outPath = join41(input.vault, "vault-graph.canvas");
|
|
9893
10254
|
try {
|
|
9894
10255
|
await writeFile9(outPath, JSON.stringify(canvas, null, 2));
|
|
9895
10256
|
} catch (e) {
|
|
@@ -10033,12 +10394,12 @@ async function runSourceDisposition(input) {
|
|
|
10033
10394
|
}
|
|
10034
10395
|
|
|
10035
10396
|
// src/utils/source-disposal.ts
|
|
10036
|
-
import { createHash as
|
|
10037
|
-
import { readFile as
|
|
10397
|
+
import { createHash as createHash8 } from "crypto";
|
|
10398
|
+
import { readFile as readFile23, unlink as unlink5 } from "fs/promises";
|
|
10038
10399
|
|
|
10039
10400
|
// src/utils/raw-asset-reference-index.ts
|
|
10040
|
-
import { readdir as readdir7, readFile as
|
|
10041
|
-
import { join as
|
|
10401
|
+
import { readdir as readdir7, readFile as readFile22 } from "fs/promises";
|
|
10402
|
+
import { join as join42, relative as relative6, sep as sep4 } from "path";
|
|
10042
10403
|
var PREVIEW_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "avif", "pdf", "mp3", "wav", "m4a", "mp4", "webm"]);
|
|
10043
10404
|
async function walkFiles(root, dir = root) {
|
|
10044
10405
|
let entries;
|
|
@@ -10049,7 +10410,7 @@ async function walkFiles(root, dir = root) {
|
|
|
10049
10410
|
}
|
|
10050
10411
|
const out = [];
|
|
10051
10412
|
for (const entry of entries) {
|
|
10052
|
-
const path =
|
|
10413
|
+
const path = join42(dir, entry.name);
|
|
10053
10414
|
if (entry.isDirectory()) out.push(...await walkFiles(root, path));
|
|
10054
10415
|
else if (entry.isFile()) out.push(relative6(root, path).split(sep4).join("/"));
|
|
10055
10416
|
}
|
|
@@ -10065,7 +10426,7 @@ function addInbound(map, target, source) {
|
|
|
10065
10426
|
map.set(target, refs);
|
|
10066
10427
|
}
|
|
10067
10428
|
async function buildRawAssetReferenceIndex(vault) {
|
|
10068
|
-
const assetRoot =
|
|
10429
|
+
const assetRoot = join42(vault, "raw", "assets");
|
|
10069
10430
|
const relativeAssets = await walkFiles(assetRoot);
|
|
10070
10431
|
const assets = relativeAssets.map((path) => `raw/assets/${path}`).sort((a, b) => a.localeCompare(b));
|
|
10071
10432
|
const assetSet = new Set(assets);
|
|
@@ -10081,7 +10442,7 @@ async function buildRawAssetReferenceIndex(vault) {
|
|
|
10081
10442
|
for (const page of scan.data.allMarkdown) {
|
|
10082
10443
|
let text;
|
|
10083
10444
|
try {
|
|
10084
|
-
text = await
|
|
10445
|
+
text = await readFile22(page.absPath, "utf8");
|
|
10085
10446
|
} catch {
|
|
10086
10447
|
continue;
|
|
10087
10448
|
}
|
|
@@ -10117,7 +10478,7 @@ async function buildRawAssetReferenceIndex(vault) {
|
|
|
10117
10478
|
|
|
10118
10479
|
// src/utils/source-disposal.ts
|
|
10119
10480
|
function sha256(bytes) {
|
|
10120
|
-
return
|
|
10481
|
+
return createHash8("sha256").update(bytes).digest("hex");
|
|
10121
10482
|
}
|
|
10122
10483
|
function exactTarget(path) {
|
|
10123
10484
|
if (/[?*\[\]{}]/.test(path) || !path.includes("/") || path.endsWith("/")) return err("RAW_DISPOSAL_TARGET_INVALID", { path, message: "exact file path required; globs, basenames, and directories are refused" });
|
|
@@ -10141,7 +10502,7 @@ async function planSourceDisposal(input) {
|
|
|
10141
10502
|
if (sensitive.length > 0) return err("SENSITIVE_CONTENT_DETECTED", { findings: sensitive });
|
|
10142
10503
|
const target = await resolveExistingRegularFileInsideVault(input.vault, input.rawPath);
|
|
10143
10504
|
if (!target.ok) return err("RAW_DISPOSAL_TARGET_INVALID", { path: input.rawPath, cause: target });
|
|
10144
|
-
const bytes = await
|
|
10505
|
+
const bytes = await readFile23(target.data);
|
|
10145
10506
|
const size = bytes.byteLength;
|
|
10146
10507
|
let typedReferences = [];
|
|
10147
10508
|
let otherReferences = [];
|
|
@@ -10265,7 +10626,7 @@ async function runSourceDisposal(input) {
|
|
|
10265
10626
|
import { existsSync as existsSync15, readFileSync as readFileSync16 } from "fs";
|
|
10266
10627
|
import { execSync as nodeExecSync } from "child_process";
|
|
10267
10628
|
import { hostname as nodeHostname, platform as nodePlatform } from "os";
|
|
10268
|
-
import { join as
|
|
10629
|
+
import { join as join43 } from "path";
|
|
10269
10630
|
var SSH_TIMEOUT_MS = 15e3;
|
|
10270
10631
|
var TIMER_UNIT = "agent-memory-trends.timer";
|
|
10271
10632
|
var SERVICE_UNIT = "agent-memory-trends.service";
|
|
@@ -10495,7 +10856,7 @@ async function runFleetHealth(input) {
|
|
|
10495
10856
|
const home = input.home ?? env.HOME ?? "";
|
|
10496
10857
|
const osHostname = input.osHostname ?? env.HOSTNAME ?? nodeHostname();
|
|
10497
10858
|
const vault = input.vault ?? env.WIKI_PATH;
|
|
10498
|
-
const file = input.file ?? (vault ?
|
|
10859
|
+
const file = input.file ?? (vault ? join43(vault, FLEET_REL_PATH) : void 0);
|
|
10499
10860
|
if (!file) {
|
|
10500
10861
|
return {
|
|
10501
10862
|
exitCode: ExitCode.NO_VAULT_CONFIGURED,
|
|
@@ -10580,7 +10941,7 @@ async function runFleetHealth(input) {
|
|
|
10580
10941
|
|
|
10581
10942
|
// src/utils/auto-commit.ts
|
|
10582
10943
|
import { existsSync as existsSync16 } from "fs";
|
|
10583
|
-
import { isAbsolute as isAbsolute3, join as
|
|
10944
|
+
import { isAbsolute as isAbsolute3, join as join44, normalize } from "path";
|
|
10584
10945
|
function isValidLastOpPath(file) {
|
|
10585
10946
|
if (!file || typeof file !== "string") return false;
|
|
10586
10947
|
const trimmed = file.trim();
|
|
@@ -10601,7 +10962,7 @@ async function postCommit(vault, exitCode) {
|
|
|
10601
10962
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
10602
10963
|
const autoCommit = process.env.AUTO_COMMIT ?? dotenv["AUTO_COMMIT"];
|
|
10603
10964
|
if (autoCommit === "false") return;
|
|
10604
|
-
if (!existsSync16(
|
|
10965
|
+
if (!existsSync16(join44(vault, ".git"))) return;
|
|
10605
10966
|
const lastOps = readLastOp(vault);
|
|
10606
10967
|
if (lastOps.length === 0) return;
|
|
10607
10968
|
const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
|
|
@@ -10641,90 +11002,6 @@ async function postCommit(vault, exitCode) {
|
|
|
10641
11002
|
|
|
10642
11003
|
// src/commands/mcp-auth.ts
|
|
10643
11004
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
|
|
10644
|
-
|
|
10645
|
-
// src/utils/mcp-token-map.ts
|
|
10646
|
-
import { createHash as createHash8, randomBytes } from "crypto";
|
|
10647
|
-
import yaml from "js-yaml";
|
|
10648
|
-
var HOST_ID_RE = /^[a-z][a-z0-9-]{1,62}$/;
|
|
10649
|
-
function writerIdFromValue(value) {
|
|
10650
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
10651
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
10652
|
-
const rec = value;
|
|
10653
|
-
if (typeof rec.writer_id === "string" && rec.writer_id.length > 0) return rec.writer_id;
|
|
10654
|
-
if (typeof rec.host_id === "string" && rec.host_id.length > 0) return rec.host_id;
|
|
10655
|
-
}
|
|
10656
|
-
return void 0;
|
|
10657
|
-
}
|
|
10658
|
-
function parseAllowedVaultIds(raw) {
|
|
10659
|
-
if (!raw || raw.length === 0) return { vaults: [] };
|
|
10660
|
-
const vaults = [];
|
|
10661
|
-
const seen = /* @__PURE__ */ new Set();
|
|
10662
|
-
for (const item of raw) {
|
|
10663
|
-
const id = item.trim();
|
|
10664
|
-
if (!id) continue;
|
|
10665
|
-
if (id.includes("*") || id.includes("/") || !HOST_ID_RE.test(id)) {
|
|
10666
|
-
return { error: "INVALID_ALLOWED_VAULTS" };
|
|
10667
|
-
}
|
|
10668
|
-
if (seen.has(id)) continue;
|
|
10669
|
-
seen.add(id);
|
|
10670
|
-
vaults.push(id);
|
|
10671
|
-
}
|
|
10672
|
-
return { vaults };
|
|
10673
|
-
}
|
|
10674
|
-
function parseMcpTokenMap(yamlText) {
|
|
10675
|
-
const parsed = yaml.load(yamlText);
|
|
10676
|
-
const map = /* @__PURE__ */ new Map();
|
|
10677
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
10678
|
-
return map;
|
|
10679
|
-
}
|
|
10680
|
-
const records = parsed;
|
|
10681
|
-
const nested = records.tokens;
|
|
10682
|
-
const source = nested && typeof nested === "object" && !Array.isArray(nested) ? nested : records;
|
|
10683
|
-
for (const [hash, value] of Object.entries(source)) {
|
|
10684
|
-
if (hash === "tokens") continue;
|
|
10685
|
-
const writerId = writerIdFromValue(value);
|
|
10686
|
-
if (!writerId) continue;
|
|
10687
|
-
const normalized = hash.trim().toLowerCase();
|
|
10688
|
-
if (!/^[0-9a-f]{64}$/.test(normalized)) continue;
|
|
10689
|
-
map.set(normalized, writerId);
|
|
10690
|
-
}
|
|
10691
|
-
return map;
|
|
10692
|
-
}
|
|
10693
|
-
function generateHostBearer(rng) {
|
|
10694
|
-
const bytes = (rng ?? (() => randomBytes(32)))();
|
|
10695
|
-
const raw = bytes.toString("base64url");
|
|
10696
|
-
const hashHex = createHash8("sha256").update(raw, "utf8").digest("hex");
|
|
10697
|
-
return { raw, hashHex };
|
|
10698
|
-
}
|
|
10699
|
-
function formatHostRow(hashHex, hostId, allowedVaults) {
|
|
10700
|
-
if (allowedVaults.length === 0) {
|
|
10701
|
-
return `${hashHex}: ${hostId}
|
|
10702
|
-
`;
|
|
10703
|
-
}
|
|
10704
|
-
return `${hashHex}:
|
|
10705
|
-
writer_id: ${hostId}
|
|
10706
|
-
allowed_vaults: [${allowedVaults.join(", ")}]
|
|
10707
|
-
`;
|
|
10708
|
-
}
|
|
10709
|
-
function appendHostHash(yamlText, hashHex, hostId, allowedVaults) {
|
|
10710
|
-
if (!HOST_ID_RE.test(hostId)) {
|
|
10711
|
-
return { error: "INVALID_HOST_ID" };
|
|
10712
|
-
}
|
|
10713
|
-
const granted = parseAllowedVaultIds(allowedVaults);
|
|
10714
|
-
if ("error" in granted) return granted;
|
|
10715
|
-
const map = parseMcpTokenMap(yamlText);
|
|
10716
|
-
const normalizedHash = hashHex.trim().toLowerCase();
|
|
10717
|
-
if ([...map.values()].includes(hostId)) {
|
|
10718
|
-
return { error: "DUPLICATE_HOST_ID" };
|
|
10719
|
-
}
|
|
10720
|
-
if (map.has(normalizedHash)) {
|
|
10721
|
-
return { error: "DUPLICATE_HASH" };
|
|
10722
|
-
}
|
|
10723
|
-
return { yaml: yamlText.replace(/\s*$/, "") + `
|
|
10724
|
-
${formatHostRow(normalizedHash, hostId, granted.vaults)}` };
|
|
10725
|
-
}
|
|
10726
|
-
|
|
10727
|
-
// src/commands/mcp-auth.ts
|
|
10728
11005
|
function isEnoent(error) {
|
|
10729
11006
|
return error.code === "ENOENT";
|
|
10730
11007
|
}
|
|
@@ -10733,7 +11010,7 @@ function preflight(error) {
|
|
|
10733
11010
|
}
|
|
10734
11011
|
async function runMcpAuthIssueHost(input) {
|
|
10735
11012
|
const isTty = input.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
10736
|
-
const
|
|
11013
|
+
const readFile24 = input.readFile ?? readFileSync17;
|
|
10737
11014
|
const writeFile10 = input.writeFile ?? writeFileSync8;
|
|
10738
11015
|
const stderrWrite = input.stderrWrite ?? ((s) => {
|
|
10739
11016
|
process.stderr.write(s);
|
|
@@ -10748,7 +11025,7 @@ async function runMcpAuthIssueHost(input) {
|
|
|
10748
11025
|
if (write && !isTty) return preflight("NO_TTY");
|
|
10749
11026
|
let yamlText = "";
|
|
10750
11027
|
try {
|
|
10751
|
-
yamlText =
|
|
11028
|
+
yamlText = readFile24(mapPath, "utf8");
|
|
10752
11029
|
} catch (error) {
|
|
10753
11030
|
if (!isEnoent(error)) throw error;
|
|
10754
11031
|
}
|
|
@@ -10930,7 +11207,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
10930
11207
|
if (dirty) {
|
|
10931
11208
|
return emit(dirty, void 0, { postCommit: false });
|
|
10932
11209
|
}
|
|
10933
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
11210
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-ATDFVLJV.js");
|
|
10934
11211
|
const run = await runManagedWriteTransaction2({
|
|
10935
11212
|
vault,
|
|
10936
11213
|
command,
|
|
@@ -10960,7 +11237,7 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
10960
11237
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
10961
11238
|
});
|
|
10962
11239
|
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) => {
|
|
10963
|
-
const out = opts.out ??
|
|
11240
|
+
const out = opts.out ?? join45(vault, ".skillwiki", "graph.json");
|
|
10964
11241
|
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
10965
11242
|
});
|
|
10966
11243
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
@@ -11571,7 +11848,18 @@ program.command("doctor").description("diagnose skillwiki setup issues").option(
|
|
|
11571
11848
|
currentVersion: pkg.version,
|
|
11572
11849
|
cwd: process.cwd(),
|
|
11573
11850
|
checkSnapshotter: !!opts.checkSnapshotter,
|
|
11574
|
-
checkMcp: !!opts.checkMcp
|
|
11851
|
+
checkMcp: !!opts.checkMcp,
|
|
11852
|
+
env: process.env
|
|
11853
|
+
})));
|
|
11854
|
+
program.command("connect").description(CONNECT_DESCRIPTION).option("--from-file <path>", "chat-attached env file (not Drive / \u4E91\u76D8)").option("--from-stdin", "read env file from stdin (no echo)").option("--dry-run", "validate without writing ~/.skillwiki/.env").option("--force", "overwrite a different existing token").action(async (opts) => emit(await runConnect({
|
|
11855
|
+
home: process.env.HOME ?? "",
|
|
11856
|
+
fromFile: opts.fromFile,
|
|
11857
|
+
fromStdin: !!opts.fromStdin,
|
|
11858
|
+
dryRun: !!opts.dryRun,
|
|
11859
|
+
force: !!opts.force,
|
|
11860
|
+
checkMcp: !opts.dryRun,
|
|
11861
|
+
currentVersion: pkg.version,
|
|
11862
|
+
env: process.env
|
|
11575
11863
|
})));
|
|
11576
11864
|
program.command("status [vault]").description("output vault diagnostics").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11577
11865
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -11961,7 +12249,7 @@ program.command("observe [vault]").description("create a raw transcript observat
|
|
|
11961
12249
|
})
|
|
11962
12250
|
);
|
|
11963
12251
|
});
|
|
11964
|
-
program.command("session-brief [vault]").description("render or refresh the bounded startup session brief").option("--project <slug>", "project slug, or auto for deterministic detection", "auto").option("--write", "write meta/latest-session-brief.md and local cache files", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
12252
|
+
program.command("session-brief [vault]").description("render or refresh the bounded startup session brief").option("--project <slug>", "project slug, or auto for deterministic detection", "auto").option("--write", "write meta/latest-session-brief.md and local cache files", false).option("--agent-memory-run-state <path>", "explicit agent-memory latest-run JSON path").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11965
12253
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
11966
12254
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
11967
12255
|
else if (opts.write) return emitManagedVaultWrite(
|
|
@@ -11971,6 +12259,7 @@ program.command("session-brief [vault]").description("render or refresh the boun
|
|
|
11971
12259
|
vault: v.vault,
|
|
11972
12260
|
project: opts.project,
|
|
11973
12261
|
write: true,
|
|
12262
|
+
agentMemoryRunState: opts.agentMemoryRunState,
|
|
11974
12263
|
cwd: process.cwd(),
|
|
11975
12264
|
env: { SKILLWIKI_PROJECT: process.env.SKILLWIKI_PROJECT }
|
|
11976
12265
|
}),
|
|
@@ -11980,6 +12269,7 @@ program.command("session-brief [vault]").description("render or refresh the boun
|
|
|
11980
12269
|
vault: v.vault,
|
|
11981
12270
|
project: opts.project,
|
|
11982
12271
|
write: false,
|
|
12272
|
+
agentMemoryRunState: opts.agentMemoryRunState,
|
|
11983
12273
|
cwd: process.cwd(),
|
|
11984
12274
|
env: { SKILLWIKI_PROJECT: process.env.SKILLWIKI_PROJECT }
|
|
11985
12275
|
}), v.vault, { postCommit: false });
|