skillwiki 0.10.73 → 0.10.75
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-4XCBKPKD.js} +1 -1
- package/dist/{chunk-YKVJ2A3O.js → chunk-WAUWBPIQ.js} +258 -59
- package/dist/cli.js +39 -17
- package/dist/{managed-write-preflight-PUEJOXJO.js → managed-write-preflight-KCFPPZNF.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 +9 -2
- package/skills/skills/using-skillwiki/SKILL.md +4 -3
- package/skills/skills/wiki-lint/SKILL.md +2 -2
- package/skills/skills/wiki-sync/SKILL.md +2 -2
- package/skills/skillwiki-mcp/SKILL.md +9 -2
- package/skills/using-skillwiki/SKILL.md +4 -3
- 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>");
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
snapshotterAliasForLocalHost,
|
|
41
41
|
toUndirectedWeighted,
|
|
42
42
|
writeDotenv
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-4XCBKPKD.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);
|
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
snapshotterHealthChecks,
|
|
51
51
|
upsertIndexEntry,
|
|
52
52
|
vectorIndexStatus
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-WAUWBPIQ.js";
|
|
54
54
|
import {
|
|
55
55
|
normalizeDistTag,
|
|
56
56
|
readCache,
|
|
@@ -151,7 +151,7 @@ import {
|
|
|
151
151
|
supersedeStaleReviewRequiredJournals,
|
|
152
152
|
taxonomyCommentForPage,
|
|
153
153
|
writeDotenv
|
|
154
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-4XCBKPKD.js";
|
|
155
155
|
import {
|
|
156
156
|
assertTargetInsideVault,
|
|
157
157
|
atomicWriteText,
|
|
@@ -4246,24 +4246,45 @@ function parseCoreSemver(version) {
|
|
|
4246
4246
|
if (!m) return null;
|
|
4247
4247
|
return { major: parseInt(m[1], 10), minor: parseInt(m[2], 10), patch: parseInt(m[3], 10) };
|
|
4248
4248
|
}
|
|
4249
|
+
function isAtLeast(parsed, major, minor, patch) {
|
|
4250
|
+
return parsed.major > major || parsed.major === major && parsed.minor > minor || parsed.major === major && parsed.minor === minor && parsed.patch >= patch;
|
|
4251
|
+
}
|
|
4252
|
+
function isBelow(parsed, major, minor, patch) {
|
|
4253
|
+
return !isAtLeast(parsed, major, minor, patch);
|
|
4254
|
+
}
|
|
4249
4255
|
function needs0101Migration(previousVersion, newVersion) {
|
|
4250
4256
|
const p = parseCoreSemver(previousVersion);
|
|
4251
4257
|
const n = parseCoreSemver(newVersion);
|
|
4252
4258
|
if (!p || !n) return false;
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4259
|
+
return isBelow(p, 0, 10, 1) && isAtLeast(n, 0, 10, 1);
|
|
4260
|
+
}
|
|
4261
|
+
function needsHttpMcpMigration(previousVersion, newVersion) {
|
|
4262
|
+
const p = parseCoreSemver(previousVersion);
|
|
4263
|
+
const n = parseCoreSemver(newVersion);
|
|
4264
|
+
if (!p || !n) return false;
|
|
4265
|
+
return isBelow(p, 0, 10, 70) && isAtLeast(n, 0, 10, 70);
|
|
4256
4266
|
}
|
|
4257
4267
|
function migrationNotesForUpgrade(previousVersion, newVersion) {
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4268
|
+
const notes = [];
|
|
4269
|
+
if (needs0101Migration(previousVersion, newVersion)) {
|
|
4270
|
+
notes.push(
|
|
4271
|
+
"Migration 0.10.1:",
|
|
4272
|
+
"- pull helper resolves from dist/ + host vault-sync install",
|
|
4273
|
+
"- run: skillwiki doctor",
|
|
4274
|
+
"- if managed writes blocked: skillwiki sync journal list",
|
|
4275
|
+
"- then: skillwiki sync journal clear-stale --dry-run",
|
|
4276
|
+
"- legacy override: SKILLWIKI_VAULT_SYNC_PULL_HELPER=<path-to-wiki-pull-with-auto-resolve.sh>"
|
|
4277
|
+
);
|
|
4278
|
+
}
|
|
4279
|
+
if (needsHttpMcpMigration(previousVersion, newVersion)) {
|
|
4280
|
+
notes.push(
|
|
4281
|
+
"Migration HTTP MCP:",
|
|
4282
|
+
"- upgrade the plugin channel, then start a new session (plugin instructions do not hot-swap)",
|
|
4283
|
+
"- run: skillwiki doctor --check-mcp",
|
|
4284
|
+
"- frozen-leaf hosts write via HTTP MCP only; do not local-write raw/transcripts/ or vault git"
|
|
4285
|
+
);
|
|
4286
|
+
}
|
|
4287
|
+
return notes;
|
|
4267
4288
|
}
|
|
4268
4289
|
function resolveGlobalSkillsRoot() {
|
|
4269
4290
|
try {
|
|
@@ -10410,7 +10431,7 @@ async function emitManagedVaultWrite(vault, command, mutate, opts) {
|
|
|
10410
10431
|
if (dirty) {
|
|
10411
10432
|
return emit(dirty, void 0, { postCommit: false });
|
|
10412
10433
|
}
|
|
10413
|
-
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-
|
|
10434
|
+
const { runManagedWriteTransaction: runManagedWriteTransaction2 } = await import("./managed-write-preflight-KCFPPZNF.js");
|
|
10414
10435
|
const run = await runManagedWriteTransaction2({
|
|
10415
10436
|
vault,
|
|
10416
10437
|
command,
|
|
@@ -11043,13 +11064,14 @@ configCmd.command("get <key>").description("print the value of a config key").ac
|
|
|
11043
11064
|
configCmd.command("set <key> <value>").description("set a config key value").action(async (key, value) => emit(await runConfigSet({ key, value, home: process.env.HOME ?? "" })));
|
|
11044
11065
|
configCmd.command("list").option("--profiles", "show wiki profiles summary", false).description("list all config key=value pairs").action(async (opts) => emit(await runConfigList({ home: process.env.HOME ?? "", profiles: !!opts.profiles })));
|
|
11045
11066
|
configCmd.command("path").description("print the config file path").action(async () => emit(await runConfigPath({ home: process.env.HOME ?? "" })));
|
|
11046
|
-
program.command("doctor").description("diagnose skillwiki setup issues").option("--check-snapshotter", "SSH-probe fleet snapshotter (short timeout)", false).action(async (opts) => emit(await runDoctor({
|
|
11067
|
+
program.command("doctor").description("diagnose skillwiki setup issues").option("--check-snapshotter", "SSH-probe fleet snapshotter (short timeout)", false).option("--check-mcp", "live-handshake HTTP MCP initialize + tools/list (network)", false).action(async (opts) => emit(await runDoctor({
|
|
11047
11068
|
home: process.env.HOME ?? "",
|
|
11048
11069
|
envValue: process.env.WIKI_PATH,
|
|
11049
11070
|
argv: process.argv,
|
|
11050
11071
|
currentVersion: pkg.version,
|
|
11051
11072
|
cwd: process.cwd(),
|
|
11052
|
-
checkSnapshotter: !!opts.checkSnapshotter
|
|
11073
|
+
checkSnapshotter: !!opts.checkSnapshotter,
|
|
11074
|
+
checkMcp: !!opts.checkMcp
|
|
11053
11075
|
})));
|
|
11054
11076
|
program.command("status [vault]").description("output vault diagnostics").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11055
11077
|
const v = await resolveVaultArg(vault, opts.wiki);
|
package/dist/skillwiki-mcp.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
runSkillwikiMcpStdio
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-WAUWBPIQ.js";
|
|
5
5
|
import "./chunk-7I2TPIV5.js";
|
|
6
6
|
import "./chunk-KFEOMMWK.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-4XCBKPKD.js";
|
|
8
8
|
import "./chunk-BPJ5KWIT.js";
|
|
9
9
|
import "./chunk-NPTIYO2S.js";
|
|
10
10
|
import "./chunk-OMO45AHI.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.75",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.75",
|
|
4
4
|
"description": "Project-aware Karpathy-style knowledge base: 21 prompt-only skills (wiki-*, proj-*, using-skillwiki, skillwiki-mcp) backed by HTTP MCP wiki_capture.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "karlorz"
|
|
@@ -21,10 +21,15 @@ You are a project work item manager specializing in creating and executing work
|
|
|
21
21
|
|
|
22
22
|
**Execution Process:**
|
|
23
23
|
|
|
24
|
-
If `$VAULT/.WIKI_GIT_FROZEN` exists after `skillwiki path`:
|
|
24
|
+
If `$VAULT/.WIKI_GIT_FROZEN` exists after `skillwiki path`:
|
|
25
|
+
- Existing work folder reads are allowed.
|
|
26
|
+
- Git add/commit/push and wiki-sync remain **fail closed**.
|
|
27
|
+
- **If live MCP tools include `wiki_workitem_write`**: create, mutate, or close work items via MCP `wiki_workitem_write` (using CAS base_sha256). Do not write local files in `~/wiki`.
|
|
28
|
+
- **If live MCP tools include `wiki_page_publish`**: publish typed pages via MCP `wiki_page_publish` CAS.
|
|
29
|
+
- **If those tools are absent**: capture a close note via HTTP MCP `wiki_capture` or STOP. Do not local-write.
|
|
25
30
|
|
|
26
31
|
### Creating a New Work Item
|
|
27
|
-
1. **Resolve vault.** Run `skillwiki path`. If `.WIKI_GIT_FROZEN` exists,
|
|
32
|
+
1. **Resolve vault.** Run `skillwiki path`. If `.WIKI_GIT_FROZEN` exists, follow the freeze rule above (MCP `wiki_workitem_write` when advertised; otherwise STOP). Do not local-create.
|
|
28
33
|
2. **Determine slug and kind.** From task prompt: kind (`feature` | `issue` | `refactor` | `decision`) and work slug.
|
|
29
34
|
3. **Create folder.** `projects/{slug}/work/YYYY-MM-DD-{work-slug}/`.
|
|
30
35
|
4. **Write spec.md.** Frontmatter with kind, status=planned, project wikilink. Body with context and scope.
|
|
@@ -55,13 +60,14 @@ Return:
|
|
|
55
60
|
- Log entries appended
|
|
56
61
|
|
|
57
62
|
**Stop Conditions:**
|
|
58
|
-
- `$VAULT/.WIKI_GIT_FROZEN` exists and the task would create, mutate, or close a work item
|
|
63
|
+
- `$VAULT/.WIKI_GIT_FROZEN` exists and the task would create, mutate, or close a work item when `wiki_workitem_write` is absent from live MCP tools
|
|
59
64
|
- `validate` non-zero
|
|
60
65
|
- Conflicting work folder name
|
|
61
66
|
- No project context and no `playground` fallback
|
|
62
67
|
|
|
63
68
|
**Forbidden:**
|
|
64
|
-
- Creating or mutating work items on a vault that has `.WIKI_GIT_FROZEN`
|
|
69
|
+
- Creating or mutating local work items on a vault that has `.WIKI_GIT_FROZEN` (use MCP `wiki_workitem_write` when advertised)
|
|
70
|
+
- `git add` / `git commit` / `git push` on a vault that has `.WIKI_GIT_FROZEN`
|
|
65
71
|
- Writing spec/plan files outside the work folder
|
|
66
72
|
- Marking `status: completed` without a `completed:` date
|
|
67
73
|
- Accepting tasks.md DONE labels without independent disk verification
|
package/skills/package.json
CHANGED
|
@@ -33,9 +33,10 @@ Standard four + project context (project README, last ~5 work logs).
|
|
|
33
33
|
After `skillwiki path`, if `$VAULT/.WIKI_GIT_FROZEN` exists:
|
|
34
34
|
|
|
35
35
|
- **Reads** of existing work folders are allowed.
|
|
36
|
-
- **
|
|
37
|
-
- **
|
|
38
|
-
-
|
|
36
|
+
- **Git fail-closed:** Git add/commit/push / `wiki-sync`: still **fail closed**. GitHub is sg01 `wiki-snapshot`.
|
|
37
|
+
- **If the live MCP tool list includes `wiki_workitem_write`**: create, mutate, or close work items via MCP `wiki_workitem_write`. Read current bytes (`wiki_read_page` sha256 or local file hash), pass `path`, `content`, `base_sha256`. On `FILE_CHANGED`, re-read and retry once. Never local-write `~/wiki`.
|
|
38
|
+
- **If the live MCP tool list includes `wiki_page_publish`**: publish typed Layer-2 pages via MCP `wiki_page_publish` with the same CAS. Do not run local `skillwiki page publish` on the frozen leaf.
|
|
39
|
+
- **If those tools are absent** (daemon still captures-only): capture a close note via HTTP MCP `wiki_capture` (`kind: note` or `task`) or STOP. Do not local-write. Do not claim the tools exist.
|
|
39
40
|
|
|
40
41
|
## Executing an Existing Work Item
|
|
41
42
|
|
|
@@ -99,12 +100,13 @@ Rules:
|
|
|
99
100
|
- **Re-marking without doing**: do not simply re-write tasks.md to say DONE without applying the corresponding fix. The next session will find the same gap.
|
|
100
101
|
|
|
101
102
|
## Stop conditions
|
|
102
|
-
- `$VAULT/.WIKI_GIT_FROZEN` exists and the user asked to create, mutate, or close a work item — capture via `wiki_capture` or STOP; do not local-write.
|
|
103
|
+
- `$VAULT/.WIKI_GIT_FROZEN` exists and the user asked to create, mutate, or close a work item when `wiki_workitem_write` is absent from the live MCP tool list — capture via `wiki_capture` or STOP; do not local-write.
|
|
103
104
|
- `validate` non-zero.
|
|
104
105
|
- Conflicting work folder name.
|
|
105
106
|
|
|
106
107
|
## Forbidden
|
|
107
|
-
- Creating or mutating work items on a vault that has `.WIKI_GIT_FROZEN
|
|
108
|
+
- Creating or mutating local work items on a vault that has `.WIKI_GIT_FROZEN` (use MCP `wiki_workitem_write` when advertised).
|
|
109
|
+
- `git add` / `git commit` / `git push` on a vault that has `.WIKI_GIT_FROZEN`.
|
|
108
110
|
- Writing spec/plan files outside the work folder.
|
|
109
111
|
- Marking `status: completed` without a `completed:` date.
|
|
110
112
|
- Accepting tasks.md status labels without independent disk verification.
|
|
@@ -33,9 +33,10 @@ Standard four + project context (project README, last ~5 work logs).
|
|
|
33
33
|
After `skillwiki path`, if `$VAULT/.WIKI_GIT_FROZEN` exists:
|
|
34
34
|
|
|
35
35
|
- **Reads** of existing work folders are allowed.
|
|
36
|
-
- **
|
|
37
|
-
- **
|
|
38
|
-
-
|
|
36
|
+
- **Git fail-closed:** Git add/commit/push / `wiki-sync`: still **fail closed**. GitHub is sg01 `wiki-snapshot`.
|
|
37
|
+
- **If the live MCP tool list includes `wiki_workitem_write`**: create, mutate, or close work items via MCP `wiki_workitem_write`. Read current bytes (`wiki_read_page` sha256 or local file hash), pass `path`, `content`, `base_sha256`. On `FILE_CHANGED`, re-read and retry once. Never local-write `~/wiki`.
|
|
38
|
+
- **If the live MCP tool list includes `wiki_page_publish`**: publish typed Layer-2 pages via MCP `wiki_page_publish` with the same CAS. Do not run local `skillwiki page publish` on the frozen leaf.
|
|
39
|
+
- **If those tools are absent** (daemon still captures-only): capture a close note via HTTP MCP `wiki_capture` (`kind: note` or `task`) or STOP. Do not local-write. Do not claim the tools exist.
|
|
39
40
|
|
|
40
41
|
## Executing an Existing Work Item
|
|
41
42
|
|
|
@@ -99,12 +100,13 @@ Rules:
|
|
|
99
100
|
- **Re-marking without doing**: do not simply re-write tasks.md to say DONE without applying the corresponding fix. The next session will find the same gap.
|
|
100
101
|
|
|
101
102
|
## Stop conditions
|
|
102
|
-
- `$VAULT/.WIKI_GIT_FROZEN` exists and the user asked to create, mutate, or close a work item — capture via `wiki_capture` or STOP; do not local-write.
|
|
103
|
+
- `$VAULT/.WIKI_GIT_FROZEN` exists and the user asked to create, mutate, or close a work item when `wiki_workitem_write` is absent from the live MCP tool list — capture via `wiki_capture` or STOP; do not local-write.
|
|
103
104
|
- `validate` non-zero.
|
|
104
105
|
- Conflicting work folder name.
|
|
105
106
|
|
|
106
107
|
## Forbidden
|
|
107
|
-
- Creating or mutating work items on a vault that has `.WIKI_GIT_FROZEN
|
|
108
|
+
- Creating or mutating local work items on a vault that has `.WIKI_GIT_FROZEN` (use MCP `wiki_workitem_write` when advertised).
|
|
109
|
+
- `git add` / `git commit` / `git push` on a vault that has `.WIKI_GIT_FROZEN`.
|
|
108
110
|
- Writing spec/plan files outside the work folder.
|
|
109
111
|
- Marking `status: completed` without a `completed:` date.
|
|
110
112
|
- Accepting tasks.md status labels without independent disk verification.
|
|
@@ -22,13 +22,20 @@ SkillWiki captures are HTTP MCP only (`type: http`). Claude/Grok use `SKILLWIKI_
|
|
|
22
22
|
- Never auto-source `mcp.env` or auto-write `~/.cursor/mcp.json`, Grok `config.toml`, or `mcp.env`.
|
|
23
23
|
- Never print the bearer token.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Upgrade / migration
|
|
26
|
+
|
|
27
|
+
Remotes still on a pre-HTTP-MCP plugin or CLI must upgrade the plugin channel (and the CLI where that host has one), then start a **new session** — plugin instructions do not hot-swap. The operator check is the same `skillwiki doctor` as metal: default rows cover URL/auth presence and frozen-leaf write-path; `skillwiki doctor --check-mcp` does the live initialize handshake. Do not invent a second admin skill or HTTP MCP admin tool. `wiki_status` remains daemon health only.
|
|
28
|
+
|
|
29
|
+
## Writes
|
|
26
30
|
|
|
27
31
|
On leaf hosts, wiki captures go through MCP. Do **not** write `raw/transcripts/` or `log.md` as local files.
|
|
28
32
|
|
|
29
33
|
1. Call MCP `wiki_capture` with `kind` (`task` | `idea` | `bug` | `note`), `project`, `title`, and `content`. Optional `agent_note`.
|
|
30
34
|
2. Call MCP `wiki_log_append` when a structural `log.md` line is needed. Pass append-only `content`. Do not rewrite log history.
|
|
31
|
-
3.
|
|
35
|
+
3. **Feature-detect work-item and page-publish tools**:
|
|
36
|
+
- When the server advertises `wiki_workitem_write`: use it to create, mutate, or close work items under `projects/<slug>/work/**` or update `projects/<slug>/knowledge.md`. Pass `path`, `content`, and `base_sha256` of existing bytes (from `wiki_read_page` or local mirror). On `FILE_CHANGED`, re-read and retry once.
|
|
37
|
+
- When the server advertises `wiki_page_publish`: use it to publish typed Layer-2 pages (`entities/`, `concepts/`, `comparisons/`, `queries/`, `meta/`) using the same CAS protocol.
|
|
38
|
+
- When those tools are **absent** from the live tool list (e.g. server is captures-only): do not call them or claim they exist. If `$VAULT/.WIKI_GIT_FROZEN` exists, fall back to capturing a close note via `wiki_capture` or STOP. Work-item close remains unavailable on the frozen leaf without `wiki_workitem_write`. Do not `git commit` / `wiki-push` against `~/wiki`.
|
|
32
39
|
|
|
33
40
|
## Reads
|
|
34
41
|
|
|
@@ -170,8 +170,9 @@ If the vault has `.WIKI_GIT_FROZEN`:
|
|
|
170
170
|
| Intent | Allowed? | How |
|
|
171
171
|
|---|---|---|
|
|
172
172
|
| Capture note/idea/bug/task | Yes | HTTP MCP `wiki_capture` / `wiki_log_append` |
|
|
173
|
-
| Close or mutate a work item (`projects/*/work/**`, `knowledge.md`) |
|
|
174
|
-
|
|
|
173
|
+
| Close or mutate a work item (`projects/*/work/**`, `knowledge.md`) | Conditional | If live MCP tools include `wiki_workitem_write`, mutate/close via MCP CAS; if absent, capture a close note via MCP `wiki_capture` or STOP. Never local-write. |
|
|
174
|
+
| Publish typed Layer-2 page (`concepts/`, `queries/`, etc.) | Conditional | If live MCP tools include `wiki_page_publish`, publish via MCP CAS; if absent, STOP. Do not run local `skillwiki page publish`. |
|
|
175
|
+
| `wiki-sync` push/commit | **No** | Git fail-closed. GitHub is sg01 `wiki-snapshot`. |
|
|
175
176
|
| Read local `~/wiki` | Yes | Mirror reads only |
|
|
176
177
|
| `/wiki-add-task <text>` | You're in an interactive session on an authoring host | Creates `raw/transcripts/YYYY-MM-DD-{type}-{slug}.md` with ad-hoc capture frontmatter |
|
|
177
178
|
| Filesystem drop | You're NOT in a Claude session (Obsidian, editor, sync) on an authoring host | Create a new `.md` file in `raw/transcripts/` — dev-loop discovers it on next cycle; do not edit it after capture |
|
|
@@ -245,7 +246,7 @@ Key CLI subcommands: `init`, `health`, `lint`, `config`, `doctor`, `path`, `lang
|
|
|
245
246
|
Optional read-only MCP (`skillwiki mcp`) exposes pending/compile/review list tools. Do not use MCP for compile claim, publish, or review writes.
|
|
246
247
|
`skillwiki claim` binds a transcript to a work item only through an exact `raw/transcripts/...` path in `source:` / `sources:` / `closes:`. A `--project` that contradicts the capture's explicit project is rejected. `skillwiki stale --project` uses exact normalized slugs, not substring matching. `skillwiki claims audit` is the read-only integrity report for duplicate, malformed, dangling, cross-project, and unbacked claims; it never rewrites captures or work items.
|
|
247
248
|
|
|
248
|
-
Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes the nonblocking source-lifecycle backlog. Pending captures are informational and do not make health fail. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only. Run `skillwiki config list` to see current configuration.
|
|
249
|
+
Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes the nonblocking source-lifecycle backlog. Pending captures are informational and do not make health fail. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only, including HTTP MCP URL/auth presence and the frozen-leaf write path. Pass `--check-mcp` for a live initialize handshake and pin/tool-list lag; the default doctor path does not contact HTTP MCP. There is no second MCP admin skill. Run `skillwiki config list` to see current configuration.
|
|
249
250
|
|
|
250
251
|
## Runtime Host Context and Fleet Freshness
|
|
251
252
|
Resolve the active project vault with `skillwiki path` first. Then pass that exact path to `skillwiki --human fleet context <vault>` for host identity and safety guidance. `fleet context` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
|
|
@@ -11,13 +11,13 @@ Standard four reads.
|
|
|
11
11
|
## Steps
|
|
12
12
|
0. Resolve vault: `skillwiki path` (record source for context).
|
|
13
13
|
- **CRITICAL**: Verify the correct vault when the user has multiple wiki instances (e.g., ~/wiki vs ~/wiki-fin). User may explicitly specify which vault to target — confirm before destructive operations.
|
|
14
|
-
1. For a whole-system health report, run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail`. Read the JSON envelope from stdout or the report file. Treat `skillwiki doctor` as setup/runtime diagnostics only, not a vault-content health report.
|
|
14
|
+
1. For a whole-system health report, run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail`. Read the JSON envelope from stdout or the report file. Treat `skillwiki doctor` as setup/runtime diagnostics only, not a vault-content health report. HTTP MCP URL, pin lag, handshake, and frozen-leaf write-path belong to `skillwiki doctor` (pass `--check-mcp` for the live handshake). Do not use this skill for client/plugin fleet upgrades.
|
|
15
15
|
2. For lint-only maintenance, run `skillwiki lint <vault> --summary`. This returns bounded bucket counts, capped examples, and `details_command` hints without full item arrays.
|
|
16
16
|
3. Drill into important buckets with `skillwiki lint <vault> --only <bucket>` when examples are insufficient for remediation.
|
|
17
17
|
4. Reason over findings; present grouped by severity with concrete suggested actions per kind. If the CLI was recently updated with new lint checks, re-running lint on the full vault may flag pre-existing pages that predate the new rule — treat these as legitimate findings, not false positives.
|
|
18
18
|
5. Treat `sensitive_content` as a security error. Drill down with `skillwiki lint <vault> --only sensitive_content --human`; never print the secret value. Raw findings are report/quarantine-only: `--fix` must not redact, rewrite, or recompute existing raw evidence. Maintained-page repairs may proceed only through their approved write workflow.
|
|
19
19
|
6. If `log_rotate_needed` is present and the user consents, run `skillwiki log-rotate <vault> --apply`. Otherwise leave alone.
|
|
20
|
-
7. **Post-migration verification**: If the user recently migrated content (e.g., moved entity/concept pages to another vault), re-run `skillwiki lint <vault> --summary` and verify that broken_wikilinks count decreased. Remaining broken links for migrated content indicate pages still referencing the moved files — these should be cleaned up (remove citations or migrate the referencing pages too).
|
|
20
|
+
7. **Post-migration verification**: If the user recently migrated **vault content** (e.g., moved entity/concept pages to another vault), re-run `skillwiki lint <vault> --summary` and verify that broken_wikilinks count decreased. Remaining broken links for migrated content indicate pages still referencing the moved files — these should be cleaned up (remove citations or migrate the referencing pages too). This step is content moves only — not HTTP MCP client/plugin upgrades (those use `skillwiki doctor --check-mcp`).
|
|
21
21
|
8. Append a `log.md` entry summarizing lint counts only when the user asked to record the maintenance result. Do not log routine `health` reports by default.
|
|
22
22
|
## Stop conditions
|
|
23
23
|
None — lint reports all findings even on per-page errors.
|
|
@@ -20,7 +20,7 @@ If `$VAULT/.WIKI_GIT_FROZEN` exists, **fail closed** before any stash, commit, p
|
|
|
20
20
|
|
|
21
21
|
- Do **not** `git add`, `git commit`, `git pull`, `git push`, or `skillwiki sync push`.
|
|
22
22
|
- Captures go through HTTP MCP `wiki_capture` / `wiki_log_append`.
|
|
23
|
-
-
|
|
23
|
+
- Work items mutate/close via MCP `wiki_workitem_write` when advertised by the live MCP server; if absent, capture a close note via MCP `wiki_capture` or STOP. Never local-write or git-commit on the frozen leaf.
|
|
24
24
|
- GitHub promotion is sg01 `wiki-snapshot`, not this leaf.
|
|
25
25
|
- Report the freeze marker and stop. Do not treat dirty `~/wiki` as a sync job.
|
|
26
26
|
|
|
@@ -220,7 +220,7 @@ High-signal safety rule:
|
|
|
220
220
|
Some older deployments separated a cloud-backed live vault from a Git snapshot worktree. That architecture still exists on protected snapshotters, but the snapshot worktree is **pipeline-internal**. Historical recipes that rsync into the worktree, reset it hard to origin/main, or run snapshot shell scripts by hand are obsolete and must not be copied.
|
|
221
221
|
|
|
222
222
|
## Stop conditions
|
|
223
|
-
- `$VAULT/.WIKI_GIT_FROZEN` exists — frozen leaf; refuse git close/sync. Captures via HTTP MCP; work-item close
|
|
223
|
+
- `$VAULT/.WIKI_GIT_FROZEN` exists — frozen leaf; refuse git close/sync. Captures via HTTP MCP; work-item close uses MCP `wiki_workitem_write` if advertised, else fallback capture or STOP.
|
|
224
224
|
- `skillwiki sync status` reports `not_a_repo` — the vault is not a git repository. On protected snapshotters this is expected for the FUSE live path; do not switch to `/root/wiki-git` to force a push.
|
|
225
225
|
- Lint errors are found before a push — do not push until resolved.
|
|
226
226
|
- `git push` or `git pull` fails with a network error — report and stop.
|
|
@@ -22,13 +22,20 @@ SkillWiki captures are HTTP MCP only (`type: http`). Claude/Grok use `SKILLWIKI_
|
|
|
22
22
|
- Never auto-source `mcp.env` or auto-write `~/.cursor/mcp.json`, Grok `config.toml`, or `mcp.env`.
|
|
23
23
|
- Never print the bearer token.
|
|
24
24
|
|
|
25
|
-
##
|
|
25
|
+
## Upgrade / migration
|
|
26
|
+
|
|
27
|
+
Remotes still on a pre-HTTP-MCP plugin or CLI must upgrade the plugin channel (and the CLI where that host has one), then start a **new session** — plugin instructions do not hot-swap. The operator check is the same `skillwiki doctor` as metal: default rows cover URL/auth presence and frozen-leaf write-path; `skillwiki doctor --check-mcp` does the live initialize handshake. Do not invent a second admin skill or HTTP MCP admin tool. `wiki_status` remains daemon health only.
|
|
28
|
+
|
|
29
|
+
## Writes
|
|
26
30
|
|
|
27
31
|
On leaf hosts, wiki captures go through MCP. Do **not** write `raw/transcripts/` or `log.md` as local files.
|
|
28
32
|
|
|
29
33
|
1. Call MCP `wiki_capture` with `kind` (`task` | `idea` | `bug` | `note`), `project`, `title`, and `content`. Optional `agent_note`.
|
|
30
34
|
2. Call MCP `wiki_log_append` when a structural `log.md` line is needed. Pass append-only `content`. Do not rewrite log history.
|
|
31
|
-
3.
|
|
35
|
+
3. **Feature-detect work-item and page-publish tools**:
|
|
36
|
+
- When the server advertises `wiki_workitem_write`: use it to create, mutate, or close work items under `projects/<slug>/work/**` or update `projects/<slug>/knowledge.md`. Pass `path`, `content`, and `base_sha256` of existing bytes (from `wiki_read_page` or local mirror). On `FILE_CHANGED`, re-read and retry once.
|
|
37
|
+
- When the server advertises `wiki_page_publish`: use it to publish typed Layer-2 pages (`entities/`, `concepts/`, `comparisons/`, `queries/`, `meta/`) using the same CAS protocol.
|
|
38
|
+
- When those tools are **absent** from the live tool list (e.g. server is captures-only): do not call them or claim they exist. If `$VAULT/.WIKI_GIT_FROZEN` exists, fall back to capturing a close note via `wiki_capture` or STOP. Work-item close remains unavailable on the frozen leaf without `wiki_workitem_write`. Do not `git commit` / `wiki-push` against `~/wiki`.
|
|
32
39
|
|
|
33
40
|
## Reads
|
|
34
41
|
|
|
@@ -170,8 +170,9 @@ If the vault has `.WIKI_GIT_FROZEN`:
|
|
|
170
170
|
| Intent | Allowed? | How |
|
|
171
171
|
|---|---|---|
|
|
172
172
|
| Capture note/idea/bug/task | Yes | HTTP MCP `wiki_capture` / `wiki_log_append` |
|
|
173
|
-
| Close or mutate a work item (`projects/*/work/**`, `knowledge.md`) |
|
|
174
|
-
|
|
|
173
|
+
| Close or mutate a work item (`projects/*/work/**`, `knowledge.md`) | Conditional | If live MCP tools include `wiki_workitem_write`, mutate/close via MCP CAS; if absent, capture a close note via MCP `wiki_capture` or STOP. Never local-write. |
|
|
174
|
+
| Publish typed Layer-2 page (`concepts/`, `queries/`, etc.) | Conditional | If live MCP tools include `wiki_page_publish`, publish via MCP CAS; if absent, STOP. Do not run local `skillwiki page publish`. |
|
|
175
|
+
| `wiki-sync` push/commit | **No** | Git fail-closed. GitHub is sg01 `wiki-snapshot`. |
|
|
175
176
|
| Read local `~/wiki` | Yes | Mirror reads only |
|
|
176
177
|
| `/wiki-add-task <text>` | You're in an interactive session on an authoring host | Creates `raw/transcripts/YYYY-MM-DD-{type}-{slug}.md` with ad-hoc capture frontmatter |
|
|
177
178
|
| Filesystem drop | You're NOT in a Claude session (Obsidian, editor, sync) on an authoring host | Create a new `.md` file in `raw/transcripts/` — dev-loop discovers it on next cycle; do not edit it after capture |
|
|
@@ -245,7 +246,7 @@ Key CLI subcommands: `init`, `health`, `lint`, `config`, `doctor`, `path`, `lang
|
|
|
245
246
|
Optional read-only MCP (`skillwiki mcp`) exposes pending/compile/review list tools. Do not use MCP for compile claim, publish, or review writes.
|
|
246
247
|
`skillwiki claim` binds a transcript to a work item only through an exact `raw/transcripts/...` path in `source:` / `sources:` / `closes:`. A `--project` that contradicts the capture's explicit project is rejected. `skillwiki stale --project` uses exact normalized slugs, not substring matching. `skillwiki claims audit` is the read-only integrity report for duplicate, malformed, dangling, cross-project, and unbacked claims; it never rewrites captures or work items.
|
|
247
248
|
|
|
248
|
-
Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes the nonblocking source-lifecycle backlog. Pending captures are informational and do not make health fail. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only. Run `skillwiki config list` to see current configuration.
|
|
249
|
+
Run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail` for a bounded whole-system report that includes the nonblocking source-lifecycle backlog. Pending captures are informational and do not make health fail. Run `skillwiki lint <vault> --summary` for lint-only bucket counts with capped examples and details commands. Run `skillwiki doctor` to diagnose setup/runtime issues only, including HTTP MCP URL/auth presence and the frozen-leaf write path. Pass `--check-mcp` for a live initialize handshake and pin/tool-list lag; the default doctor path does not contact HTTP MCP. There is no second MCP admin skill. Run `skillwiki config list` to see current configuration.
|
|
249
250
|
|
|
250
251
|
## Runtime Host Context and Fleet Freshness
|
|
251
252
|
Resolve the active project vault with `skillwiki path` first. Then pass that exact path to `skillwiki --human fleet context <vault>` for host identity and safety guidance. `fleet context` is authoritative for host identity. It overrides stale injected SessionStart context, remembered workspace context, and prior conversation summaries. `fleet context` is local and network-free; it reports `identity_status`, resolver trace, warnings, and the fact that remote freshness was not checked.
|
|
@@ -11,13 +11,13 @@ Standard four reads.
|
|
|
11
11
|
## Steps
|
|
12
12
|
0. Resolve vault: `skillwiki path` (record source for context).
|
|
13
13
|
- **CRITICAL**: Verify the correct vault when the user has multiple wiki instances (e.g., ~/wiki vs ~/wiki-fin). User may explicitly specify which vault to target — confirm before destructive operations.
|
|
14
|
-
1. For a whole-system health report, run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail`. Read the JSON envelope from stdout or the report file. Treat `skillwiki doctor` as setup/runtime diagnostics only, not a vault-content health report.
|
|
14
|
+
1. For a whole-system health report, run `skillwiki health <vault> --out /tmp/skillwiki-health.json --no-fail`. Read the JSON envelope from stdout or the report file. Treat `skillwiki doctor` as setup/runtime diagnostics only, not a vault-content health report. HTTP MCP URL, pin lag, handshake, and frozen-leaf write-path belong to `skillwiki doctor` (pass `--check-mcp` for the live handshake). Do not use this skill for client/plugin fleet upgrades.
|
|
15
15
|
2. For lint-only maintenance, run `skillwiki lint <vault> --summary`. This returns bounded bucket counts, capped examples, and `details_command` hints without full item arrays.
|
|
16
16
|
3. Drill into important buckets with `skillwiki lint <vault> --only <bucket>` when examples are insufficient for remediation.
|
|
17
17
|
4. Reason over findings; present grouped by severity with concrete suggested actions per kind. If the CLI was recently updated with new lint checks, re-running lint on the full vault may flag pre-existing pages that predate the new rule — treat these as legitimate findings, not false positives.
|
|
18
18
|
5. Treat `sensitive_content` as a security error. Drill down with `skillwiki lint <vault> --only sensitive_content --human`; never print the secret value. Raw findings are report/quarantine-only: `--fix` must not redact, rewrite, or recompute existing raw evidence. Maintained-page repairs may proceed only through their approved write workflow.
|
|
19
19
|
6. If `log_rotate_needed` is present and the user consents, run `skillwiki log-rotate <vault> --apply`. Otherwise leave alone.
|
|
20
|
-
7. **Post-migration verification**: If the user recently migrated content (e.g., moved entity/concept pages to another vault), re-run `skillwiki lint <vault> --summary` and verify that broken_wikilinks count decreased. Remaining broken links for migrated content indicate pages still referencing the moved files — these should be cleaned up (remove citations or migrate the referencing pages too).
|
|
20
|
+
7. **Post-migration verification**: If the user recently migrated **vault content** (e.g., moved entity/concept pages to another vault), re-run `skillwiki lint <vault> --summary` and verify that broken_wikilinks count decreased. Remaining broken links for migrated content indicate pages still referencing the moved files — these should be cleaned up (remove citations or migrate the referencing pages too). This step is content moves only — not HTTP MCP client/plugin upgrades (those use `skillwiki doctor --check-mcp`).
|
|
21
21
|
8. Append a `log.md` entry summarizing lint counts only when the user asked to record the maintenance result. Do not log routine `health` reports by default.
|
|
22
22
|
## Stop conditions
|
|
23
23
|
None — lint reports all findings even on per-page errors.
|
|
@@ -20,7 +20,7 @@ If `$VAULT/.WIKI_GIT_FROZEN` exists, **fail closed** before any stash, commit, p
|
|
|
20
20
|
|
|
21
21
|
- Do **not** `git add`, `git commit`, `git pull`, `git push`, or `skillwiki sync push`.
|
|
22
22
|
- Captures go through HTTP MCP `wiki_capture` / `wiki_log_append`.
|
|
23
|
-
-
|
|
23
|
+
- Work items mutate/close via MCP `wiki_workitem_write` when advertised by the live MCP server; if absent, capture a close note via MCP `wiki_capture` or STOP. Never local-write or git-commit on the frozen leaf.
|
|
24
24
|
- GitHub promotion is sg01 `wiki-snapshot`, not this leaf.
|
|
25
25
|
- Report the freeze marker and stop. Do not treat dirty `~/wiki` as a sync job.
|
|
26
26
|
|
|
@@ -220,7 +220,7 @@ High-signal safety rule:
|
|
|
220
220
|
Some older deployments separated a cloud-backed live vault from a Git snapshot worktree. That architecture still exists on protected snapshotters, but the snapshot worktree is **pipeline-internal**. Historical recipes that rsync into the worktree, reset it hard to origin/main, or run snapshot shell scripts by hand are obsolete and must not be copied.
|
|
221
221
|
|
|
222
222
|
## Stop conditions
|
|
223
|
-
- `$VAULT/.WIKI_GIT_FROZEN` exists — frozen leaf; refuse git close/sync. Captures via HTTP MCP; work-item close
|
|
223
|
+
- `$VAULT/.WIKI_GIT_FROZEN` exists — frozen leaf; refuse git close/sync. Captures via HTTP MCP; work-item close uses MCP `wiki_workitem_write` if advertised, else fallback capture or STOP.
|
|
224
224
|
- `skillwiki sync status` reports `not_a_repo` — the vault is not a git repository. On protected snapshotters this is expected for the FUSE live path; do not switch to `/root/wiki-git` to force a push.
|
|
225
225
|
- Lint errors are found before a push — do not push until resolved.
|
|
226
226
|
- `git push` or `git pull` fails with a network error — report and stop.
|