skillwiki 0.9.33 → 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
@@ -152,6 +153,26 @@ var CompoundSchema = z.object({
152
153
  promoted_to: wikilink.optional(),
153
154
  cssclasses: z.array(z.string()).optional()
154
155
  });
156
+ var sessionPinPath = z.string().regex(
157
+ /^(entities|concepts|comparisons|queries|meta)\/.+\.md$/,
158
+ "must reference a typed-knowledge markdown page"
159
+ );
160
+ var SessionPinSchema = z.object({
161
+ title: z.string().min(1),
162
+ path: sessionPinPath,
163
+ scope: z.enum(["global", "project"]),
164
+ project: wikilink.optional(),
165
+ summary: z.string().min(1).optional(),
166
+ updated: isoDate.optional()
167
+ }).superRefine((v, ctx) => {
168
+ if (v.scope === "project" && v.project === void 0) {
169
+ ctx.addIssue({
170
+ code: z.ZodIssueCode.custom,
171
+ path: ["project"],
172
+ message: "project is required when scope is project"
173
+ });
174
+ }
175
+ });
155
176
  var MetaSchema = z.object({
156
177
  title: z.string().min(1),
157
178
  aliases: z.array(z.string()).optional(),
@@ -164,9 +185,17 @@ var MetaSchema = z.object({
164
185
  provenance_projects: z.array(wikilink).optional(),
165
186
  generated_by: z.string().min(1).optional(),
166
187
  generated_at: z.string().datetime().optional(),
167
- generated_kind: z.enum(["session-brief"]).optional()
188
+ generated_kind: z.enum(["session-brief"]).optional(),
189
+ meta_kind: z.enum(["session-pins"]).optional(),
190
+ stale_ttl: z.number().int().positive().optional(),
191
+ pins: z.array(SessionPinSchema).optional()
168
192
  }).superRefine((v, ctx) => {
169
- if (v.generated_kind !== "session-brief" && (!v.provenance_projects || v.provenance_projects.length < 2)) {
193
+ const isGeneratedSessionBrief = v.generated_kind === "session-brief";
194
+ const isSessionPins = v.meta_kind === "session-pins";
195
+ if (isSessionPins && (!v.pins || v.pins.length === 0)) {
196
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["pins"], message: "required when meta_kind is session-pins" });
197
+ }
198
+ if (!isGeneratedSessionBrief && !isSessionPins && (!v.provenance_projects || v.provenance_projects.length < 2)) {
170
199
  ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["provenance_projects"], message: "meta pages must reference \u22652 projects" });
171
200
  }
172
201
  if (v.provenance && v.provenance !== "research" && (!v.provenance_projects || v.provenance_projects.length === 0)) {
@@ -4370,7 +4399,7 @@ function formatKnownContext(input) {
4370
4399
  const selfAliases = collectSelfAliases(input.manifest, input.hostId);
4371
4400
  const outbound = collectOutboundAccess(input.manifest, input.hostId);
4372
4401
  const maintenanceLines = formatMaintenanceLines(host);
4373
- 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.`;
4374
4403
  return [
4375
4404
  "## Runtime Host Context",
4376
4405
  "",
@@ -8848,6 +8877,7 @@ export {
8848
8877
  ok,
8849
8878
  err,
8850
8879
  TypedKnowledgeSchema,
8880
+ MetaSchema,
8851
8881
  detectSchema,
8852
8882
  isBlockedHost,
8853
8883
  splitFrontmatter,
@@ -8899,6 +8929,7 @@ export {
8899
8929
  FLEET_REL_PATH,
8900
8930
  runFleetValidate,
8901
8931
  runFleetContext,
8932
+ loadFleetManifestAndHost,
8902
8933
  loadFleetManifest,
8903
8934
  resolveFleetHostId,
8904
8935
  SATELLITE_STALE_MS,
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  ExitCode,
4
4
  FLEET_REL_PATH,
5
+ MetaSchema,
5
6
  SATELLITE_STALE_MS,
6
7
  TypedKnowledgeSchema,
7
8
  appendLastOp,
@@ -21,6 +22,7 @@ import {
21
22
  isFailedRunStatus,
22
23
  isValidRemoteDeleteCap,
23
24
  loadFleetManifest,
25
+ loadFleetManifestAndHost,
24
26
  normalizeRemoteRoot,
25
27
  ok,
26
28
  parseDotenvFile,
@@ -73,7 +75,7 @@ import {
73
75
  triggerAutoUpdate,
74
76
  writeCache,
75
77
  writeDotenv
76
- } from "./chunk-OPXMAS4O.js";
78
+ } from "./chunk-TK4ZIQNE.js";
77
79
  import {
78
80
  normalizeDistTag
79
81
  } from "./chunk-E6UWZ3S3.js";
@@ -2386,14 +2388,27 @@ async function runSessionBrief(input) {
2386
2388
  const today = generatedAt.slice(0, 10);
2387
2389
  const project = await resolveProject(input);
2388
2390
  try {
2389
- const transcripts = await loadTranscriptInfo(scan.data.raw);
2390
- const workItems = await loadWorkItems(scan.data.workItems);
2391
- const digests = await loadTrendDigests(scan.data.typedKnowledge);
2391
+ const [
2392
+ transcripts,
2393
+ workItems,
2394
+ digests,
2395
+ baseHealthWarnings,
2396
+ satelliteHealth,
2397
+ sessionPins,
2398
+ memoryTopics
2399
+ ] = await Promise.all([
2400
+ loadTranscriptInfo(scan.data.raw),
2401
+ loadWorkItems(scan.data.workItems),
2402
+ loadTrendDigests(scan.data.typedKnowledge),
2403
+ loadHealthWarnings(input.vault),
2404
+ loadSatelliteHealth(input.vault),
2405
+ loadSessionPins(input.vault, project),
2406
+ project ? loadMemoryTopics(input.vault, project) : Promise.resolve([])
2407
+ ]);
2392
2408
  const healthWarnings = [
2393
- ...await loadHealthWarnings(input.vault),
2394
- ...satelliteHealthWarnings(await loadSatelliteHealth(input.vault))
2409
+ ...baseHealthWarnings,
2410
+ ...satelliteHealthWarnings(satelliteHealth)
2395
2411
  ];
2396
- const memoryTopics = project ? await loadMemoryTopics(input.vault, project) : [];
2397
2412
  const latestLogs = newest(transcripts.filter((t) => t.kind === "session-log"), 3);
2398
2413
  const unclaimedCaptures = newest(transcripts.filter((t) => {
2399
2414
  if (t.kind !== "task" && t.kind !== "bug") return false;
@@ -2408,6 +2423,7 @@ async function runSessionBrief(input) {
2408
2423
  const brief = capWords(renderBrief({
2409
2424
  project,
2410
2425
  generatedAt,
2426
+ sessionPins,
2411
2427
  latestLogs,
2412
2428
  unclaimedCaptures,
2413
2429
  activeWork,
@@ -2443,6 +2459,7 @@ async function runSessionBrief(input) {
2443
2459
  index_updated: indexUpdated,
2444
2460
  log_updated: logUpdated,
2445
2461
  generated_at: generatedAt,
2462
+ session_pins: sessionPins,
2446
2463
  memory_topics: memoryTopics,
2447
2464
  humanHint
2448
2465
  })
@@ -2544,6 +2561,29 @@ async function loadTrendDigests(typedPages) {
2544
2561
  }
2545
2562
  return out;
2546
2563
  }
2564
+ async function loadSessionPins(vault, project) {
2565
+ const text = await readIfExists(join15(vault, "meta", "session-pins.md"));
2566
+ if (!text) return [];
2567
+ const fm = extractFrontmatter(text);
2568
+ if (!fm.ok) return [];
2569
+ const parsed = MetaSchema.safeParse(fm.data);
2570
+ if (!parsed.success || parsed.data.meta_kind !== "session-pins") return [];
2571
+ const registryUpdated = parsed.data.updated;
2572
+ const out = [];
2573
+ for (const pin of parsed.data.pins ?? []) {
2574
+ const pinProject = wikilinkSlug(pin.project);
2575
+ if (pin.scope === "project" && (!project || pinProject !== project)) continue;
2576
+ out.push({
2577
+ path: pin.path,
2578
+ title: pin.title,
2579
+ summary: pin.summary ?? "",
2580
+ date: pin.updated ?? registryUpdated,
2581
+ project: pinProject,
2582
+ kind: "session-pin"
2583
+ });
2584
+ }
2585
+ return newest(out, 5);
2586
+ }
2547
2587
  function renderBrief(input) {
2548
2588
  const lines = [
2549
2589
  "# Session Brief",
@@ -2552,6 +2592,7 @@ function renderBrief(input) {
2552
2592
  `Scope: ${input.project ? `[[${input.project}]] plus global context` : "global context"}`,
2553
2593
  ""
2554
2594
  ];
2595
+ appendPinnedContextSection(lines, input.sessionPins);
2555
2596
  appendSection(lines, "Active Work", input.activeWork, "No active project work found.");
2556
2597
  appendSection(lines, "Unclaimed Captures", input.unclaimedCaptures, "No unclaimed task or bug captures found.");
2557
2598
  appendSection(lines, "Recent Session Logs", input.project ? input.projectLogs : input.latestLogs, "No recent session logs found.");
@@ -2577,6 +2618,16 @@ function appendMemoryTopicsSection(lines, project, topics) {
2577
2618
  }
2578
2619
  lines.push("");
2579
2620
  }
2621
+ function appendPinnedContextSection(lines, pins) {
2622
+ if (pins.length === 0) return;
2623
+ lines.push("## Pinned Context", "");
2624
+ for (const pin of pins) {
2625
+ const date = pin.date ? `${pin.date} ` : "";
2626
+ const summary = pin.summary ? ` \u2014 ${pin.summary}` : "";
2627
+ lines.push(`- ${date}${pin.title} (${pin.path})${summary}`);
2628
+ }
2629
+ lines.push("");
2630
+ }
2580
2631
  function appendSection(lines, title, items, empty) {
2581
2632
  lines.push(`## ${title}`, "");
2582
2633
  if (items.length === 0) {
@@ -4704,6 +4755,36 @@ async function postCommit(vault, exitCode) {
4704
4755
  clearLastOp(vault);
4705
4756
  }
4706
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
+
4707
4788
  // src/cli.ts
4708
4789
  var pkg = readCliPackageJson();
4709
4790
  var program = new Command();
@@ -4715,6 +4796,21 @@ async function emit(r, vault, opts) {
4715
4796
  if (vault && opts?.postCommit !== false) await postCommit(vault, r.exitCode);
4716
4797
  process.exit(r.exitCode);
4717
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
+ }
4718
4814
  program.command("hash <file>").description("compute SHA-256 hash of a vault page body").action(async (file) => emit(await runHash({ file })));
4719
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 })));
4720
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) => {
@@ -4724,17 +4820,20 @@ program.command("validate <file>").description("validate vault page frontmatter
4724
4820
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4725
4821
  else vault = v.vault;
4726
4822
  }
4823
+ if (opts.apply && vault) {
4824
+ return emitGuardedVaultWrite(vault, "validate --apply", () => runValidate({ file, apply: true, vault }), void 0);
4825
+ }
4727
4826
  emit(await runValidate({ file, apply: !!opts.apply, vault }), vault);
4728
4827
  });
