skillwiki 0.9.31 → 0.9.33
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-6G7RAAOS.js → chunk-OPXMAS4O.js} +54 -34
- package/dist/cli.js +52 -6
- package/dist/skillwiki-mcp.js +1 -1
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/skills/wiki-archive/SKILL.md +3 -3
- package/skills/wiki-archive/SKILL.md +3 -3
|
@@ -2056,11 +2056,57 @@ ${markdown_links.map((l) => ` line ${l.line}: ${l.text}`).join("\n")}`;
|
|
|
2056
2056
|
|
|
2057
2057
|
// src/commands/dedup.ts
|
|
2058
2058
|
import { createHash as createHash2 } from "crypto";
|
|
2059
|
-
import { execFile } from "child_process";
|
|
2060
2059
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
2061
2060
|
import { dirname as dirname4, join as join12, resolve as resolve3 } from "path";
|
|
2061
|
+
|
|
2062
|
+
// src/utils/rclone.ts
|
|
2063
|
+
import { execFile } from "child_process";
|
|
2062
2064
|
import { promisify } from "util";
|
|
2063
2065
|
var execFileAsync = promisify(execFile);
|
|
2066
|
+
function normalizeRemoteRoot(remote) {
|
|
2067
|
+
return remote?.replace(/\/+$/, "");
|
|
2068
|
+
}
|
|
2069
|
+
function buildRemoteObjectPath(remoteRoot, relPath) {
|
|
2070
|
+
return remoteRoot ? `${remoteRoot}/${relPath}` : void 0;
|
|
2071
|
+
}
|
|
2072
|
+
function isValidRemoteDeleteCap(maxRemoteDeletes) {
|
|
2073
|
+
if (maxRemoteDeletes === void 0) return true;
|
|
2074
|
+
return Number.isInteger(maxRemoteDeletes) && maxRemoteDeletes > 0;
|
|
2075
|
+
}
|
|
2076
|
+
async function planAndMaybePruneRemoteObjects(plannedDeletes, input) {
|
|
2077
|
+
const output = { plannedDeletes, deleted: [] };
|
|
2078
|
+
if (!input.remoteDelete) return ok(output);
|
|
2079
|
+
const maxDeletes = input.maxRemoteDeletes ?? input.defaultMaxDeletes ?? 1;
|
|
2080
|
+
if (!Number.isInteger(maxDeletes) || maxDeletes < 1) {
|
|
2081
|
+
return err("USAGE", { message: "--max-remote-deletes must be a positive integer" });
|
|
2082
|
+
}
|
|
2083
|
+
if (plannedDeletes.length > maxDeletes) {
|
|
2084
|
+
return err("USAGE", { message: `remote delete cap exceeded: ${plannedDeletes.length} > ${maxDeletes}` });
|
|
2085
|
+
}
|
|
2086
|
+
const runner = input.rcloneRunner ?? defaultRcloneRunner;
|
|
2087
|
+
for (const path of plannedDeletes) {
|
|
2088
|
+
const result = await runner(["deletefile", path]);
|
|
2089
|
+
if (result.exitCode !== 0) {
|
|
2090
|
+
return err("SYNC_PUSH_FAILED", { path, stderr: result.stderr, deleted: output.deleted });
|
|
2091
|
+
}
|
|
2092
|
+
output.deleted.push(path);
|
|
2093
|
+
}
|
|
2094
|
+
return ok(output);
|
|
2095
|
+
}
|
|
2096
|
+
async function defaultRcloneRunner(args) {
|
|
2097
|
+
try {
|
|
2098
|
+
const result = await execFileAsync("rclone", args, { encoding: "utf-8" });
|
|
2099
|
+
return { exitCode: 0, stdout: result.stdout, stderr: result.stderr };
|
|
2100
|
+
} catch (e) {
|
|
2101
|
+
return {
|
|
2102
|
+
exitCode: typeof e?.code === "number" ? e.code : 1,
|
|
2103
|
+
stdout: typeof e?.stdout === "string" ? e.stdout : "",
|
|
2104
|
+
stderr: typeof e?.stderr === "string" ? e.stderr : String(e)
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// src/commands/dedup.ts
|
|
2064
2110
|
async function runDedup(input) {
|
|
2065
2111
|
if (input.canonicalPolicy && input.canonicalPolicy !== "scan-order" && input.canonicalPolicy !== "stable-path") {
|
|
2066
2112
|
return { exitCode: ExitCode.USAGE, result: err("USAGE", { message: "--canonical-policy must be stable-path or scan-order" }) };
|
|
@@ -2259,39 +2305,9 @@ function readManifest(path) {
|
|
|
2259
2305
|
}
|
|
2260
2306
|
}
|
|
2261
2307
|
async function planAndMaybePruneRemote(input, entries) {
|
|
2262
|
-
const remoteRoot = input.remote
|
|
2308
|
+
const remoteRoot = normalizeRemoteRoot(input.remote);
|
|
2263
2309
|
const plannedDeletes = remoteRoot ? entries.flatMap((entry) => entry.duplicates.map((path) => `${remoteRoot}/${path}`)) : [];
|
|
2264
|
-
|
|
2265
|
-
if (!input.remoteDelete) return ok(output);
|
|
2266
|
-
const maxDeletes = input.maxRemoteDeletes ?? 50;
|
|
2267
|
-
if (!Number.isInteger(maxDeletes) || maxDeletes < 1) {
|
|
2268
|
-
return err("USAGE", { message: "--max-remote-deletes must be a positive integer" });
|
|
2269
|
-
}
|
|
2270
|
-
if (plannedDeletes.length > maxDeletes) {
|
|
2271
|
-
return err("USAGE", { message: `remote delete cap exceeded: ${plannedDeletes.length} > ${maxDeletes}` });
|
|
2272
|
-
}
|
|
2273
|
-
const runner = input.rcloneRunner ?? defaultRcloneRunner;
|
|
2274
|
-
for (const path of plannedDeletes) {
|
|
2275
|
-
const result = await runner(["deletefile", path]);
|
|
2276
|
-
if (result.exitCode !== 0) {
|
|
2277
|
-
output.failed = { path, stderr: result.stderr };
|
|
2278
|
-
return err("SYNC_PUSH_FAILED", { path, stderr: result.stderr });
|
|
2279
|
-
}
|
|
2280
|
-
output.deleted.push(path);
|
|
2281
|
-
}
|
|
2282
|
-
return ok(output);
|
|
2283
|
-
}
|
|
2284
|
-
async function defaultRcloneRunner(args) {
|
|
2285
|
-
try {
|
|
2286
|
-
const result = await execFileAsync("rclone", args, { encoding: "utf-8" });
|
|
2287
|
-
return { exitCode: 0, stdout: result.stdout, stderr: result.stderr };
|
|
2288
|
-
} catch (e) {
|
|
2289
|
-
return {
|
|
2290
|
-
exitCode: typeof e?.code === "number" ? e.code : 1,
|
|
2291
|
-
stdout: typeof e?.stdout === "string" ? e.stdout : "",
|
|
2292
|
-
stderr: typeof e?.stderr === "string" ? e.stderr : String(e)
|
|
2293
|
-
};
|
|
2294
|
-
}
|
|
2310
|
+
return planAndMaybePruneRemoteObjects(plannedDeletes, { ...input, defaultMaxDeletes: 50 });
|
|
2295
2311
|
}
|
|
2296
2312
|
|
|
2297
2313
|
// src/utils/safe-write.ts
|
|
@@ -2711,7 +2727,7 @@ function buildCliSurface() {
|
|
|
2711
2727
|
program.command("health").option("--wiki <name>").option("--sync <mode>").option("--no-fail").option("--out <path>").option("--examples <n>");
|
|
2712
2728
|
program.command("doctor");
|
|
2713
2729
|
program.command("status").option("--wiki <name>");
|
|
2714
|
-
program.command("archive").option("--wiki <name>").option("--cascade").option("--apply");
|
|
2730
|
+
program.command("archive").option("--wiki <name>").option("--cascade").option("--apply").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>");
|
|
2715
2731
|
program.command("drift").option("--apply").option("--new <date>").option("--wiki <name>");
|
|
2716
2732
|
program.command("dedup").option("--apply").option("--canonical-policy <policy>").option("--manifest-out <path>").option("--manifest-in <path>").option("--remote <remote>").option("--remote-delete").option("--max-remote-deletes <n>").option("--wiki <name>");
|
|
2717
2733
|
program.command("migrate-citations").option("--dry-run").option("--wiki <name>");
|
|
@@ -8863,6 +8879,10 @@ export {
|
|
|
8863
8879
|
runLogRotate,
|
|
8864
8880
|
runTopicMapCheck,
|
|
8865
8881
|
runIndexLinkFormat,
|
|
8882
|
+
normalizeRemoteRoot,
|
|
8883
|
+
buildRemoteObjectPath,
|
|
8884
|
+
isValidRemoteDeleteCap,
|
|
8885
|
+
planAndMaybePruneRemoteObjects,
|
|
8866
8886
|
runDedup,
|
|
8867
8887
|
safeWritePage,
|
|
8868
8888
|
runFrontmatterFix,
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
TypedKnowledgeSchema,
|
|
7
7
|
appendLastOp,
|
|
8
8
|
assessSourceIdentity,
|
|
9
|
+
buildRemoteObjectPath,
|
|
9
10
|
clearLastOp,
|
|
10
11
|
configPath,
|
|
11
12
|
detectSchema,
|
|
@@ -18,10 +19,13 @@ import {
|
|
|
18
19
|
fixPathTooLong,
|
|
19
20
|
isBlockedHost,
|
|
20
21
|
isFailedRunStatus,
|
|
22
|
+
isValidRemoteDeleteCap,
|
|
21
23
|
loadFleetManifest,
|
|
24
|
+
normalizeRemoteRoot,
|
|
22
25
|
ok,
|
|
23
26
|
parseDotenvFile,
|
|
24
27
|
parseDotenvText,
|
|
28
|
+
planAndMaybePruneRemoteObjects,
|
|
25
29
|
profileKey,
|
|
26
30
|
readCliPackageJson,
|
|
27
31
|
readLastOp,
|
|
@@ -69,7 +73,7 @@ import {
|
|
|
69
73
|
triggerAutoUpdate,
|
|
70
74
|
writeCache,
|
|
71
75
|
writeDotenv
|
|
72
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-OPXMAS4O.js";
|
|
73
77
|
import {
|
|
74
78
|
normalizeDistTag
|
|
75
79
|
} from "./chunk-E6UWZ3S3.js";
|
|
@@ -1078,6 +1082,19 @@ function runVaultSyncHealth(home, syncMode) {
|
|
|
1078
1082
|
const filterPath = join9(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
1079
1083
|
const checks = [];
|
|
1080
1084
|
const pushScript = join9(shareDir, "wiki-push.sh");
|
|
1085
|
+
if (syncMode === "optional" && !existsSync3(pushScript)) {
|
|
1086
|
+
return {
|
|
1087
|
+
status: "pass",
|
|
1088
|
+
blocking: false,
|
|
1089
|
+
installed: false,
|
|
1090
|
+
summary: { pass: 1, info: 0, warn: 0, error: 0, skipped: 1 },
|
|
1091
|
+
checks: [{
|
|
1092
|
+
id: "vault_sync_installed",
|
|
1093
|
+
status: "pass",
|
|
1094
|
+
detail: `vault-sync not installed at ${pushScript}; optional check skipped`
|
|
1095
|
+
}]
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1081
1098
|
checks.push(existsSync3(pushScript) ? { id: "vault_sync_installed", label: "Vault sync installed", status: "pass", detail: `Found: ${pushScript}` } : { id: "vault_sync_installed", label: "Vault sync installed", status: "error", detail: `Script missing: ${pushScript}` });
|
|
1082
1099
|
if (isMac) {
|
|
1083
1100
|
const pushPlist = join9(home, "Library", "LaunchAgents", "com.karlchow.wiki-push.plist");
|
|
@@ -1217,6 +1234,7 @@ async function runHealth(input) {
|
|
|
1217
1234
|
if (!lint.result.ok) return { exitCode: lint.exitCode, result: lint.result };
|
|
1218
1235
|
const doctorStatus = statusFromCounts(doctor.result.data.summary);
|
|
1219
1236
|
const vaultSync = runVaultSyncHealth(input.home, syncMode);
|
|
1237
|
+
const vaultSyncCoverage = syncMode === "off" ? { state: "skipped", status: "pass", reason: "--sync off" } : !vaultSync.installed && vaultSync.summary.skipped > 0 && vaultSync.summary.warn === 0 && vaultSync.summary.error === 0 ? { state: "skipped", status: vaultSync.status, reason: "vault-sync not installed and --sync optional" } : { state: "checked", status: vaultSync.status };
|
|
1220
1238
|
const queryReadiness = deriveQueryReadiness(lint.result.data);
|
|
1221
1239
|
const sourceFreshness = {
|
|
1222
1240
|
status: bucketCount(lint.result.data.buckets, "stale_page") > 0 || bucketCount(lint.result.data.buckets, "file_source_url") > 0 ? "warn" : "pass",
|
|
@@ -1269,7 +1287,7 @@ async function runHealth(input) {
|
|
|
1269
1287
|
coverage: {
|
|
1270
1288
|
doctor: { state: "checked", status: doctorStatus },
|
|
1271
1289
|
lint: { state: "checked", status: lintComponent.status },
|
|
1272
|
-
vault_sync:
|
|
1290
|
+
vault_sync: vaultSyncCoverage,
|
|
1273
1291
|
query_readiness: { state: "checked", status: queryReadiness.status },
|
|
1274
1292
|
source_freshness: { state: "checked", status: sourceFreshness.status }
|
|
1275
1293
|
},
|
|
@@ -1339,6 +1357,12 @@ function arraysEqual(a, b) {
|
|
|
1339
1357
|
return true;
|
|
1340
1358
|
}
|
|
1341
1359
|
async function runArchive(input) {
|
|
1360
|
+
if (input.remoteDelete && !input.remote) {
|
|
1361
|
+
return { exitCode: ExitCode.USAGE, result: err("USAGE", { message: "--remote-delete requires --remote" }) };
|
|
1362
|
+
}
|
|
1363
|
+
if (input.remoteDelete && !isValidRemoteDeleteCap(input.maxRemoteDeletes)) {
|
|
1364
|
+
return { exitCode: ExitCode.USAGE, result: err("USAGE", { message: "--max-remote-deletes must be a positive integer" }) };
|
|
1365
|
+
}
|
|
1342
1366
|
const scan = await scanVault(input.vault);
|
|
1343
1367
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
1344
1368
|
const lookup = (pages) => {
|
|
@@ -1355,6 +1379,8 @@ async function runArchive(input) {
|
|
|
1355
1379
|
if (relPath.startsWith("_archive/")) return { exitCode: ExitCode.ARCHIVE_ALREADY_ARCHIVED, result: err("ARCHIVE_ALREADY_ARCHIVED", { page: relPath }) };
|
|
1356
1380
|
const slug = relPath.replace(/\.md$/, "").split("/").pop();
|
|
1357
1381
|
const archivePath = join10("_archive", relPath).replace(/\\/g, "/");
|
|
1382
|
+
const remoteRoot = normalizeRemoteRoot(input.remote);
|
|
1383
|
+
const remoteObjectPath = buildRemoteObjectPath(remoteRoot, relPath);
|
|
1358
1384
|
let cascade;
|
|
1359
1385
|
if (input.cascade) {
|
|
1360
1386
|
const wikilinkRefs = [];
|
|
@@ -1398,7 +1424,8 @@ async function runArchive(input) {
|
|
|
1398
1424
|
index_updated: false,
|
|
1399
1425
|
applied: false,
|
|
1400
1426
|
cascade,
|
|
1401
|
-
|
|
1427
|
+
...remoteObjectPath ? { remote: { plannedDeletes: [remoteObjectPath], deleted: [] } } : {},
|
|
1428
|
+
humanHint: summary + (remoteObjectPath ? ` (remote planned ${input.remoteDelete ? "delete" : "preview"}: ${remoteObjectPath})` : "")
|
|
1402
1429
|
})
|
|
1403
1430
|
};
|
|
1404
1431
|
}
|
|
@@ -1445,8 +1472,18 @@ ${fmRewritten}
|
|
|
1445
1472
|
files: [relPath],
|
|
1446
1473
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1447
1474
|
});
|
|
1475
|
+
let remote;
|
|
1476
|
+
if (remoteObjectPath) {
|
|
1477
|
+
const plannedDeletes = [remoteObjectPath];
|
|
1478
|
+
const pruned = await planAndMaybePruneRemoteObjects(plannedDeletes, input);
|
|
1479
|
+
if (!pruned.ok) {
|
|
1480
|
+
return { exitCode: ExitCode.SYNC_PUSH_FAILED, result: pruned };
|
|
1481
|
+
}
|
|
1482
|
+
remote = pruned.data;
|
|
1483
|
+
}
|
|
1448
1484
|
const applied = input.cascade ? true : void 0;
|
|
1449
1485
|
const cascadeNote = input.cascade ? ` (cascade: ${cascade.source_array_refs.length} src arrays updated, ${cascade.wikilink_refs.length} wikilinks reported)` : "";
|
|
1486
|
+
const remoteNote = remote ? ` (remote ${input.remoteDelete ? `deleted ${remote.deleted.length}` : `planned ${remote.plannedDeletes.length}`})` : "";
|
|
1450
1487
|
return {
|
|
1451
1488
|
exitCode: ExitCode.OK,
|
|
1452
1489
|
result: ok({
|
|
@@ -1455,7 +1492,8 @@ ${fmRewritten}
|
|
|
1455
1492
|
index_updated: indexUpdated,
|
|
1456
1493
|
...applied !== void 0 ? { applied } : {},
|
|
1457
1494
|
...cascade ? { cascade } : {},
|
|
1458
|
-
|
|
1495
|
+
...remote ? { remote } : {},
|
|
1496
|
+
humanHint: `${relPath} -> ${archivePath}${indexUpdated ? " (index updated)" : ""}${cascadeNote}${remoteNote}`
|
|
1459
1497
|
})
|
|
1460
1498
|
};
|
|
1461
1499
|
}
|
|
@@ -4886,10 +4924,18 @@ program.command("status [vault]").description("output vault diagnostics").option
|
|
|
4886
4924
|
langEnvValue: process.env.WIKI_LANG
|
|
4887
4925
|
}), v.vault);
|
|
4888
4926
|
});
|
|
4889
|
-
program.command("archive <page> [vault]").description("archive a typed-knowledge or raw page").option("--wiki <name>", "wiki profile name").option("--cascade", "scan vault for references (wikilinks + sources arrays); preview by default", false).option("--apply", "with --cascade: mutate sources arrays and archive (without --apply, --cascade is preview-only)", false).action(async (page, vault, opts) => {
|
|
4927
|
+
program.command("archive <page> [vault]").description("archive a typed-knowledge or raw page").option("--wiki <name>", "wiki profile name").option("--cascade", "scan vault for references (wikilinks + sources arrays); preview by default", false).option("--apply", "with --cascade: mutate sources arrays and archive (without --apply, --cascade is preview-only)", false).option("--remote <remote>", "rclone remote root to prune the archived source path, for example seaweed-wiki:cloud/wiki").option("--remote-delete", "delete the archived source path from the remote after local archive", false).option("--max-remote-deletes <n>", "maximum remote object deletes allowed", "1").action(async (page, vault, opts) => {
|
|
4890
4928
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4891
4929
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4892
|
-
else emit(await runArchive({
|
|
4930
|
+
else emit(await runArchive({
|
|
4931
|
+
vault: v.vault,
|
|
4932
|
+
page,
|
|
4933
|
+
cascade: !!opts.cascade,
|
|
4934
|
+
apply: !!opts.apply,
|
|
4935
|
+
remote: opts.remote,
|
|
4936
|
+
remoteDelete: !!opts.remoteDelete,
|
|
4937
|
+
maxRemoteDeletes: Number.parseInt(opts.maxRemoteDeletes, 10)
|
|
4938
|
+
}), v.vault);
|
|
4893
4939
|
});
|
|
4894
4940
|
program.command("drift [vault]").description("detect content drift in raw sources").option("--apply", "update sha256 in drifted sources").option("--new <date>", "list raw files ingested on/after this date (YYYY-MM-DD)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4895
4941
|
const v = await resolveVaultArg(vault, opts.wiki);
|
package/dist/skillwiki-mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.33",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|
package/skills/package.json
CHANGED
|
@@ -22,7 +22,7 @@ Standard four reads (SCHEMA, index, log, project context if applicable).
|
|
|
22
22
|
|
|
23
23
|
0. Resolve vault: `skillwiki path` and `skillwiki lang`.
|
|
24
24
|
1. Identify the target page. Confirm with the user which page to archive (show full relPath).
|
|
25
|
-
2. Run `skillwiki archive <page> [vault]`. Read the JSON output.
|
|
25
|
+
2. Run `skillwiki archive <page> [vault]`. On a vault-sync leaf host where S3 stale originals must be pruned, use `skillwiki archive <page> [vault] --remote seaweed-wiki:cloud/wiki --remote-delete --max-remote-deletes 1` only when that remote path deletion is explicitly intended. Read the JSON output.
|
|
26
26
|
3. Verify with `skillwiki index-check [vault]` — confirm no ghost entries remain.
|
|
27
27
|
4. Run `skillwiki lint [vault]` — check for broken wikilinks from other pages that still reference the archived page. If found, update those pages to point to the replacement or remove the stale link.
|
|
28
28
|
5. **Raw file archiving (N9 Reingest Protocol only):** When archiving a `raw/` file due to content drift, update ALL `^[raw/...]` citation markers and `sources:` frontmatter entries that reference the old path. Change `raw/articles/foo.md` to `_archive/raw/articles/foo.md` in every referencing page. Verify with `skillwiki audit` that no broken markers remain.
|
|
@@ -30,7 +30,7 @@ Standard four reads (SCHEMA, index, log, project context if applicable).
|
|
|
30
30
|
|
|
31
31
|
## Reversibility
|
|
32
32
|
|
|
33
|
-
Archiving is reversible: move the file back from `_archive/` to its original directory and re-add the wikilink entry to `index.md`.
|
|
33
|
+
Archiving is locally reversible: move the file back from `_archive/` to its original directory and re-add the wikilink entry to `index.md`. If `--remote-delete` was used, the stale active-path object is pruned from the remote after the archive move, but the archived copy remains and a restore will republish the active path on the next push.
|
|
34
34
|
|
|
35
35
|
## Stop conditions
|
|
36
36
|
|
|
@@ -42,4 +42,4 @@ Archiving is reversible: move the file back from `_archive/` to its original dir
|
|
|
42
42
|
- Archiving `raw/` files outside the N9 Reingest Protocol (raw is immutable except during content-drift reingestion).
|
|
43
43
|
- Archiving raw files without updating all `^[raw/...]` citation markers that reference them.
|
|
44
44
|
- Archiving without user confirmation.
|
|
45
|
-
- Deleting files (archive moves
|
|
45
|
+
- Deleting local vault files directly (archive moves locally; remote stale-path pruning is only allowed through the explicit `skillwiki archive --remote ... --remote-delete --max-remote-deletes 1` path).
|
|
@@ -22,7 +22,7 @@ Standard four reads (SCHEMA, index, log, project context if applicable).
|
|
|
22
22
|
|
|
23
23
|
0. Resolve vault: `skillwiki path` and `skillwiki lang`.
|
|
24
24
|
1. Identify the target page. Confirm with the user which page to archive (show full relPath).
|
|
25
|
-
2. Run `skillwiki archive <page> [vault]`. Read the JSON output.
|
|
25
|
+
2. Run `skillwiki archive <page> [vault]`. On a vault-sync leaf host where S3 stale originals must be pruned, use `skillwiki archive <page> [vault] --remote seaweed-wiki:cloud/wiki --remote-delete --max-remote-deletes 1` only when that remote path deletion is explicitly intended. Read the JSON output.
|
|
26
26
|
3. Verify with `skillwiki index-check [vault]` — confirm no ghost entries remain.
|
|
27
27
|
4. Run `skillwiki lint [vault]` — check for broken wikilinks from other pages that still reference the archived page. If found, update those pages to point to the replacement or remove the stale link.
|
|
28
28
|
5. **Raw file archiving (N9 Reingest Protocol only):** When archiving a `raw/` file due to content drift, update ALL `^[raw/...]` citation markers and `sources:` frontmatter entries that reference the old path. Change `raw/articles/foo.md` to `_archive/raw/articles/foo.md` in every referencing page. Verify with `skillwiki audit` that no broken markers remain.
|
|
@@ -30,7 +30,7 @@ Standard four reads (SCHEMA, index, log, project context if applicable).
|
|
|
30
30
|
|
|
31
31
|
## Reversibility
|
|
32
32
|
|
|
33
|
-
Archiving is reversible: move the file back from `_archive/` to its original directory and re-add the wikilink entry to `index.md`.
|
|
33
|
+
Archiving is locally reversible: move the file back from `_archive/` to its original directory and re-add the wikilink entry to `index.md`. If `--remote-delete` was used, the stale active-path object is pruned from the remote after the archive move, but the archived copy remains and a restore will republish the active path on the next push.
|
|
34
34
|
|
|
35
35
|
## Stop conditions
|
|
36
36
|
|
|
@@ -42,4 +42,4 @@ Archiving is reversible: move the file back from `_archive/` to its original dir
|
|
|
42
42
|
- Archiving `raw/` files outside the N9 Reingest Protocol (raw is immutable except during content-drift reingestion).
|
|
43
43
|
- Archiving raw files without updating all `^[raw/...]` citation markers that reference them.
|
|
44
44
|
- Archiving without user confirmation.
|
|
45
|
-
- Deleting files (archive moves
|
|
45
|
+
- Deleting local vault files directly (archive moves locally; remote stale-path pruning is only allowed through the explicit `skillwiki archive --remote ... --remote-delete --max-remote-deletes 1` path).
|