skillwiki 0.10.73 → 0.10.76
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-R5SDVKHS.js → chunk-5XT6MBWA.js} +4 -1
- package/dist/{chunk-YKVJ2A3O.js → chunk-CLF6DZ6C.js} +258 -59
- package/dist/cli.js +161 -19
- package/dist/{managed-write-preflight-PUEJOXJO.js → managed-write-preflight-LSG2Y24X.js} +1 -1
- package/dist/skillwiki-mcp.js +2 -2
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/.cursor-plugin/plugin.json +1 -1
- package/skills/agents/proj-work.md +10 -4
- package/skills/package.json +1 -1
- package/skills/proj-work/SKILL.md +7 -5
- package/skills/skills/proj-work/SKILL.md +7 -5
- package/skills/skills/skillwiki-mcp/SKILL.md +11 -2
- package/skills/skills/using-skillwiki/SKILL.md +5 -4
- package/skills/skills/wiki-lint/SKILL.md +2 -2
- package/skills/skills/wiki-sync/SKILL.md +2 -2
- package/skills/skillwiki-mcp/SKILL.md +11 -2
- package/skills/using-skillwiki/SKILL.md +5 -4
- package/skills/wiki-lint/SKILL.md +2 -2
- package/skills/wiki-sync/SKILL.md +2 -2
|
@@ -3331,7 +3331,7 @@ function buildCliSurface() {
|
|
|
3331
3331
|
program.command("lint").option("--days <n>").option("--lines <n>").option("--log-threshold <n>").option("--fix").option("--only <bucket>").option("--summary").option("--examples <n>").option("--wiki <name>");
|
|
3332
3332
|
program.command("config");
|
|
3333
3333
|
program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
|
|
3334
|
-
program.command("doctor").option("--check-snapshotter");
|
|
3334
|
+
program.command("doctor").option("--check-snapshotter").option("--check-mcp");
|
|
3335
3335
|
program.command("status").option("--wiki <name>");
|
|
3336
3336
|
program.command("archive").option("--wiki <name>").option("--cascade").option("--apply").option("--approve <token>").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>");
|
|
3337
3337
|
program.command("remove").option("--wiki <name>").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>").option("--reason <text>");
|
|
@@ -3360,6 +3360,7 @@ function buildCliSurface() {
|
|
|
3360
3360
|
program.command("project-page");
|
|
3361
3361
|
program.command("write-preflight").option("--command <name>").option("--dirty-threshold <n>").option("--skip-dirty").option("--prior-artifact-file <path>").option("--prior-artifact-text <text>").option("--consecutive-no-decision <n>").option("--no-decision-threshold <n>").option("--human-allow").option("--mission-kind <kind>").option("--skip-mission").option("--project <slug>").option("--capture-day <date>").option("--capture-budget <n>").option("--severity <level>").option("--skip-budget").option("--checks <list>").option("--wiki <name>");
|
|
3362
3362
|
program.command("mcp");
|
|
3363
|
+
program.command("mcp-auth");
|
|
3363
3364
|
const graphCmd = program.commands.find((c) => c.name() === "graph");
|
|
3364
3365
|
graphCmd.command("build").option("--out <path>").option("--wiki <name>");
|
|
3365
3366
|
const vectorsCmd = program.commands.find((c) => c.name() === "vectors");
|
|
@@ -3436,6 +3437,8 @@ function buildCliSurface() {
|
|
|
3436
3437
|
fleetCmd.command("validate");
|
|
3437
3438
|
fleetCmd.command("context").option("--file <path>").option("--host-id <id>");
|
|
3438
3439
|
fleetCmd.command("health").option("--file <path>").option("--host-id <id>").option("--json");
|
|
3440
|
+
const mcpAuth = program.commands.find((c) => c.name() === "mcp-auth");
|
|
3441
|
+
mcpAuth.command("issue-host").requiredOption("--host-id <id>").option("--map <path>").option("--write");
|
|
3439
3442
|
const surface = /* @__PURE__ */ new Map();
|
|
3440
3443
|
const rootFlags = new Set(program.options.map((o) => o.long ?? o.short).filter((f) => f != null));
|
|
3441
3444
|
function walk(cmd, prefix, parentFlags) {
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
snapshotterAliasForLocalHost,
|
|
41
41
|
toUndirectedWeighted,
|
|
42
42
|
writeDotenv
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-5XT6MBWA.js";
|
|
44
44
|
import {
|
|
45
45
|
atomicWriteText,
|
|
46
46
|
prepareTypedPage
|
|
@@ -1079,7 +1079,7 @@ async function runConfigPath(input) {
|
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
1081
|
// src/doctor/runner.ts
|
|
1082
|
-
import { existsSync as
|
|
1082
|
+
import { existsSync as existsSync18 } from "fs";
|
|
1083
1083
|
|
|
1084
1084
|
// src/doctor/probes/environment.ts
|
|
1085
1085
|
import { existsSync as existsSync4, lstatSync, readlinkSync } from "fs";
|
|
@@ -3981,6 +3981,204 @@ var dsStoreNoiseProbe = {
|
|
|
3981
3981
|
}
|
|
3982
3982
|
};
|
|
3983
3983
|
|
|
3984
|
+
// src/doctor/probes/mcp-handshake.ts
|
|
3985
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3986
|
+
import { join as join20 } from "path";
|
|
3987
|
+
var DEFAULT_MCP_URL = "https://wiki.karldigi.dev/mcp";
|
|
3988
|
+
var URL_ENV = "SKILLWIKI_MCP_URL";
|
|
3989
|
+
var TOKEN_ENV = "SKILLWIKI_MCP_TOKEN";
|
|
3990
|
+
function mcpEnv(ctx) {
|
|
3991
|
+
return ctx.input.env ?? process.env;
|
|
3992
|
+
}
|
|
3993
|
+
function checkMcpUrl(ctx) {
|
|
3994
|
+
const env = mcpEnv(ctx);
|
|
3995
|
+
const override = env[URL_ENV];
|
|
3996
|
+
if (typeof override === "string" && override.trim().length > 0) {
|
|
3997
|
+
return check("pass", "mcp_url_configured", "HTTP MCP URL", "configured via env override");
|
|
3998
|
+
}
|
|
3999
|
+
return check(
|
|
4000
|
+
"pass",
|
|
4001
|
+
"mcp_url_configured",
|
|
4002
|
+
"HTTP MCP URL",
|
|
4003
|
+
`plugin default ${DEFAULT_MCP_URL}`
|
|
4004
|
+
);
|
|
4005
|
+
}
|
|
4006
|
+
function checkMcpCredential(ctx) {
|
|
4007
|
+
const env = mcpEnv(ctx);
|
|
4008
|
+
const value = env[TOKEN_ENV];
|
|
4009
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
4010
|
+
return check("pass", "mcp_credential_present", "HTTP MCP auth", "present");
|
|
4011
|
+
}
|
|
4012
|
+
return check(
|
|
4013
|
+
"warn",
|
|
4014
|
+
"mcp_credential_present",
|
|
4015
|
+
"HTTP MCP auth",
|
|
4016
|
+
"not set \u2014 set in process env or host Configure (value not shown)"
|
|
4017
|
+
);
|
|
4018
|
+
}
|
|
4019
|
+
function checkFrozenLeafWritePath(ctx) {
|
|
4020
|
+
const vault = ctx.resolvedPath;
|
|
4021
|
+
if (!vault || !existsSync17(join20(vault, ".WIKI_GIT_FROZEN"))) {
|
|
4022
|
+
return check(
|
|
4023
|
+
"pass",
|
|
4024
|
+
"mcp_frozen_leaf_write_path",
|
|
4025
|
+
"HTTP MCP write path",
|
|
4026
|
+
"not a frozen leaf"
|
|
4027
|
+
);
|
|
4028
|
+
}
|
|
4029
|
+
return check(
|
|
4030
|
+
"pass",
|
|
4031
|
+
"mcp_frozen_leaf_write_path",
|
|
4032
|
+
"HTTP MCP write path",
|
|
4033
|
+
"frozen leaf \u2014 writes must use HTTP MCP, not vault git or raw/transcripts/"
|
|
4034
|
+
);
|
|
4035
|
+
}
|
|
4036
|
+
var HANDSHAKE_ID = "mcp_handshake";
|
|
4037
|
+
var HANDSHAKE_LABEL = "HTTP MCP handshake";
|
|
4038
|
+
var WORKITEM_TOOL = "wiki_workitem_write";
|
|
4039
|
+
function parseCoreSemver(version) {
|
|
4040
|
+
const m = version.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
4041
|
+
if (!m) return null;
|
|
4042
|
+
return { major: Number.parseInt(m[1], 10), minor: Number.parseInt(m[2], 10), patch: Number.parseInt(m[3], 10) };
|
|
4043
|
+
}
|
|
4044
|
+
function versionLessThan(a, b) {
|
|
4045
|
+
const pa = parseCoreSemver(a);
|
|
4046
|
+
const pb = parseCoreSemver(b);
|
|
4047
|
+
if (!pa || !pb) return false;
|
|
4048
|
+
if (pa.major !== pb.major) return pa.major < pb.major;
|
|
4049
|
+
if (pa.minor !== pb.minor) return pa.minor < pb.minor;
|
|
4050
|
+
return pa.patch < pb.patch;
|
|
4051
|
+
}
|
|
4052
|
+
function redactSecret(detail, secret) {
|
|
4053
|
+
if (!secret || secret.length === 0 || !detail.includes(secret)) return detail;
|
|
4054
|
+
return detail.split(secret).join("[REDACTED]");
|
|
4055
|
+
}
|
|
4056
|
+
function handshakeRow(status, detail, secret) {
|
|
4057
|
+
return check(status, HANDSHAKE_ID, HANDSHAKE_LABEL, redactSecret(detail, secret));
|
|
4058
|
+
}
|
|
4059
|
+
function parseMcpBody(text) {
|
|
4060
|
+
const trimmed = text.trim();
|
|
4061
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
4062
|
+
return JSON.parse(trimmed);
|
|
4063
|
+
}
|
|
4064
|
+
for (const line of trimmed.split(/\r?\n/)) {
|
|
4065
|
+
const prefix = line.startsWith("data:") ? line.slice(5).trim() : "";
|
|
4066
|
+
if (prefix.startsWith("{")) return JSON.parse(prefix);
|
|
4067
|
+
}
|
|
4068
|
+
throw new Error("non-JSON MCP body");
|
|
4069
|
+
}
|
|
4070
|
+
function jsonRpcResult(body) {
|
|
4071
|
+
if (typeof body !== "object" || body === null) {
|
|
4072
|
+
throw new Error("invalid JSON-RPC body");
|
|
4073
|
+
}
|
|
4074
|
+
const rec = body;
|
|
4075
|
+
if (rec.error) {
|
|
4076
|
+
throw new Error(typeof rec.error.message === "string" ? rec.error.message : "JSON-RPC error");
|
|
4077
|
+
}
|
|
4078
|
+
if (typeof rec.result !== "object" || rec.result === null) {
|
|
4079
|
+
throw new Error("missing JSON-RPC result");
|
|
4080
|
+
}
|
|
4081
|
+
return rec.result;
|
|
4082
|
+
}
|
|
4083
|
+
async function mcpJsonRpc(fetchFn, url, token, method, id, params, sessionId) {
|
|
4084
|
+
const headers = {
|
|
4085
|
+
Authorization: `Bearer ${token}`,
|
|
4086
|
+
"Content-Type": "application/json",
|
|
4087
|
+
Accept: "application/json, text/event-stream"
|
|
4088
|
+
};
|
|
4089
|
+
if (sessionId) headers["mcp-session-id"] = sessionId;
|
|
4090
|
+
const res = await fetchFn(url, {
|
|
4091
|
+
method: "POST",
|
|
4092
|
+
headers,
|
|
4093
|
+
body: JSON.stringify({ jsonrpc: "2.0", id, method, params })
|
|
4094
|
+
});
|
|
4095
|
+
if (!res.ok) {
|
|
4096
|
+
throw new Error(`HTTP ${res.status}`);
|
|
4097
|
+
}
|
|
4098
|
+
const nextSession = res.headers.get("mcp-session-id") ?? sessionId;
|
|
4099
|
+
const result = jsonRpcResult(parseMcpBody(await res.text()));
|
|
4100
|
+
return { result, sessionId: nextSession ?? void 0 };
|
|
4101
|
+
}
|
|
4102
|
+
function toolNames(result) {
|
|
4103
|
+
const tools = result.tools;
|
|
4104
|
+
if (!Array.isArray(tools)) return [];
|
|
4105
|
+
return tools.map((t) => typeof t === "object" && t !== null && typeof t.name === "string" ? t.name : "").filter((name) => name.length > 0);
|
|
4106
|
+
}
|
|
4107
|
+
async function checkMcpHandshake(ctx) {
|
|
4108
|
+
if (!ctx.input.checkMcp) {
|
|
4109
|
+
return handshakeRow("pass", "not requested \u2014 check skipped");
|
|
4110
|
+
}
|
|
4111
|
+
const env = mcpEnv(ctx);
|
|
4112
|
+
const url = typeof env[URL_ENV] === "string" && env[URL_ENV].trim().length > 0 ? env[URL_ENV].trim() : DEFAULT_MCP_URL;
|
|
4113
|
+
const token = typeof env[TOKEN_ENV] === "string" ? env[TOKEN_ENV] : "";
|
|
4114
|
+
if (token.trim().length === 0) {
|
|
4115
|
+
return handshakeRow("error", "auth not set \u2014 handshake not attempted (value not shown)");
|
|
4116
|
+
}
|
|
4117
|
+
const fetchFn = ctx.input.mcpFetch ?? globalThis.fetch;
|
|
4118
|
+
const shipped = ctx.input.currentVersion;
|
|
4119
|
+
try {
|
|
4120
|
+
const init = await mcpJsonRpc(
|
|
4121
|
+
fetchFn,
|
|
4122
|
+
url,
|
|
4123
|
+
token,
|
|
4124
|
+
"initialize",
|
|
4125
|
+
1,
|
|
4126
|
+
{
|
|
4127
|
+
protocolVersion: "2025-11-25",
|
|
4128
|
+
capabilities: {},
|
|
4129
|
+
clientInfo: { name: "skillwiki-doctor", version: shipped }
|
|
4130
|
+
}
|
|
4131
|
+
);
|
|
4132
|
+
const serverInfo = init.result.serverInfo;
|
|
4133
|
+
const advertised = typeof serverInfo === "object" && serverInfo !== null && typeof serverInfo.version === "string" ? serverInfo.version : "";
|
|
4134
|
+
if (!advertised) {
|
|
4135
|
+
return handshakeRow("error", "handshake failed \u2014 missing serverInfo.version", token);
|
|
4136
|
+
}
|
|
4137
|
+
const listed = await mcpJsonRpc(
|
|
4138
|
+
fetchFn,
|
|
4139
|
+
url,
|
|
4140
|
+
token,
|
|
4141
|
+
"tools/list",
|
|
4142
|
+
2,
|
|
4143
|
+
{},
|
|
4144
|
+
init.sessionId
|
|
4145
|
+
);
|
|
4146
|
+
const tools = toolNames(listed.result);
|
|
4147
|
+
const frozen = Boolean(ctx.resolvedPath && existsSync17(join20(ctx.resolvedPath, ".WIKI_GIT_FROZEN")));
|
|
4148
|
+
if (frozen && !tools.includes(WORKITEM_TOOL)) {
|
|
4149
|
+
return handshakeRow(
|
|
4150
|
+
"error",
|
|
4151
|
+
`captures-only tool list (${tools.length} tools, missing ${WORKITEM_TOOL}) on frozen leaf \u2014 advertised ${advertised}`,
|
|
4152
|
+
token
|
|
4153
|
+
);
|
|
4154
|
+
}
|
|
4155
|
+
if (versionLessThan(advertised, shipped)) {
|
|
4156
|
+
return handshakeRow(
|
|
4157
|
+
"warn",
|
|
4158
|
+
`advertised ${advertised} lags shipped ${shipped} (${tools.length} tools)`,
|
|
4159
|
+
token
|
|
4160
|
+
);
|
|
4161
|
+
}
|
|
4162
|
+
return handshakeRow("pass", `advertised ${advertised}, ${tools.length} tools`, token);
|
|
4163
|
+
} catch (e) {
|
|
4164
|
+
const raw = e instanceof Error ? e.message : String(e);
|
|
4165
|
+
const safe = redactSecret(raw, token);
|
|
4166
|
+
return handshakeRow("error", `handshake failed \u2014 ${safe}`, token);
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
var mcpHandshakeProbe = {
|
|
4170
|
+
id: "mcp",
|
|
4171
|
+
label: "HTTP MCP",
|
|
4172
|
+
async run(ctx) {
|
|
4173
|
+
return [
|
|
4174
|
+
checkMcpUrl(ctx),
|
|
4175
|
+
checkMcpCredential(ctx),
|
|
4176
|
+
checkFrozenLeafWritePath(ctx),
|
|
4177
|
+
await checkMcpHandshake(ctx)
|
|
4178
|
+
];
|
|
4179
|
+
}
|
|
4180
|
+
};
|
|
4181
|
+
|
|
3984
4182
|
// src/doctor/probes/index.ts
|
|
3985
4183
|
var DOCTOR_PROBES = [
|
|
3986
4184
|
environmentProbe,
|
|
@@ -3994,7 +4192,8 @@ var DOCTOR_PROBES = [
|
|
|
3994
4192
|
metricsProbe,
|
|
3995
4193
|
fuseStalenessProbe,
|
|
3996
4194
|
activationMarkerProbe,
|
|
3997
|
-
dsStoreNoiseProbe
|
|
4195
|
+
dsStoreNoiseProbe,
|
|
4196
|
+
mcpHandshakeProbe
|
|
3998
4197
|
];
|
|
3999
4198
|
|
|
4000
4199
|
// src/doctor/runner.ts
|
|
@@ -4005,7 +4204,7 @@ function resolveSnapshotGitWorktree(home) {
|
|
|
4005
4204
|
const configured = resolveConfiguredSnapshotWorktree(home);
|
|
4006
4205
|
if (configured) return configured;
|
|
4007
4206
|
const defaultPath = "/root/wiki-git";
|
|
4008
|
-
return
|
|
4207
|
+
return existsSync18(defaultPath) ? defaultPath : void 0;
|
|
4009
4208
|
}
|
|
4010
4209
|
var DoctorRunner = class {
|
|
4011
4210
|
probes;
|
|
@@ -4101,8 +4300,8 @@ function readCliPackageJson(baseUrl = import.meta.url) {
|
|
|
4101
4300
|
|
|
4102
4301
|
// src/utils/vault-write-gates.ts
|
|
4103
4302
|
import { execFileSync } from "child_process";
|
|
4104
|
-
import { existsSync as
|
|
4105
|
-
import { join as
|
|
4303
|
+
import { existsSync as existsSync19, readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync3 } from "fs";
|
|
4304
|
+
import { join as join21, relative as relative2 } from "path";
|
|
4106
4305
|
var DEFAULT_DIRTY_VOLUME_THRESHOLD = 50;
|
|
4107
4306
|
var DEFAULT_CAPTURE_BUDGET = 20;
|
|
4108
4307
|
var DEFAULT_NO_DECISION_STREAK = 3;
|
|
@@ -4164,7 +4363,7 @@ function measureDirtyVolume(vault) {
|
|
|
4164
4363
|
is_git_repo: false,
|
|
4165
4364
|
...extra
|
|
4166
4365
|
});
|
|
4167
|
-
if (!
|
|
4366
|
+
if (!existsSync19(vault) || !statSync3(vault).isDirectory()) {
|
|
4168
4367
|
return empty();
|
|
4169
4368
|
}
|
|
4170
4369
|
const gitDir = git(vault, ["rev-parse", "--absolute-git-dir"]);
|
|
@@ -4202,8 +4401,8 @@ function measureDirtyVolume(vault) {
|
|
|
4202
4401
|
if (!rel) continue;
|
|
4203
4402
|
if (code === "??") {
|
|
4204
4403
|
untracked += 1;
|
|
4205
|
-
const abs =
|
|
4206
|
-
if (
|
|
4404
|
+
const abs = join21(vault, rel);
|
|
4405
|
+
if (existsSync19(abs) && statSync3(abs).isDirectory()) {
|
|
4207
4406
|
const files = listFilesRecursive(abs);
|
|
4208
4407
|
expanded += files.length;
|
|
4209
4408
|
addBucket(rel, files.length);
|
|
@@ -4239,7 +4438,7 @@ function listFilesRecursive(dir) {
|
|
|
4239
4438
|
}
|
|
4240
4439
|
for (const name of entries) {
|
|
4241
4440
|
if (name === ".git") continue;
|
|
4242
|
-
const p =
|
|
4441
|
+
const p = join21(dir, name);
|
|
4243
4442
|
try {
|
|
4244
4443
|
const st = statSync3(p);
|
|
4245
4444
|
if (st.isDirectory()) out.push(...listFilesRecursive(p));
|
|
@@ -4334,7 +4533,7 @@ var CAPTURE_HYGIENE_CONTRACT = `After each productive cycle (or when daily captu
|
|
|
4334
4533
|
function listProjectDayCaptures(vault, project, day) {
|
|
4335
4534
|
const found = [];
|
|
4336
4535
|
const slug = project.replace(/^\[\[/, "").replace(/\]\]$/, "").trim();
|
|
4337
|
-
if (!slug || !
|
|
4536
|
+
if (!slug || !existsSync19(vault)) return found;
|
|
4338
4537
|
const consider = (abs, rel) => {
|
|
4339
4538
|
if (!rel.endsWith(".md")) return;
|
|
4340
4539
|
const base = rel.split(/[/\\]/).pop() ?? "";
|
|
@@ -4362,7 +4561,7 @@ function listProjectDayCaptures(vault, project, day) {
|
|
|
4362
4561
|
}
|
|
4363
4562
|
};
|
|
4364
4563
|
const walk = (dir, relBase) => {
|
|
4365
|
-
if (!
|
|
4564
|
+
if (!existsSync19(dir)) return;
|
|
4366
4565
|
let entries;
|
|
4367
4566
|
try {
|
|
4368
4567
|
entries = readdirSync7(dir);
|
|
@@ -4370,7 +4569,7 @@ function listProjectDayCaptures(vault, project, day) {
|
|
|
4370
4569
|
return;
|
|
4371
4570
|
}
|
|
4372
4571
|
for (const name of entries) {
|
|
4373
|
-
const abs =
|
|
4572
|
+
const abs = join21(dir, name);
|
|
4374
4573
|
const rel = relBase ? `${relBase}/${name}` : name;
|
|
4375
4574
|
try {
|
|
4376
4575
|
const st = statSync3(abs);
|
|
@@ -4380,16 +4579,16 @@ function listProjectDayCaptures(vault, project, day) {
|
|
|
4380
4579
|
}
|
|
4381
4580
|
}
|
|
4382
4581
|
};
|
|
4383
|
-
walk(
|
|
4384
|
-
walk(
|
|
4385
|
-
walk(
|
|
4386
|
-
const workRoot =
|
|
4387
|
-
if (
|
|
4582
|
+
walk(join21(vault, "raw", "transcripts"), "raw/transcripts");
|
|
4583
|
+
walk(join21(vault, "projects", slug, "raw", "transcripts"), `projects/${slug}/raw/transcripts`);
|
|
4584
|
+
walk(join21(vault, "projects", slug, "requirements"), `projects/${slug}/requirements`);
|
|
4585
|
+
const workRoot = join21(vault, "projects", slug, "work");
|
|
4586
|
+
if (existsSync19(workRoot)) {
|
|
4388
4587
|
try {
|
|
4389
4588
|
for (const name of readdirSync7(workRoot)) {
|
|
4390
4589
|
if (!name.startsWith(day)) continue;
|
|
4391
4590
|
if (!/investigate|pilot-q|research|cycle|dev-loop/.test(name)) continue;
|
|
4392
|
-
const abs =
|
|
4591
|
+
const abs = join21(workRoot, name);
|
|
4393
4592
|
if (statSync3(abs).isDirectory()) {
|
|
4394
4593
|
for (const f of listFilesRecursive(abs)) {
|
|
4395
4594
|
const rel = relative2(vault, f).replace(/\\/g, "/");
|
|
@@ -4434,7 +4633,7 @@ function evaluateCaptureBudget(input) {
|
|
|
4434
4633
|
return { allowed: true, reason: "under_budget", report };
|
|
4435
4634
|
}
|
|
4436
4635
|
function runWritePreflight(input) {
|
|
4437
|
-
if (!input.vault || !
|
|
4636
|
+
if (!input.vault || !existsSync19(input.vault)) {
|
|
4438
4637
|
return err(GateError.VAULT_PATH_INVALID, { path: input.vault });
|
|
4439
4638
|
}
|
|
4440
4639
|
const want = new Set(input.checks ?? ["all"]);
|
|
@@ -4494,8 +4693,8 @@ function runWritePreflight(input) {
|
|
|
4494
4693
|
|
|
4495
4694
|
// src/commands/observe.ts
|
|
4496
4695
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
4497
|
-
import { existsSync as
|
|
4498
|
-
import { join as
|
|
4696
|
+
import { existsSync as existsSync20, statSync as statSync4 } from "fs";
|
|
4697
|
+
import { join as join22 } from "path";
|
|
4499
4698
|
import { createHash as createHash2 } from "crypto";
|
|
4500
4699
|
var ALLOWED_KINDS = /* @__PURE__ */ new Set(["note", "bug", "task", "idea", "session-log"]);
|
|
4501
4700
|
function slugify(text) {
|
|
@@ -4518,7 +4717,7 @@ async function runObserve(input) {
|
|
|
4518
4717
|
result: err("SCHEME_REJECTED", { message: "Text must not be empty" })
|
|
4519
4718
|
};
|
|
4520
4719
|
}
|
|
4521
|
-
if (!
|
|
4720
|
+
if (!existsSync20(input.vault) || !statSync4(input.vault).isDirectory()) {
|
|
4522
4721
|
return {
|
|
4523
4722
|
exitCode: ExitCode.VAULT_PATH_INVALID,
|
|
4524
4723
|
result: err("VAULT_PATH_INVALID", { path: input.vault })
|
|
@@ -4544,7 +4743,7 @@ async function runObserve(input) {
|
|
|
4544
4743
|
};
|
|
4545
4744
|
}
|
|
4546
4745
|
}
|
|
4547
|
-
const transcriptsDir =
|
|
4746
|
+
const transcriptsDir = join22(input.vault, "raw", "transcripts");
|
|
4548
4747
|
try {
|
|
4549
4748
|
await mkdir3(transcriptsDir, { recursive: true });
|
|
4550
4749
|
} catch {
|
|
@@ -4556,7 +4755,7 @@ async function runObserve(input) {
|
|
|
4556
4755
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
4557
4756
|
const slug = slugify(input.text);
|
|
4558
4757
|
const fileName = `${today}-observation-${slug}.md`;
|
|
4559
|
-
const filePath =
|
|
4758
|
+
const filePath = join22(transcriptsDir, fileName);
|
|
4560
4759
|
const body = `
|
|
4561
4760
|
${input.text.trim()}
|
|
4562
4761
|
`;
|
|
@@ -4598,7 +4797,7 @@ ${input.text.trim()}
|
|
|
4598
4797
|
// src/commands/memory.ts
|
|
4599
4798
|
import { createHash as createHash3 } from "crypto";
|
|
4600
4799
|
import { mkdir as mkdir4, readFile as readFile7, readdir as readdir2, stat as stat2, writeFile as writeFile3 } from "fs/promises";
|
|
4601
|
-
import { basename as basename2, extname, join as
|
|
4800
|
+
import { basename as basename2, extname, join as join23, relative as relative3, sep as sep2 } from "path";
|
|
4602
4801
|
|
|
4603
4802
|
// src/utils/memory-authority.ts
|
|
4604
4803
|
var TIER_RANK = {
|
|
@@ -4717,8 +4916,8 @@ async function runMemoryIndex(input) {
|
|
|
4717
4916
|
}
|
|
4718
4917
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
4719
4918
|
const relCachePath = memoryCacheRelPath(input.project);
|
|
4720
|
-
const absCachePath =
|
|
4721
|
-
await mkdir4(
|
|
4919
|
+
const absCachePath = join23(input.vault, relCachePath);
|
|
4920
|
+
await mkdir4(join23(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
|
|
4722
4921
|
await writeFile3(absCachePath, `${JSON.stringify({
|
|
4723
4922
|
generated_at: generatedAt,
|
|
4724
4923
|
project: input.project,
|
|
@@ -4910,7 +5109,7 @@ async function buildMemoryIndexState(pages, project) {
|
|
|
4910
5109
|
}
|
|
4911
5110
|
async function checkMemoryIndex(vault, project, current) {
|
|
4912
5111
|
const relCachePath = memoryCacheRelPath(project);
|
|
4913
|
-
const cacheText = await readIfExists(
|
|
5112
|
+
const cacheText = await readIfExists(join23(vault, relCachePath));
|
|
4914
5113
|
if (!cacheText) {
|
|
4915
5114
|
return {
|
|
4916
5115
|
ok: true,
|
|
@@ -5319,7 +5518,7 @@ async function walkImportFiles(dir, out) {
|
|
|
5319
5518
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
5320
5519
|
for (const entry of entries) {
|
|
5321
5520
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
5322
|
-
const path =
|
|
5521
|
+
const path = join23(dir, entry.name);
|
|
5323
5522
|
if (entry.isDirectory()) {
|
|
5324
5523
|
await walkImportFiles(path, out);
|
|
5325
5524
|
} else if (entry.isFile() && isImportCandidate(path)) {
|
|
@@ -5386,8 +5585,8 @@ async function writeImportCapture(vault, entry, today) {
|
|
|
5386
5585
|
const content = hiddenString(entry, "__content");
|
|
5387
5586
|
const project = hiddenString(entry, "__project");
|
|
5388
5587
|
const relPath = await availableImportPath(vault, entry.proposed_path);
|
|
5389
|
-
const absPath =
|
|
5390
|
-
await mkdir4(
|
|
5588
|
+
const absPath = join23(vault, relPath);
|
|
5589
|
+
await mkdir4(join23(vault, "raw", "transcripts"), { recursive: true });
|
|
5391
5590
|
await writeFile3(absPath, renderImportCapture(entry, content, project, today), "utf8");
|
|
5392
5591
|
const validation = await runValidate({ file: absPath });
|
|
5393
5592
|
return {
|
|
@@ -5403,7 +5602,7 @@ async function availableImportPath(vault, proposed) {
|
|
|
5403
5602
|
const stem = proposed.slice(0, -ext.length);
|
|
5404
5603
|
let candidate = proposed;
|
|
5405
5604
|
let i = 2;
|
|
5406
|
-
while (await readIfExists(
|
|
5605
|
+
while (await readIfExists(join23(vault, candidate))) {
|
|
5407
5606
|
candidate = `${stem}-${i}${ext}`;
|
|
5408
5607
|
i++;
|
|
5409
5608
|
}
|
|
@@ -5588,10 +5787,10 @@ function memoryCacheRelPath(project) {
|
|
|
5588
5787
|
}
|
|
5589
5788
|
async function readMemoryCache(vault, project) {
|
|
5590
5789
|
if (project) {
|
|
5591
|
-
const projectCache = await readIfExists(
|
|
5790
|
+
const projectCache = await readIfExists(join23(vault, memoryCacheRelPath(project)));
|
|
5592
5791
|
if (projectCache) return projectCache;
|
|
5593
5792
|
}
|
|
5594
|
-
return readIfExists(
|
|
5793
|
+
return readIfExists(join23(vault, ".skillwiki", "memory-topics.json"));
|
|
5595
5794
|
}
|
|
5596
5795
|
function dedupePages(pages) {
|
|
5597
5796
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5683,7 +5882,7 @@ function slugify2(value) {
|
|
|
5683
5882
|
|
|
5684
5883
|
// src/commands/query.ts
|
|
5685
5884
|
import { readFile as readFile9, stat as stat4 } from "fs/promises";
|
|
5686
|
-
import { join as
|
|
5885
|
+
import { join as join25 } from "path";
|
|
5687
5886
|
|
|
5688
5887
|
// src/utils/rrf.ts
|
|
5689
5888
|
var RRF_K = 60;
|
|
@@ -5699,8 +5898,8 @@ function fuseRankings(lists, k = RRF_K) {
|
|
|
5699
5898
|
|
|
5700
5899
|
// src/utils/vector-index.ts
|
|
5701
5900
|
import { mkdir as mkdir5, readFile as readFile8, stat as stat3 } from "fs/promises";
|
|
5702
|
-
import { join as
|
|
5703
|
-
import { existsSync as
|
|
5901
|
+
import { join as join24 } from "path";
|
|
5902
|
+
import { existsSync as existsSync21 } from "fs";
|
|
5704
5903
|
var VECTOR_INDEX_REL = ".skillwiki/vectors/index.json";
|
|
5705
5904
|
var VECTOR_INDEX_SCHEMA = "skillwiki-tfidf-index/v1";
|
|
5706
5905
|
function tokenize(text) {
|
|
@@ -5714,7 +5913,7 @@ function extractPageTokens(text) {
|
|
|
5714
5913
|
return tokenize(`${title} ${body}`);
|
|
5715
5914
|
}
|
|
5716
5915
|
function vectorIndexPath(vault) {
|
|
5717
|
-
return
|
|
5916
|
+
return join24(vault, ...VECTOR_INDEX_REL.split("/"));
|
|
5718
5917
|
}
|
|
5719
5918
|
function termFreq(tokens) {
|
|
5720
5919
|
const tf = /* @__PURE__ */ new Map();
|
|
@@ -5769,7 +5968,7 @@ async function buildVectorIndex(vault, now = (/* @__PURE__ */ new Date()).toISOS
|
|
|
5769
5968
|
docs
|
|
5770
5969
|
};
|
|
5771
5970
|
const dest = vectorIndexPath(vault);
|
|
5772
|
-
await mkdir5(
|
|
5971
|
+
await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
|
|
5773
5972
|
await atomicWriteText(dest, `${JSON.stringify(index)}
|
|
5774
5973
|
`);
|
|
5775
5974
|
return ok(index);
|
|
@@ -5812,7 +6011,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
|
|
|
5812
6011
|
if (!normalizedPage.endsWith(".md")) {
|
|
5813
6012
|
return err("USAGE", { message: `Page must be a .md file: ${pageRelPath}` });
|
|
5814
6013
|
}
|
|
5815
|
-
const absPath =
|
|
6014
|
+
const absPath = join24(vault, ...normalizedPage.split("/"));
|
|
5816
6015
|
let text;
|
|
5817
6016
|
try {
|
|
5818
6017
|
text = await readFile8(absPath, "utf8");
|
|
@@ -5897,7 +6096,7 @@ async function reindexPageInVectorIndex(vault, pageRelPath, now = (/* @__PURE__
|
|
|
5897
6096
|
}
|
|
5898
6097
|
}
|
|
5899
6098
|
const dest = vectorIndexPath(vault);
|
|
5900
|
-
await mkdir5(
|
|
6099
|
+
await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
|
|
5901
6100
|
await atomicWriteText(dest, `${JSON.stringify(index)}
|
|
5902
6101
|
`);
|
|
5903
6102
|
return ok({
|
|
@@ -5915,8 +6114,8 @@ async function pruneVectorIndex(vault, opts) {
|
|
|
5915
6114
|
const orphans = [];
|
|
5916
6115
|
for (const docKey of Object.keys(index.docs)) {
|
|
5917
6116
|
const normalizedKey = docKey.split(/\\|\//).join("/");
|
|
5918
|
-
const absPath =
|
|
5919
|
-
if (!
|
|
6117
|
+
const absPath = join24(vault, ...normalizedKey.split("/"));
|
|
6118
|
+
if (!existsSync21(absPath)) {
|
|
5920
6119
|
orphans.push(normalizedKey);
|
|
5921
6120
|
}
|
|
5922
6121
|
}
|
|
@@ -5962,7 +6161,7 @@ async function pruneVectorIndex(vault, opts) {
|
|
|
5962
6161
|
index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
|
|
5963
6162
|
} else {
|
|
5964
6163
|
try {
|
|
5965
|
-
const text = await readFile8(
|
|
6164
|
+
const text = await readFile8(join24(vault, ...docKey.split("/")), "utf8");
|
|
5966
6165
|
const tokens = extractPageTokens(text);
|
|
5967
6166
|
index.docs[docKey] = toTfIdf(termFreq(tokens), index.df, newPageCount);
|
|
5968
6167
|
} catch {
|
|
@@ -5972,7 +6171,7 @@ async function pruneVectorIndex(vault, opts) {
|
|
|
5972
6171
|
}
|
|
5973
6172
|
if (!opts?.dryRun) {
|
|
5974
6173
|
const dest = vectorIndexPath(vault);
|
|
5975
|
-
await mkdir5(
|
|
6174
|
+
await mkdir5(join24(vault, ".skillwiki", "vectors"), { recursive: true });
|
|
5976
6175
|
await atomicWriteText(dest, `${JSON.stringify(index)}
|
|
5977
6176
|
`);
|
|
5978
6177
|
}
|
|
@@ -6176,7 +6375,7 @@ function computeKeywordScore(terms, title, tags, body) {
|
|
|
6176
6375
|
return score;
|
|
6177
6376
|
}
|
|
6178
6377
|
async function loadOrBuildGraph(vault) {
|
|
6179
|
-
const graphPath =
|
|
6378
|
+
const graphPath = join25(vault, ".skillwiki", "graph.json");
|
|
6180
6379
|
let needsBuild = false;
|
|
6181
6380
|
try {
|
|
6182
6381
|
const fileStat = await stat4(graphPath);
|
|
@@ -6265,7 +6464,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
6265
6464
|
import { z } from "zod";
|
|
6266
6465
|
|
|
6267
6466
|
// src/mcp/vault-resolve.ts
|
|
6268
|
-
import { join as
|
|
6467
|
+
import { join as join26, resolve as resolve4 } from "path";
|
|
6269
6468
|
|
|
6270
6469
|
// src/mcp/allowlist.ts
|
|
6271
6470
|
import { resolve as resolve3, sep as sep3 } from "path";
|
|
@@ -6327,7 +6526,7 @@ async function resolveMcpVault(input) {
|
|
|
6327
6526
|
return ok({ vault: vaultPath, source });
|
|
6328
6527
|
}
|
|
6329
6528
|
function defaultGraphOut(vault) {
|
|
6330
|
-
return
|
|
6529
|
+
return join26(vault, ".skillwiki", "graph.json");
|
|
6331
6530
|
}
|
|
6332
6531
|
|
|
6333
6532
|
// src/mcp/result-format.ts
|
|
@@ -6344,7 +6543,7 @@ function formatToolResult(payload) {
|
|
|
6344
6543
|
// src/mcp/audit-log.ts
|
|
6345
6544
|
import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
6346
6545
|
import { homedir } from "os";
|
|
6347
|
-
import { join as
|
|
6546
|
+
import { join as join27 } from "path";
|
|
6348
6547
|
function auditEnabled() {
|
|
6349
6548
|
const v = process.env.SKILLWIKI_MCP_AUDIT;
|
|
6350
6549
|
if (v === "0" || v === "false") return false;
|
|
@@ -6356,7 +6555,7 @@ function auditSink() {
|
|
|
6356
6555
|
function auditFilePath() {
|
|
6357
6556
|
const custom = process.env.SKILLWIKI_MCP_AUDIT_FILE;
|
|
6358
6557
|
if (custom && custom.length > 0) return custom;
|
|
6359
|
-
return
|
|
6558
|
+
return join27(homedir(), ".skillwiki", "mcp-audit.jsonl");
|
|
6360
6559
|
}
|
|
6361
6560
|
function auditMcpToolCall(entry) {
|
|
6362
6561
|
if (!auditEnabled()) return;
|
|
@@ -6366,7 +6565,7 @@ function auditMcpToolCall(entry) {
|
|
|
6366
6565
|
return;
|
|
6367
6566
|
}
|
|
6368
6567
|
const path = auditFilePath();
|
|
6369
|
-
mkdirSync2(
|
|
6568
|
+
mkdirSync2(join27(path, ".."), { recursive: true });
|
|
6370
6569
|
appendFileSync(path, line, "utf8");
|
|
6371
6570
|
}
|
|
6372
6571
|
async function runMcpToolHandler(tool, input, fn) {
|
|
@@ -6640,7 +6839,7 @@ function registerMcpMutatingTools(server) {
|
|
|
6640
6839
|
|
|
6641
6840
|
// src/mcp/resources.ts
|
|
6642
6841
|
import { readFile as readFile11 } from "fs/promises";
|
|
6643
|
-
import { join as
|
|
6842
|
+
import { join as join29 } from "path";
|
|
6644
6843
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6645
6844
|
|
|
6646
6845
|
// src/mcp/lint-bucket.ts
|
|
@@ -6770,8 +6969,8 @@ async function fetchQueryPreview(input) {
|
|
|
6770
6969
|
|
|
6771
6970
|
// src/mcp/graph-html.ts
|
|
6772
6971
|
import { readFile as readFile10 } from "fs/promises";
|
|
6773
|
-
import { join as
|
|
6774
|
-
import { existsSync as
|
|
6972
|
+
import { join as join28 } from "path";
|
|
6973
|
+
import { existsSync as existsSync22 } from "fs";
|
|
6775
6974
|
var TYPE_COLORS = {
|
|
6776
6975
|
entities: "#e74c3c",
|
|
6777
6976
|
concepts: "#27ae60",
|
|
@@ -6839,9 +7038,9 @@ ${nodeSvg}
|
|
|
6839
7038
|
return { html, node_count: nodes.length, edge_count: edges.length, truncated };
|
|
6840
7039
|
}
|
|
6841
7040
|
async function fetchGraphHtmlReport(input) {
|
|
6842
|
-
const graphPath = input.graphPath ??
|
|
7041
|
+
const graphPath = input.graphPath ?? join28(input.vault, ".skillwiki", "graph.json");
|
|
6843
7042
|
const maxNodes = Math.min(Math.max(10, input.maxNodes ?? 120), 500);
|
|
6844
|
-
if (!
|
|
7043
|
+
if (!existsSync22(graphPath)) {
|
|
6845
7044
|
return {
|
|
6846
7045
|
exitCode: ExitCode.FILE_NOT_FOUND,
|
|
6847
7046
|
result: err("GRAPH_MISSING", { path: graphPath, hint: "Run skillwiki.graph_build first." })
|
|
@@ -6913,7 +7112,7 @@ async function fetchStaleSummary(input) {
|
|
|
6913
7112
|
|
|
6914
7113
|
// src/mcp/resources.ts
|
|
6915
7114
|
async function readVaultFile(vault, rel) {
|
|
6916
|
-
return readFile11(
|
|
7115
|
+
return readFile11(join29(vault, rel), "utf8");
|
|
6917
7116
|
}
|
|
6918
7117
|
async function tailLines(text, lines) {
|
|
6919
7118
|
const parts = text.split(/\r?\n/);
|
|
@@ -6999,7 +7198,7 @@ function registerMcpResources(server) {
|
|
|
6999
7198
|
if (!v.ok) {
|
|
7000
7199
|
return { contents: [{ uri: uri.href, mimeType: "text/plain", text: JSON.stringify(v) }] };
|
|
7001
7200
|
}
|
|
7002
|
-
const path =
|
|
7201
|
+
const path = join29(v.data.vault, ".skillwiki", "graph.json");
|
|
7003
7202
|
try {
|
|
7004
7203
|
const raw = await readFile11(path, "utf8");
|
|
7005
7204
|
const graph = JSON.parse(raw);
|