4729
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) => {
4730
4829
  const out = opts.out ?? join25(vault, ".skillwiki", "graph.json");
4731
- emit(await runGraphBuild({ vault, out }), vault);
4830
+ return emitGuardedVaultWrite(vault, "graph build", () => runGraphBuild({ vault, out }));
4732
4831
  });
4733
4832
  var canvasCmd = program.command("canvas").description("manage Obsidian canvas files");
4734
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) => {
4735
4834
  const v = await resolveVaultArg(vault, opts.wiki);
4736
4835
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4737
- else emit(await runCanvasGenerate({ vault: v.vault, graphPath: opts.graphPath }), v.vault);
4836
+ else return emitGuardedVaultWrite(v.vault, "canvas generate", () => runCanvasGenerate({ vault: v.vault, graphPath: opts.graphPath }));
4738
4837
  });
4739
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) => {
4740
4839
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -4836,12 +4935,21 @@ program.command("topic-map-check [vault]").description("check whether a topic ma
4836
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) => {
4837
4936
  const v = await resolveVaultArg(vault, opts.wiki);
4838
4937
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4839
- else emit(await runStale({ vault: v.vault, days: opts.days, archive: !!opts.archive, forceScan: !!opts.forceScan, project: opts.project }), v.vault);
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);
4840
4944
  });
