skillwiki 0.9.34 → 0.9.35
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.
|
@@ -63,7 +63,8 @@ var ExitCode = {
|
|
|
63
63
|
LOG_APPEND_LOCK_HELD: 49,
|
|
64
64
|
FLEET_MANIFEST_INVALID: 50,
|
|
65
65
|
SENSITIVE_CONTENT_DETECTED: 51,
|
|
66
|
-
FLEET_SATELLITE_HEALTH_FAILED: 52
|
|
66
|
+
FLEET_SATELLITE_HEALTH_FAILED: 52,
|
|
67
|
+
PROTECTED_SNAPSHOTTER_WRITE_BLOCKED: 53
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
// ../shared/src/json-output.ts
|
|
@@ -4398,7 +4399,7 @@ function formatKnownContext(input) {
|
|
|
4398
4399
|
const selfAliases = collectSelfAliases(input.manifest, input.hostId);
|
|
4399
4400
|
const outbound = collectOutboundAccess(input.manifest, input.hostId);
|
|
4400
4401
|
const maintenanceLines = formatMaintenanceLines(host);
|
|
4401
|
-
const guidance = input.hostId === "macos-dev" ? "use declared SSH aliases for remote work when needed; do not assume undeclared hosts have reciprocal SSH access." : `this session is already on \`${input.hostId}\`; do not SSH to self aliases unless the user explicitly asks. Do not assume outbound SSH to other fleet hosts is configured.`;
|
|
4402
|
+
const guidance = host.role === "snapshotter" && host.protected === true ? `this session is already on \`${input.hostId}\`; this is a protected snapshotter host. Do not mutate the vault or repo-local project workspaces from this session except explicitly approved snapshot maintenance. Use read-only investigation here and route authoring to a leaf host.` : input.hostId === "macos-dev" ? "use declared SSH aliases for remote work when needed; do not assume undeclared hosts have reciprocal SSH access." : `this session is already on \`${input.hostId}\`; do not SSH to self aliases unless the user explicitly asks. Do not assume outbound SSH to other fleet hosts is configured.`;
|
|
4402
4403
|
return [
|
|
4403
4404
|
"## Runtime Host Context",
|
|
4404
4405
|
"",
|
|
@@ -8928,6 +8929,7 @@ export {
|
|
|
8928
8929
|
FLEET_REL_PATH,
|
|
8929
8930
|
runFleetValidate,
|
|
8930
8931
|
runFleetContext,
|
|
8932
|
+
loadFleetManifestAndHost,
|
|
8931
8933
|
loadFleetManifest,
|
|
8932
8934
|
resolveFleetHostId,
|
|
8933
8935
|
SATELLITE_STALE_MS,
|
package/dist/cli.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isFailedRunStatus,
|
|
23
23
|
isValidRemoteDeleteCap,
|
|
24
24
|
loadFleetManifest,
|
|
25
|
+
loadFleetManifestAndHost,
|
|
25
26
|
normalizeRemoteRoot,
|
|
26
27
|
ok,
|
|
27
28
|
parseDotenvFile,
|
|
@@ -74,7 +75,7 @@ import {
|
|
|
74
75
|
triggerAutoUpdate,
|
|
75
76
|
writeCache,
|
|
76
77
|
writeDotenv
|
|
77
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-TK4ZIQNE.js";
|
|
78
79
|
import {
|
|
79
80
|
normalizeDistTag
|
|
80
81
|
} from "./chunk-E6UWZ3S3.js";
|
|
@@ -4754,6 +4755,36 @@ async function postCommit(vault, exitCode) {
|
|
|
4754
4755
|
clearLastOp(vault);
|
|
4755
4756
|
}
|
|
4756
4757
|
|
|
4758
|
+
// src/utils/protected-vault-write-guard.ts
|
|
4759
|
+
async function guardProtectedVaultWrite(input) {
|
|
4760
|
+
const load = await loadFleetManifestAndHost({
|
|
4761
|
+
vault: input.vault,
|
|
4762
|
+
hostId: input.hostId,
|
|
4763
|
+
env: input.env ?? process.env,
|
|
4764
|
+
home: input.home ?? process.env.HOME ?? "",
|
|
4765
|
+
cwd: input.cwd ?? process.cwd(),
|
|
4766
|
+
osHostname: input.osHostname ?? process.env.HOSTNAME,
|
|
4767
|
+
user: input.user ?? process.env.USER
|
|
4768
|
+
});
|
|
4769
|
+
if (!load?.hostId || load.identityStatus !== "known") {
|
|
4770
|
+
return { blocked: false };
|
|
4771
|
+
}
|
|
4772
|
+
const host = load.manifest.hosts[load.hostId];
|
|
4773
|
+
if (!host || host.role !== "snapshotter" || host.protected !== true) {
|
|
4774
|
+
return { blocked: false };
|
|
4775
|
+
}
|
|
4776
|
+
return {
|
|
4777
|
+
blocked: true,
|
|
4778
|
+
exitCode: ExitCode.PROTECTED_SNAPSHOTTER_WRITE_BLOCKED,
|
|
4779
|
+
result: err("PROTECTED_SNAPSHOTTER_WRITE_BLOCKED", {
|
|
4780
|
+
host_id: load.hostId,
|
|
4781
|
+
command: input.command,
|
|
4782
|
+
reason: `refusing vault mutation from protected snapshotter host '${load.hostId}'`,
|
|
4783
|
+
guidance: "Use a leaf authoring host for vault/project writes. Keep protected snapshotter sessions read-only except explicit snapshot maintenance."
|
|
4784
|
+
})
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4757
4788
|
// src/cli.ts
|
|
4758
4789
|
var pkg = readCliPackageJson();
|
|
4759
4790
|
var program = new Command();
|
|
@@ -4765,6 +4796,21 @@ async function emit(r, vault, opts) {
|
|
|
4765
4796
|
if (vault && opts?.postCommit !== false) await postCommit(vault, r.exitCode);
|
|
4766
4797
|
process.exit(r.exitCode);
|
|
4767
4798
|
}
|
|
4799
|
+
async function emitGuardedVaultWrite(vault, command, run, opts) {
|
|
4800
|
+
const guard = await guardProtectedVaultWrite({
|
|
4801
|
+
vault,
|
|
4802
|
+
command,
|
|
4803
|
+
env: process.env,
|
|
4804
|
+
home: process.env.HOME ?? "",
|
|
4805
|
+
cwd: process.cwd(),
|
|
4806
|
+
osHostname: process.env.HOSTNAME,
|
|
4807
|
+
user: process.env.USER
|
|
4808
|
+
});
|
|
4809
|
+
if (guard.blocked) {
|
|
4810
|
+
return emit({ exitCode: guard.exitCode, result: guard.result }, void 0, { postCommit: false });
|
|
4811
|
+
}
|
|
4812
|
+
return emit(await run(), vault, opts);
|
|
4813
|
+
}
|
|
4768
4814
|
program.command("hash <file>").description("compute SHA-256 hash of a vault page body").action(async (file) => emit(await runHash({ file })));
|
|
4769
4815
|
program.command("fetch-guard <url>").description("check if a URL passes fetch guard rules and sanitize secrets").action(async (url) => emit(await runFetchGuard({ url })));
|
|
4770
4816
|
program.command("validate <file>").description("validate vault page frontmatter against its detected schema").option("--apply", "auto-update vault index.md and log.md after successful validation", false).option("--vault <dir>", "vault root directory (required with --apply)").option("--wiki <name>", "wiki profile name").action(async (file, opts) => {
|
|
@@ -4774,17 +4820,20 @@ program.command("validate <file>").description("validate vault page frontmatter
|
|
|
4774
4820
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4775
4821
|
else vault = v.vault;
|
|
4776
4822
|
}
|
|
4823
|
+
if (opts.apply && vault) {
|
|
4824
|
+
return emitGuardedVaultWrite(vault, "validate --apply", () => runValidate({ file, apply: true, vault }), void 0);
|
|
4825
|
+
}
|
|
4777
4826
|
emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
|
|
4778
4827
|
});
|
|
4779
4828
|
program.command("graph").description("graph subcommands").command("build <vault>").option("--out <path>", "graph output path (default: <vault>/.skillwiki/graph.json)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4780
4829
|
const out = opts.out ?? join25(vault, ".skillwiki", "graph.json");
|
|
4781
|
-
|
|
4830
|
+
return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
|
|
4782
4831
|
});
|
|
4783
4832
|
var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
|
|
4784
4833
|
canvasCmd.command("generate [vault]").description("generate .canvas from graph.json").option("--graph-path <path>", "explicit path to graph.json").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4785
4834
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4786
4835
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4787
|
-
else
|
|
4836
|
+
else return emitGuardedVaultWrite(v.vault, "canvas generate", () => runCanvasGenerate({ vault: v.vault, graphPath: opts.graphPath }));
|
|
4788
4837
|
});
|
|
4789
4838
|
program.command("overlap [vault]").description("detect typed-knowledge pages that share the same raw sources").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4790
4839
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -4886,12 +4935,21 @@ program.command("topic-map-check [vault]").description("check whether a topic ma
|
|
|
4886
4935
|
program.command("stale [vault]").description("identify stale transcripts and incomplete work items").option("--archive", "move stale items to _archive/", false).option("--days <n>", "staleness threshold in days", (s) => parseInt(s, 10), 3).option("--force-scan", "infer kind/project from filename and content when frontmatter is missing", false).option("--project <slug>", "scope to a single project").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4887
4936
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4888
4937
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4889
|
-
else
|
|
4938
|
+
else if (opts.archive) return emitGuardedVaultWrite(
|
|
4939
|
+
v.vault,
|
|
4940
|
+
"stale --archive",
|
|
4941
|
+
() => runStale({ vault: v.vault, days: opts.days, archive: true, forceScan: !!opts.forceScan, project: opts.project })
|
|
4942
|
+
);
|
|
4943
|
+
else emit(await runStale({ vault: v.vault, days: opts.days, archive: false, forceScan: !!opts.forceScan, project: opts.project }), v.vault);
|
|
4890
4944
|
});
|
|
4891
4945
|
program.command("claim <transcript> [vault]").description("claim an unclaimed transcript by creating a work item with source: link").option("--project <slug>", "project slug (overrides transcript frontmatter)").option("--slug <slug>", "work-item slug (defaults to transcript filename without date/kind prefix)").option("--wiki <name>", "wiki profile name").action(async (transcript, vault, opts) => {
|
|
4892
4946
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4893
4947
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4894
|
-
else
|
|
4948
|
+
else return emitGuardedVaultWrite(
|
|
4949
|
+
v.vault,
|
|
4950
|
+
"claim",
|
|
4951
|
+
() => runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug })
|
|
4952
|
+
);
|
|
4895
4953
|
});
|
|
4896
4954
|
program.command("pagesize [vault]").description("report page sizes and flag oversized pages").option("--lines <n>", "max body lines", (s) => parseInt(s, 10), 200).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4897
4955
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -4901,23 +4959,47 @@ program.command("pagesize [vault]").description("report page sizes and flag over
|
|
|
4901
4959
|
program.command("log-rotate [vault]").description("rotate or trim the vault log file").option("--threshold <n>", "entry count threshold", (s) => parseInt(s, 10), 500).option("--apply", "actually rotate", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4902
4960
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4903
4961
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4904
|
-
else
|
|
4962
|
+
else if (opts.apply) return emitGuardedVaultWrite(
|
|
4963
|
+
v.vault,
|
|
4964
|
+
"log-rotate --apply",
|
|
4965
|
+
() => runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: true })
|
|
4966
|
+
);
|
|
4967
|
+
else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: false }), v.vault);
|
|
4905
4968
|
});
|
|
4906
4969
|
program.command("log-append [vault]").description("append a single entry to the vault log under a short advisory lock").requiredOption("--content <text>", "log entry text to append").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4907
4970
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4908
4971
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4909
|
-
else
|
|
4972
|
+
else return emitGuardedVaultWrite(
|
|
4973
|
+
v.vault,
|
|
4974
|
+
"log-append",
|
|
4975
|
+
() => runLogAppend({ vault: v.vault, content: opts.content })
|
|
4976
|
+
);
|
|
4910
4977
|
});
|
|
4911
4978
|
program.command("lint [vault]").description("run all vault health checks").option("--days <n>", "stale threshold", (s) => parseInt(s, 10), 90).option("--lines <n>", "pagesize threshold", (s) => parseInt(s, 10), 200).option("--log-threshold <n>", "log rotation threshold", (s) => parseInt(s, 10), 500).option("--fix", "auto-fix supported lint violations").option("--only <bucket>", "run only the specified lint bucket").option("--summary", "emit bounded bucket counts instead of full item arrays", false).option("--examples <n>", "example count per bucket in summary mode", (s) => parseInt(s, 10), 3).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4912
4979
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4913
4980
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4981
|
+
else if (opts.fix) return emitGuardedVaultWrite(
|
|
4982
|
+
v.vault,
|
|
4983
|
+
"lint --fix",
|
|
4984
|
+
() => runLint({
|
|
4985
|
+
vault: v.vault,
|
|
4986
|
+
source: vault ? "flag" : void 0,
|
|
4987
|
+
days: opts.days,
|
|
4988
|
+
lines: opts.lines,
|
|
4989
|
+
logThreshold: opts.logThreshold,
|
|
4990
|
+
fix: true,
|
|
4991
|
+
only: opts.only,
|
|
4992
|
+
summary: !!opts.summary,
|
|
4993
|
+
examplesLimit: opts.summary ? opts.examples : void 0
|
|
4994
|
+
})
|
|
4995
|
+
);
|
|
4914
4996
|
else if (opts.summary) emit(await runLint({
|
|
4915
4997
|
vault: v.vault,
|
|
4916
4998
|
source: vault ? "flag" : void 0,
|
|
4917
4999
|
days: opts.days,
|
|
4918
5000
|
lines: opts.lines,
|
|
4919
5001
|
logThreshold: opts.logThreshold,
|
|
4920
|
-
fix:
|
|
5002
|
+
fix: false,
|
|
4921
5003
|
only: opts.only,
|
|
4922
5004
|
summary: true,
|
|
4923
5005
|
examplesLimit: opts.examples
|
|
@@ -4928,7 +5010,7 @@ program.command("lint [vault]").description("run all vault health checks").optio
|
|
|
4928
5010
|
days: opts.days,
|
|
4929
5011
|
lines: opts.lines,
|
|
4930
5012
|
logThreshold: opts.logThreshold,
|
|
4931
|
-
fix:
|
|
5013
|
+
fix: false,
|
|
4932
5014
|
only: opts.only
|
|
4933
5015
|
}), v.vault);
|
|
4934
5016
|
});
|
|
@@ -4977,24 +5059,56 @@ program.command("status [vault]").description("output vault diagnostics").option
|
|
|
4977
5059
|
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) => {
|
|
4978
5060
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4979
5061
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4980
|
-
else emit(await runArchive({
|
|
5062
|
+
else if (opts.cascade && !opts.apply) emit(await runArchive({
|
|
4981
5063
|
vault: v.vault,
|
|
4982
5064
|
page,
|
|
4983
|
-
cascade:
|
|
4984
|
-
apply:
|
|
5065
|
+
cascade: true,
|
|
5066
|
+
apply: false,
|
|
4985
5067
|
remote: opts.remote,
|
|
4986
5068
|
remoteDelete: !!opts.remoteDelete,
|
|
4987
5069
|
maxRemoteDeletes: Number.parseInt(opts.maxRemoteDeletes, 10)
|
|
4988
5070
|
}), v.vault);
|
|
5071
|
+
else return emitGuardedVaultWrite(
|
|
5072
|
+
v.vault,
|
|
5073
|
+
"archive",
|
|
5074
|
+
() => runArchive({
|
|
5075
|
+
vault: v.vault,
|
|
5076
|
+
page,
|
|
5077
|
+
cascade: !!opts.cascade,
|
|
5078
|
+
apply: !!opts.apply,
|
|
5079
|
+
remote: opts.remote,
|
|
5080
|
+
remoteDelete: !!opts.remoteDelete,
|
|
5081
|
+
maxRemoteDeletes: Number.parseInt(opts.maxRemoteDeletes, 10)
|
|
5082
|
+
})
|
|
5083
|
+
);
|
|
4989
5084
|
});
|
|
4990
5085
|
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) => {
|
|
4991
5086
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4992
5087
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
4993
|
-
else
|
|
5088
|
+
else if (opts.apply) return emitGuardedVaultWrite(
|
|
5089
|
+
v.vault,
|
|
5090
|
+
"drift --apply",
|
|
5091
|
+
() => runDrift({ vault: v.vault, apply: true, newSince: opts.new })
|
|
5092
|
+
);
|
|
5093
|
+
else emit(await runDrift({ vault: v.vault, apply: false, newSince: opts.new }), v.vault);
|
|
4994
5094
|
});
|
|
4995
5095
|
program.command("dedup [vault]").description("detect duplicate raw sources by sha256").option("--apply", "rewire citations and remove duplicate raw files", false).option("--canonical-policy <policy>", "canonical policy: stable-path or scan-order", "stable-path").option("--manifest-out <path>", "write raw dedup delete manifest before applying").option("--manifest-in <path>", "read existing raw dedup delete manifest for remote pruning").option("--remote <remote>", "rclone remote root, for example seaweed-wiki:cloud/wiki").option("--remote-delete", "delete manifest duplicate paths from the remote", false).option("--max-remote-deletes <n>", "maximum remote object deletes allowed", "50").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
4996
5096
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
4997
5097
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5098
|
+
else if (opts.apply || opts.manifestOut) return emitGuardedVaultWrite(
|
|
5099
|
+
v.vault,
|
|
5100
|
+
opts.apply ? "dedup --apply" : "dedup --manifest-out",
|
|
5101
|
+
() => runDedup({
|
|
5102
|
+
vault: v.vault,
|
|
5103
|
+
apply: opts.apply,
|
|
5104
|
+
canonicalPolicy: opts.canonicalPolicy,
|
|
5105
|
+
manifestOut: opts.manifestOut,
|
|
5106
|
+
manifestIn: opts.manifestIn,
|
|
5107
|
+
remote: opts.remote,
|
|
5108
|
+
remoteDelete: !!opts.remoteDelete,
|
|
5109
|
+
maxRemoteDeletes: Number.parseInt(opts.maxRemoteDeletes, 10)
|
|
5110
|
+
})
|
|
5111
|
+
);
|
|
4998
5112
|
else emit(await runDedup({
|
|
4999
5113
|
vault: v.vault,
|
|
5000
5114
|
apply: opts.apply,
|
|
@@ -5009,12 +5123,22 @@ program.command("dedup [vault]").description("detect duplicate raw sources by sh
|
|
|
5009
5123
|
program.command("migrate-citations [vault]").description("migrate ^[raw/...] markers to paragraph-end citations").option("--dry-run", "preview changes without writing", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5010
5124
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5011
5125
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5012
|
-
else emit(await runMigrateCitations({ vault: v.vault, dryRun:
|
|
5126
|
+
else if (opts.dryRun) emit(await runMigrateCitations({ vault: v.vault, dryRun: true }), v.vault);
|
|
5127
|
+
else return emitGuardedVaultWrite(
|
|
5128
|
+
v.vault,
|
|
5129
|
+
"migrate-citations",
|
|
5130
|
+
() => runMigrateCitations({ vault: v.vault, dryRun: false })
|
|
5131
|
+
);
|
|
5013
5132
|
});
|
|
5014
5133
|
program.command("frontmatter-fix [vault]").description("fix common frontmatter issues on typed-knowledge pages").option("--dry-run", "preview changes without writing", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5015
5134
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5016
5135
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5017
|
-
else emit(await runFrontmatterFix({ vault: v.vault, dryRun:
|
|
5136
|
+
else if (opts.dryRun) emit(await runFrontmatterFix({ vault: v.vault, dryRun: true }), v.vault);
|
|
5137
|
+
else return emitGuardedVaultWrite(
|
|
5138
|
+
v.vault,
|
|
5139
|
+
"frontmatter-fix",
|
|
5140
|
+
() => runFrontmatterFix({ vault: v.vault, dryRun: false })
|
|
5141
|
+
);
|
|
5018
5142
|
});
|
|
5019
5143
|
program.command("update").description("update skillwiki CLI from npm dist-tag").option("--tag <tag>", "npm dist-tag", "latest").action(async (opts) => emit(await runUpdate({
|
|
5020
5144
|
home: process.env.HOME ?? "",
|
|
@@ -5033,13 +5157,23 @@ program.command("transcripts [vault]").description("list transcript files in raw
|
|
|
5033
5157
|
program.command("project-index <slug> [vault]").description("generate a knowledge index for a project workspace").option("--apply", "write knowledge.md to the project directory", false).option("--wiki <name>", "wiki profile name").action(async (slug, vault, opts) => {
|
|
5034
5158
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5035
5159
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5036
|
-
else
|
|
5160
|
+
else if (opts.apply) return emitGuardedVaultWrite(
|
|
5161
|
+
v.vault,
|
|
5162
|
+
"project-index --apply",
|
|
5163
|
+
() => runProjectIndex({ vault: v.vault, slug, apply: true })
|
|
5164
|
+
);
|
|
5165
|
+
else emit(await runProjectIndex({ vault: v.vault, slug, apply: false }), v.vault);
|
|
5037
5166
|
});
|
|
5038
5167
|
var compoundCmd = program.command("compound").description("manage project compound entries");
|
|
5039
5168
|
compoundCmd.command("promote [vault]").description("promote retros with Generalize?: yes to compound entries").requiredOption("--project <slug>", "project slug (e.g., llm-wiki)").option("--dry-run", "preview promotions without writing files", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5040
5169
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5041
5170
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5042
|
-
else emit(await runCompound({ vault: v.vault, project: opts.project, dryRun:
|
|
5171
|
+
else if (opts.dryRun) emit(await runCompound({ vault: v.vault, project: opts.project, dryRun: true }), v.vault);
|
|
5172
|
+
else return emitGuardedVaultWrite(
|
|
5173
|
+
v.vault,
|
|
5174
|
+
"compound promote",
|
|
5175
|
+
() => runCompound({ vault: v.vault, project: opts.project, dryRun: false })
|
|
5176
|
+
);
|
|
5043
5177
|
});
|
|
5044
5178
|
compoundCmd.command("list [vault]").description("list compound entries for a project").requiredOption("--project <slug>", "project slug (e.g., llm-wiki)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5045
5179
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -5049,12 +5183,21 @@ compoundCmd.command("list [vault]").description("list compound entries for a pro
|
|
|
5049
5183
|
compoundCmd.command("delete <entry> [vault]").description("delete a compound entry and regenerate knowledge index").requiredOption("--project <slug>", "project slug (e.g., llm-wiki)").option("--wiki <name>", "wiki profile name").action(async (entry, vault, opts) => {
|
|
5050
5184
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5051
5185
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5052
|
-
else
|
|
5186
|
+
else return emitGuardedVaultWrite(
|
|
5187
|
+
v.vault,
|
|
5188
|
+
"compound delete",
|
|
5189
|
+
() => runCompoundDelete({ vault: v.vault, project: opts.project, entry })
|
|
5190
|
+
);
|
|
5053
5191
|
});
|
|
5054
5192
|
program.command("tag-sync [vault]").description("mirror frontmatter enum values to nested Obsidian tags").option("--dry-run", "preview changes without writing", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5055
5193
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5056
5194
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5057
|
-
else emit(await runTagSync({ vault: v.vault, dryRun:
|
|
5195
|
+
else if (opts.dryRun) emit(await runTagSync({ vault: v.vault, dryRun: true }), v.vault);
|
|
5196
|
+
else return emitGuardedVaultWrite(
|
|
5197
|
+
v.vault,
|
|
5198
|
+
"tag-sync",
|
|
5199
|
+
() => runTagSync({ vault: v.vault, dryRun: false })
|
|
5200
|
+
);
|
|
5058
5201
|
});
|
|
5059
5202
|
var syncCmd = program.command("sync").description("manage vault sync");
|
|
5060
5203
|
syncCmd.command("status [vault]").description("check vault git sync status").option("--wiki <name>", "wiki profile name").option("--include-stashes", "enumerate all stashes in output", false).action(async (vault, opts) => {
|
|
@@ -5065,25 +5208,41 @@ syncCmd.command("status [vault]").description("check vault git sync status").opt
|
|
|
5065
5208
|
syncCmd.command("push [vault]").description("lint, commit, and push vault changes to remote").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5066
5209
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5067
5210
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5068
|
-
else
|
|
5211
|
+
else return emitGuardedVaultWrite(
|
|
5212
|
+
v.vault,
|
|
5213
|
+
"sync push",
|
|
5214
|
+
() => runSyncPush({ vault: v.vault })
|
|
5215
|
+
);
|
|
5069
5216
|
});
|
|
5070
5217
|
syncCmd.command("pull [vault]").description("pull remote vault changes and lint").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5071
5218
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5072
5219
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5073
|
-
else
|
|
5220
|
+
else return emitGuardedVaultWrite(
|
|
5221
|
+
v.vault,
|
|
5222
|
+
"sync pull",
|
|
5223
|
+
() => runSyncPull({ vault: v.vault })
|
|
5224
|
+
);
|
|
5074
5225
|
});
|
|
5075
5226
|
syncCmd.command("lock [vault]").description("acquire advisory lock on vault").option("--summary <text>", "lock description", "skillwiki sync").option("--ttl-minutes <n>", "lock time-to-live in minutes", "30").option("--force", "overwrite existing lock", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5076
5227
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5077
5228
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5078
5229
|
else {
|
|
5079
5230
|
const ttl = parseInt(opts.ttlMinutes, 10) || 30;
|
|
5080
|
-
|
|
5231
|
+
return emitGuardedVaultWrite(
|
|
5232
|
+
v.vault,
|
|
5233
|
+
"sync lock",
|
|
5234
|
+
async () => runSyncLock({ vault: v.vault, summary: opts.summary, ttlMinutes: ttl, force: !!opts.force, sessionId: getCliSessionId() })
|
|
5235
|
+
);
|
|
5081
5236
|
}
|
|
5082
5237
|
});
|
|
5083
5238
|
syncCmd.command("unlock [vault]").description("release advisory lock on vault").option("--force", "release lock regardless of holder", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5084
5239
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5085
5240
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5086
|
-
else
|
|
5241
|
+
else return emitGuardedVaultWrite(
|
|
5242
|
+
v.vault,
|
|
5243
|
+
"sync unlock",
|
|
5244
|
+
async () => runSyncUnlock({ vault: v.vault, force: !!opts.force, sessionId: getCliSessionId() })
|
|
5245
|
+
);
|
|
5087
5246
|
});
|
|
5088
5247
|
syncCmd.command("peers [vault]").description("list active locks and recent wiki-sync stashes").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5089
5248
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -5118,41 +5277,65 @@ backupCmd.command("restore [vault]").description("restore vault from S3-compatib
|
|
|
5118
5277
|
}
|
|
5119
5278
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
|
|
5120
5279
|
const dotenv = await parseDotenvFile(configPath(home));
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5280
|
+
return emitGuardedVaultWrite(
|
|
5281
|
+
v.vault,
|
|
5282
|
+
"backup restore",
|
|
5283
|
+
() => runBackupRestore({
|
|
5284
|
+
vault: v.vault,
|
|
5285
|
+
bucket: opts.bucket ?? dotenv["BACKUP_BUCKET"] ?? "",
|
|
5286
|
+
endpoint: opts.endpoint ?? dotenv["BACKUP_ENDPOINT"] ?? "",
|
|
5287
|
+
region: opts.region ?? dotenv["BACKUP_REGION"] ?? "us-east-1",
|
|
5288
|
+
accessKeyId: dotenv["BACKUP_ACCESS_KEY_ID"] ?? "",
|
|
5289
|
+
secretAccessKey: dotenv["BACKUP_SECRET_ACCESS_KEY"] ?? "",
|
|
5290
|
+
target: opts.target
|
|
5291
|
+
})
|
|
5292
|
+
);
|
|
5130
5293
|
});
|
|
5131
5294
|
program.command("seed [vault]").description("populate a vault with example content").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5132
5295
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5133
5296
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5134
|
-
else
|
|
5297
|
+
else return emitGuardedVaultWrite(
|
|
5298
|
+
v.vault,
|
|
5299
|
+
"seed",
|
|
5300
|
+
() => runSeed({ vault: v.vault })
|
|
5301
|
+
);
|
|
5135
5302
|
});
|
|
5136
5303
|
program.command("observe [vault]").description("create a raw transcript observation entry").requiredOption("--text <text>", "observation text").option("--kind <kind>", "observation kind (note|bug|task|idea|session-log)", "task").option("--project <slug>", "associated project slug (required for task/bug claim detection)").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5137
5304
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5138
5305
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5139
|
-
else
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5306
|
+
else return emitGuardedVaultWrite(
|
|
5307
|
+
v.vault,
|
|
5308
|
+
"observe",
|
|
5309
|
+
() => runObserve({
|
|
5310
|
+
vault: v.vault,
|
|
5311
|
+
text: opts.text,
|
|
5312
|
+
kind: opts.kind,
|
|
5313
|
+
project: opts.project
|
|
5314
|
+
})
|
|
5315
|
+
);
|
|
5145
5316
|
});
|
|
5146
5317
|
program.command("session-brief [vault]").description("render or refresh the bounded startup session brief").option("--project <slug>", "project slug, or auto for deterministic detection", "auto").option("--write", "write meta/latest-session-brief.md and local cache files", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5147
5318
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5148
5319
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5320
|
+
else if (opts.write) return emitGuardedVaultWrite(
|
|
5321
|
+
v.vault,
|
|
5322
|
+
"session-brief --write",
|
|
5323
|
+
() => runSessionBrief({
|
|
5324
|
+
vault: v.vault,
|
|
5325
|
+
project: opts.project,
|
|
5326
|
+
write: true,
|
|
5327
|
+
cwd: process.cwd(),
|
|
5328
|
+
env: { SKILLWIKI_PROJECT: process.env.SKILLWIKI_PROJECT }
|
|
5329
|
+
}),
|
|
5330
|
+
{ postCommit: true }
|
|
5331
|
+
);
|
|
5149
5332
|
else emit(await runSessionBrief({
|
|
5150
5333
|
vault: v.vault,
|
|
5151
5334
|
project: opts.project,
|
|
5152
|
-
write:
|
|
5335
|
+
write: false,
|
|
5153
5336
|
cwd: process.cwd(),
|
|
5154
5337
|
env: { SKILLWIKI_PROJECT: process.env.SKILLWIKI_PROJECT }
|
|
5155
|
-
}), v.vault, { postCommit:
|
|
5338
|
+
}), v.vault, { postCommit: false });
|
|
5156
5339
|
});
|
|
5157
5340
|
var memoryCmd = program.command("memory").description("inspect derived agent memory caches");
|
|
5158
5341
|
memoryCmd.command("topics [vault]").description("list topic-oriented memory from the optional derived cache").option("--project <slug>", "filter topics by project slug").option("--limit <n>", "maximum topics to return", (s) => parseInt(s, 10), 10).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
@@ -5167,12 +5350,23 @@ memoryCmd.command("topics [vault]").description("list topic-oriented memory from
|
|
|
5167
5350
|
memoryCmd.command("index [vault]").description("build the derived project memory topic cache").requiredOption("--project <slug>", "project slug").option("--check", "report whether the local project memory cache is missing or stale without writing", false).option("--if-stale", "rebuild the local project memory cache only when it is missing or stale", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5168
5351
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5169
5352
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5170
|
-
else emit(await runMemoryIndex({
|
|
5353
|
+
else if (opts.check) emit(await runMemoryIndex({
|
|
5171
5354
|
vault: v.vault,
|
|
5172
5355
|
project: opts.project,
|
|
5173
|
-
check:
|
|
5356
|
+
check: true,
|
|
5174
5357
|
ifStale: !!opts.ifStale
|
|
5175
|
-
}), v.vault, { postCommit:
|
|
5358
|
+
}), v.vault, { postCommit: false });
|
|
5359
|
+
else return emitGuardedVaultWrite(
|
|
5360
|
+
v.vault,
|
|
5361
|
+
opts.ifStale ? "memory index --if-stale" : "memory index",
|
|
5362
|
+
() => runMemoryIndex({
|
|
5363
|
+
vault: v.vault,
|
|
5364
|
+
project: opts.project,
|
|
5365
|
+
check: false,
|
|
5366
|
+
ifStale: !!opts.ifStale
|
|
5367
|
+
}),
|
|
5368
|
+
{ postCommit: true }
|
|
5369
|
+
);
|
|
5176
5370
|
});
|
|
5177
5371
|
memoryCmd.command("recall [vault]").description("recall bounded source summaries for a memory topic").requiredOption("--project <slug>", "project slug").requiredOption("--topic <slug>", "memory topic slug").option("--scope <scope>", "memory scope: project, global, cross-agent, or all").option("--limit <n>", "maximum sources to return", (s) => parseInt(s, 10), 10).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5178
5372
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
@@ -5198,25 +5392,53 @@ memoryCmd.command("review [vault]").description("review deterministic memory gap
|
|
|
5198
5392
|
memoryCmd.command("import [vault]").description("preview or apply local memory imports into raw captures").requiredOption("--from <path>", "source file or directory to scan").requiredOption("--project <slug>", "project slug").option("--dry-run", "preview only; do not write captures", false).option("--apply", "write validated raw captures for ready entries", false).option("--max-bytes <n>", "reject source files above this size", (s) => parseInt(s, 10), 2e5).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
5199
5393
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
5200
5394
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
5395
|
+
else if (!!opts.apply && !opts.dryRun) return emitGuardedVaultWrite(
|
|
5396
|
+
v.vault,
|
|
5397
|
+
"memory import --apply",
|
|
5398
|
+
() => runMemoryImport({
|
|
5399
|
+
vault: v.vault,
|
|
5400
|
+
from: opts.from,
|
|
5401
|
+
project: opts.project,
|
|
5402
|
+
apply: true,
|
|
5403
|
+
maxBytes: opts.maxBytes
|
|
5404
|
+
}),
|
|
5405
|
+
{ postCommit: true }
|
|
5406
|
+
);
|
|
5201
5407
|
else emit(await runMemoryImport({
|
|
5202
5408
|
vault: v.vault,
|
|
5203
5409
|
from: opts.from,
|
|
5204
5410
|
project: opts.project,
|
|
5205
|
-
apply:
|
|
5411
|
+
apply: false,
|
|
5206
5412
|
maxBytes: opts.maxBytes
|
|
5207
|
-
}), v.vault, { postCommit:
|
|
5413
|
+
}), v.vault, { postCommit: false });
|
|
5208
5414
|
});
|
|
5209
5415
|
program.command("ingest <source>").description("ingest a source URL or local file into the vault").requiredOption("--vault <path>", "vault root directory").requiredOption("--type <type>", "typed-knowledge type (entity|concept|comparison|query)").requiredOption("--title <title>", "page title").option("--tags <csv>", "comma-separated tags").option("--provenance <provenance>", "provenance (research|project)").option("--dry-run", "preview without writing files", false).action(async (source, opts) => {
|
|
5210
5416
|
const tags = typeof opts.tags === "string" ? opts.tags.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5417
|
+
if (opts.dryRun) {
|
|
5418
|
+
emit(await runIngest({
|
|
5419
|
+
source,
|
|
5420
|
+
vault: opts.vault,
|
|
5421
|
+
type: opts.type,
|
|
5422
|
+
title: opts.title,
|
|
5423
|
+
tags,
|
|
5424
|
+
provenance: opts.provenance,
|
|
5425
|
+
dryRun: true
|
|
5426
|
+
}), opts.vault);
|
|
5427
|
+
return;
|
|
5428
|
+
}
|
|
5429
|
+
return emitGuardedVaultWrite(
|
|
5430
|
+
opts.vault,
|
|
5431
|
+
"ingest",
|
|
5432
|
+
() => runIngest({
|
|
5433
|
+
source,
|
|
5434
|
+
vault: opts.vault,
|
|
5435
|
+
type: opts.type,
|
|
5436
|
+
title: opts.title,
|
|
5437
|
+
tags,
|
|
5438
|
+
provenance: opts.provenance,
|
|
5439
|
+
dryRun: false
|
|
5440
|
+
})
|
|
5441
|
+
);
|
|
5220
5442
|
});
|
|
5221
5443
|
var fleetCmd = program.command("fleet").description("manage fleet topology metadata");
|
|
5222
5444
|
fleetCmd.command("validate <file>").description("validate a fleet manifest").action(async (file) => {
|
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.35",
|
|
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": {
|