4841
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) => {
4842
4946
  const v = await resolveVaultArg(vault, opts.wiki);
4843
4947
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4844
- else emit(await runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug }), v.vault);
4948
+ else return emitGuardedVaultWrite(
4949
+ v.vault,
4950
+ "claim",
4951
+ () => runClaim({ vault: v.vault, transcript, project: opts.project, slug: opts.slug })
4952
+ );
4845
4953
  });
4846
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) => {
4847
4955
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -4851,23 +4959,47 @@ program.command("pagesize [vault]").description("report page sizes and flag over
4851
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) => {
4852
4960
  const v = await resolveVaultArg(vault, opts.wiki);
4853
4961
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4854
- else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: !!opts.apply }), v.vault);
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);
4855
4968
  });
4856
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) => {
4857
4970
  const v = await resolveVaultArg(vault, opts.wiki);
4858
4971
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4859
- else emit(await runLogAppend({ vault: v.vault, content: opts.content }), v.vault);
4972
+ else return emitGuardedVaultWrite(
4973
+ v.vault,
4974
+ "log-append",
4975
+ () => runLogAppend({ vault: v.vault, content: opts.content })
4976
+ );
4860
4977
  });
4861
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) => {
4862
4979
  const v = await resolveVaultArg(vault, opts.wiki);
4863
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
+ );
4864
4996
  else if (opts.summary) emit(await runLint({
4865
4997
  vault: v.vault,
4866
4998
  source: vault ? "flag" : void 0,
4867
4999
  days: opts.days,
4868
5000
  lines: opts.lines,
4869
5001
  logThreshold: opts.logThreshold,
4870
- fix: opts.fix ?? false,
5002
+ fix: false,
4871
5003
  only: opts.only,
4872
5004
  summary: true,
4873
5005
  examplesLimit: opts.examples
@@ -4878,7 +5010,7 @@ program.command("lint [vault]").description("run all vault health checks").optio
4878
5010
  days: opts.days,
4879
5011
  lines: opts.lines,
4880
5012
  logThreshold: opts.logThreshold,
4881
- fix: opts.fix ?? false,
5013
+ fix: false,
4882
5014
  only: opts.only
4883
5015
  }), v.vault);
4884
5016
  });
@@ -4927,24 +5059,56 @@ program.command("status [vault]").description("output vault diagnostics").option
4927
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) => {
4928
5060
  const v = await resolveVaultArg(vault, opts.wiki);
4929
5061
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4930
- else emit(await runArchive({
5062
+ else if (opts.cascade && !opts.apply) emit(await runArchive({
4931
5063
  vault: v.vault,
4932
5064
  page,
4933
- cascade: !!opts.cascade,
4934
- apply: !!opts.apply,
5065
+ cascade: true,
5066
+ apply: false,
4935
5067
  remote: opts.remote,
4936
5068
  remoteDelete: !!opts.remoteDelete,
4937
5069
  maxRemoteDeletes: Number.parseInt(opts.maxRemoteDeletes, 10)
4938
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
+ );
4939
5084
  });
4940
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) => {
4941
5086
  const v = await resolveVaultArg(vault, opts.wiki);
4942
5087
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4943
- else emit(await runDrift({ vault: v.vault, apply: opts.apply, newSince: opts.new }), v.vault);
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);
4944
5094
  });
4945
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) => {
4946
5096
  const v = await resolveVaultArg(vault, opts.wiki);
4947
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
+ );
4948
5112
  else emit(await runDedup({
4949
5113
  vault: v.vault,
4950
5114
  apply: opts.apply,
@@ -4959,12 +5123,22 @@ program.command("dedup [vault]").description("detect duplicate raw sources by sh
4959
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) => {
4960
5124
  const v = await resolveVaultArg(vault, opts.wiki);
4961
5125
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4962
- else emit(await runMigrateCitations({ vault: v.vault, dryRun: !!opts.dryRun }), v.vault);
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
+ );
4963
5132
  });
4964
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) => {
4965
5134
  const v = await resolveVaultArg(vault, opts.wiki);
4966
5135
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4967
- else emit(await runFrontmatterFix({ vault: v.vault, dryRun: !!opts.dryRun }), v.vault);
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
+ );
4968
5142
  });
4969
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({
4970
5144
  home: process.env.HOME ?? "",
@@ -4983,13 +5157,23 @@ program.command("transcripts [vault]").description("list transcript files in raw
4983
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) => {
4984
5158
  const v = await resolveVaultArg(vault, opts.wiki);
4985
5159
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4986
- else emit(await runProjectIndex({ vault: v.vault, slug, apply: !!opts.apply }), v.vault);
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);
4987
5166
  });
4988
5167
  var compoundCmd = program.command("compound").description("manage project compound entries");
4989
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) => {
4990
5169
  const v = await resolveVaultArg(vault, opts.wiki);
4991
5170
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
4992
- else emit(await runCompound({ vault: v.vault, project: opts.project, dryRun: !!opts.dryRun }), v.vault);
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
+ );
4993
5177
  });
4994
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) => {
4995
5179
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -4999,12 +5183,21 @@ compoundCmd.command("list [vault]").description("list compound entries for a pro
4999
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) => {
5000
5184
  const v = await resolveVaultArg(vault, opts.wiki);
5001
5185
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5002
- else emit(await runCompoundDelete({ vault: v.vault, project: opts.project, entry }), v.vault);
5186
+ else return emitGuardedVaultWrite(
5187
+ v.vault,
5188
+ "compound delete",
5189
+ () => runCompoundDelete({ vault: v.vault, project: opts.project, entry })
5190
+ );
5003
5191
  });
5004
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) => {
5005
5193
  const v = await resolveVaultArg(vault, opts.wiki);
5006
5194
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5007
- else emit(await runTagSync({ vault: v.vault, dryRun: !!opts.dryRun }), v.vault);
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
+ );
5008
5201
  });
5009
5202
  var syncCmd = program.command("sync").description("manage vault sync");
5010
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) => {
@@ -5015,25 +5208,41 @@ syncCmd.command("status [vault]").description("check vault git sync status").opt
5015
5208
  syncCmd.command("push [vault]").description("lint, commit, and push vault changes to remote").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5016
5209
  const v = await resolveVaultArg(vault, opts.wiki);
5017
5210
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5018
- else emit(await runSyncPush({ vault: v.vault }));
5211
+ else return emitGuardedVaultWrite(
5212
+ v.vault,
5213
+ "sync push",
5214
+ () => runSyncPush({ vault: v.vault })
5215
+ );
5019
5216
  });
5020
5217
  syncCmd.command("pull [vault]").description("pull remote vault changes and lint").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5021
5218
  const v = await resolveVaultArg(vault, opts.wiki);
5022
5219
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5023
- else emit(await runSyncPull({ vault: v.vault }), v.vault);
5220
+ else return emitGuardedVaultWrite(
5221
+ v.vault,
5222
+ "sync pull",
5223
+ () => runSyncPull({ vault: v.vault })
5224
+ );
5024
5225
  });
5025
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) => {
5026
5227
  const v = await resolveVaultArg(vault, opts.wiki);
5027
5228
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5028
5229
  else {
5029
5230
  const ttl = parseInt(opts.ttlMinutes, 10) || 30;
5030
- emit(runSyncLock({ vault: v.vault, summary: opts.summary, ttlMinutes: ttl, force: !!opts.force, sessionId: getCliSessionId() }));
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
+ );
5031
5236
  }
5032
5237
  });
5033
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) => {
5034
5239
  const v = await resolveVaultArg(vault, opts.wiki);
5035
5240
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5036
- else emit(runSyncUnlock({ vault: v.vault, force: !!opts.force, sessionId: getCliSessionId() }));
5241
+ else return emitGuardedVaultWrite(
5242
+ v.vault,
5243
+ "sync unlock",
5244
+ async () => runSyncUnlock({ vault: v.vault, force: !!opts.force, sessionId: getCliSessionId() })
5245
+ );
5037
5246
  });
5038
5247
  syncCmd.command("peers [vault]").description("list active locks and recent wiki-sync stashes").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5039
5248
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -5068,41 +5277,65 @@ backupCmd.command("restore [vault]").description("restore vault from S3-compatib
5068
5277
  }
5069
5278
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp";
5070
5279
  const dotenv = await parseDotenvFile(configPath(home));
5071
- emit(await runBackupRestore({
5072
- vault: v.vault,
5073
- bucket: opts.bucket ?? dotenv["BACKUP_BUCKET"] ?? "",
5074
- endpoint: opts.endpoint ?? dotenv["BACKUP_ENDPOINT"] ?? "",
5075
- region: opts.region ?? dotenv["BACKUP_REGION"] ?? "us-east-1",
5076
- accessKeyId: dotenv["BACKUP_ACCESS_KEY_ID"] ?? "",
5077
- secretAccessKey: dotenv["BACKUP_SECRET_ACCESS_KEY"] ?? "",
5078
- target: opts.target
5079
- }), v.vault);
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
+ );
5080
5293
  });
5081
5294
  program.command("seed [vault]").description("populate a vault with example content").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
5082
5295
  const v = await resolveVaultArg(vault, opts.wiki);
5083
5296
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5084
- else emit(await runSeed({ vault: v.vault }), v.vault);
5297
+ else return emitGuardedVaultWrite(
5298
+ v.vault,
5299
+ "seed",
5300
+ () => runSeed({ vault: v.vault })
5301
+ );
5085
5302
  });
5086
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) => {
5087
5304
  const v = await resolveVaultArg(vault, opts.wiki);
5088
5305
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5089
- else emit(await runObserve({
5090
- vault: v.vault,
5091
- text: opts.text,
5092
- kind: opts.kind,
5093
- project: opts.project
5094
- }), v.vault);
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
+ );
5095
5316
  });
5096
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) => {
5097
5318
  const v = await resolveVaultArg(vault, opts.wiki);
5098
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
+ );
5099
5332
  else emit(await runSessionBrief({
5100
5333
  vault: v.vault,
5101
5334
  project: opts.project,
5102
- write: !!opts.write,
5335
+ write: false,
5103
5336
  cwd: process.cwd(),
5104
5337
  env: { SKILLWIKI_PROJECT: process.env.SKILLWIKI_PROJECT }
5105
- }), v.vault, { postCommit: !!opts.write });
5338
+ }), v.vault, { postCommit: false });
5106
5339
  });
5107
5340
  var memoryCmd = program.command("memory").description("inspect derived agent memory caches");
5108
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) => {
@@ -5117,12 +5350,23 @@ memoryCmd.command("topics [vault]").description("list topic-oriented memory from
5117
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) => {
5118
5351
  const v = await resolveVaultArg(vault, opts.wiki);
5119
5352
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
5120
- else emit(await runMemoryIndex({
5353
+ else if (opts.check) emit(await runMemoryIndex({
5121
5354
  vault: v.vault,
5122
5355
  project: opts.project,
5123
- check: !!opts.check,
5356
+ check: true,
5124
5357
  ifStale: !!opts.ifStale
5125
- }), v.vault, { postCommit: !opts.check });
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
+ );
5126
5370
  });
5127
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) => {
5128
5372
  const v = await resolveVaultArg(vault, opts.wiki);
@@ -5148,25 +5392,53 @@ memoryCmd.command("review [vault]").description("review deterministic memory gap
5148
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) => {
5149
5393
  const v = await resolveVaultArg(vault, opts.wiki);
5150
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
+ );
5151
5407
  else emit(await runMemoryImport({
5152
5408
  vault: v.vault,
5153
5409
  from: opts.from,
5154
5410
  project: opts.project,
5155
- apply: !!opts.apply && !opts.dryRun,
5411
+ apply: false,
5156
5412
  maxBytes: opts.maxBytes
5157
- }), v.vault, { postCommit: !!opts.apply && !opts.dryRun });
5413
+ }), v.vault, { postCommit: false });
5158
5414
  });
5159
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) => {
5160
5416
  const tags = typeof opts.tags === "string" ? opts.tags.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : [];
5161
- emit(await runIngest({
5162
- source,
5163
- vault: opts.vault,
5164
- type: opts.type,
5165
- title: opts.title,
5166
- tags,
5167
- provenance: opts.provenance,
5168
- dryRun: !!opts.dryRun
5169
- }), opts.vault);
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
+ );
5170
5442
  });
5171
5443
  var fleetCmd = program.command("fleet").description("manage fleet topology metadata");
5172
5444
  fleetCmd.command("validate <file>").description("validate a fleet manifest").action(async (file) => {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-OPXMAS4O.js";
4
+ } from "./chunk-TK4ZIQNE.js";
5
5
  import "./chunk-E6UWZ3S3.js";
6
6
 
7
7
  // src/mcp-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.33",
3
+ "version": "0.9.35",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.33",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.33",
3
+ "version": "0.9.35",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 18 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.9.33",
3
+ "version": "0.9.35",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",