skillwiki 0.6.2-beta.1 → 0.7.0

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/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-TPS5XD2J.js";
9
9
 
10
10
  // src/cli.ts
11
- import { readFileSync as readFileSync11 } from "fs";
11
+ import { readFileSync as readFileSync12 } from "fs";
12
12
  import { join as join41 } from "path";
13
13
  import { Command as Command2 } from "commander";
14
14
 
@@ -3489,9 +3489,10 @@ async function runConfigPath(input) {
3489
3489
  }
3490
3490
 
3491
3491
  // src/commands/doctor.ts
3492
- import { existsSync as existsSync7, lstatSync, readlinkSync, readdirSync, statSync as statSync2 } from "fs";
3492
+ import { existsSync as existsSync7, lstatSync, readlinkSync, readdirSync, statSync as statSync2, readFileSync as readFileSync7 } from "fs";
3493
3493
  import { join as join24, resolve as resolve4 } from "path";
3494
3494
  import { execSync as execSync2 } from "child_process";
3495
+ import { platform as platform2 } from "os";
3495
3496
 
3496
3497
  // src/utils/auto-update.ts
3497
3498
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
@@ -4209,12 +4210,12 @@ function checkRcloneFlagAudit(resolvedPath) {
4209
4210
  }
4210
4211
  return check("pass", "rclone_flags", "rclone VFS flags", `PID ${pid}: all critical flags at safe values`);
4211
4212
  }
4212
- function checkRcloneVersion(resolvedPath) {
4213
- if (!resolvedPath) {
4213
+ function checkRcloneVersion(resolvedPath, vaultSyncInstalled) {
4214
+ if (!resolvedPath && !vaultSyncInstalled) {
4214
4215
  return check("pass", "rclone_version", "rclone version", "No vault path \u2014 check skipped");
4215
4216
  }
4216
- const fuse = detectFuseMount(resolvedPath);
4217
- if (!fuse) {
4217
+ const fuse = resolvedPath ? detectFuseMount(resolvedPath) : null;
4218
+ if (!fuse && !vaultSyncInstalled) {
4218
4219
  return check("pass", "rclone_version", "rclone version", "local disk \u2014 check skipped");
4219
4220
  }
4220
4221
  const ver = getRcloneVersion();
@@ -4326,6 +4327,301 @@ function checkVfsCacheHealth(resolvedPath) {
4326
4327
  `${stats.files} files, ${(stats.bytesUsed / 1024 / 1024).toFixed(1)}MB \u2014 clean (0 errored, 0 pending)`
4327
4328
  );
4328
4329
  }
4330
+ function readVaultSyncConfig(home) {
4331
+ try {
4332
+ const content = readFileSync7(join24(home, ".skillwiki", ".env"), "utf8");
4333
+ let installed = false;
4334
+ let role;
4335
+ for (const line of content.split(/\r?\n/)) {
4336
+ const trimmed = line.trim();
4337
+ if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
4338
+ const eq = trimmed.indexOf("=");
4339
+ if (eq <= 0) continue;
4340
+ const k = trimmed.slice(0, eq).trim();
4341
+ const v = trimmed.slice(eq + 1).trim();
4342
+ if (v.length === 0) continue;
4343
+ if (k === "vault_sync.installed" && v === "true") installed = true;
4344
+ if (k === "vault_sync.role") role = v;
4345
+ }
4346
+ return { installed, role };
4347
+ } catch {
4348
+ return { installed: false };
4349
+ }
4350
+ }
4351
+ function vaultSyncChecks(input) {
4352
+ const os = input.os ?? platform2();
4353
+ const home = input.home;
4354
+ if (!input.vaultSyncInstalled) {
4355
+ const skip = (id, label) => check("pass", id, label, "vault-sync not installed \u2014 check skipped");
4356
+ return [
4357
+ skip("vault_sync_installed", "Vault sync installed"),
4358
+ skip("vault_sync_jobs_enabled", "Vault sync jobs enabled"),
4359
+ skip("vault_sync_last_push_age", "Vault sync last push recency"),
4360
+ skip("vault_sync_last_fetch_status", "Vault sync last fetch status"),
4361
+ skip("vault_sync_filter_present", "Vault sync filter file present"),
4362
+ skip("vault_sync_snapshot_guard", "Snapshot script guard")
4363
+ ];
4364
+ }
4365
+ const isMac = os === "darwin";
4366
+ const logDir = input.logDir ?? (isMac ? join24(home, "Library", "Logs") : join24(home, ".local", "state", "vault-sync", "log"));
4367
+ const shareDir = input.shareDir ?? (isMac ? join24(home, "Library", "Application Support", "vault-sync", "bin") : join24(home, ".local", "share", "vault-sync", "bin"));
4368
+ const filterPath = input.filterPath ?? join24(home, ".config", "rclone", "wiki-push-filters.txt");
4369
+ const snapshotPath = input.snapshotScriptPath ?? "/root/.hermes/scripts/wiki-snapshot-v3.sh";
4370
+ const pushScriptPath = join24(shareDir, "wiki-push.sh");
4371
+ const c1 = existsSync7(pushScriptPath) ? check("pass", "vault_sync_installed", "Vault sync installed", `Found: ${pushScriptPath}`) : check("error", "vault_sync_installed", "Vault sync installed", `Script not found at ${pushScriptPath} \u2014 run vault-sync-install`);
4372
+ let c2;
4373
+ try {
4374
+ if (isMac) {
4375
+ const uidStr = execSync2("id -u", {
4376
+ encoding: "utf8",
4377
+ timeout: 2e3,
4378
+ stdio: ["pipe", "pipe", "pipe"]
4379
+ }).trim();
4380
+ const uid = parseInt(uidStr, 10);
4381
+ execSync2(`launchctl print gui/${uid}/com.karlchow.wiki-push`, {
4382
+ encoding: "utf8",
4383
+ timeout: 2e3,
4384
+ stdio: ["pipe", "pipe", "pipe"]
4385
+ });
4386
+ c2 = check(
4387
+ "pass",
4388
+ "vault_sync_jobs_enabled",
4389
+ "Vault sync jobs enabled",
4390
+ "launchd: com.karlchow.wiki-push loaded"
4391
+ );
4392
+ } else {
4393
+ const out = execSync2("systemctl --user is-enabled wiki-push.timer", {
4394
+ encoding: "utf8",
4395
+ timeout: 2e3,
4396
+ stdio: ["pipe", "pipe", "pipe"]
4397
+ }).trim();
4398
+ if (out === "enabled") {
4399
+ c2 = check(
4400
+ "pass",
4401
+ "vault_sync_jobs_enabled",
4402
+ "Vault sync jobs enabled",
4403
+ "systemd: wiki-push.timer enabled"
4404
+ );
4405
+ } else {
4406
+ c2 = check(
4407
+ "error",
4408
+ "vault_sync_jobs_enabled",
4409
+ "Vault sync jobs enabled",
4410
+ `systemd: wiki-push.timer is ${out} \u2014 run vault-sync-install`
4411
+ );
4412
+ }
4413
+ }
4414
+ } catch {
4415
+ c2 = check(
4416
+ "error",
4417
+ "vault_sync_jobs_enabled",
4418
+ "Vault sync jobs enabled",
4419
+ "Scheduler check failed \u2014 run vault-sync-install"
4420
+ );
4421
+ }
4422
+ const logFile = join24(logDir, "wiki-push.log");
4423
+ let c3;
4424
+ try {
4425
+ const logContent = readFileSync7(logFile, "utf8");
4426
+ const lines = logContent.trim().split("\n").filter(Boolean);
4427
+ if (lines.length === 0) {
4428
+ c3 = check(
4429
+ "warn",
4430
+ "vault_sync_last_push_age",
4431
+ "Vault sync last push recency",
4432
+ "Log file is empty"
4433
+ );
4434
+ } else {
4435
+ const lastLine = lines[lines.length - 1];
4436
+ if (/FAIL/.test(lastLine)) {
4437
+ c3 = check(
4438
+ "error",
4439
+ "vault_sync_last_push_age",
4440
+ "Vault sync last push recency",
4441
+ `Last push failed: ${lastLine}`
4442
+ );
4443
+ } else if (/OK push/.test(lastLine)) {
4444
+ const tsMatch = lastLine.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/);
4445
+ if (tsMatch) {
4446
+ const lastPush = new Date(tsMatch[1]).getTime();
4447
+ const ageSec = (Date.now() - lastPush) / 1e3;
4448
+ if (ageSec <= 180) {
4449
+ c3 = check(
4450
+ "pass",
4451
+ "vault_sync_last_push_age",
4452
+ "Vault sync last push recency",
4453
+ `Last push ${ageSec.toFixed(0)}s ago`
4454
+ );
4455
+ } else {
4456
+ c3 = check(
4457
+ "warn",
4458
+ "vault_sync_last_push_age",
4459
+ "Vault sync last push recency",
4460
+ `Last push ${Math.round(ageSec)}s ago (>3 min)`
4461
+ );
4462
+ }
4463
+ } else {
4464
+ c3 = check(
4465
+ "warn",
4466
+ "vault_sync_last_push_age",
4467
+ "Vault sync last push recency",
4468
+ `Unparseable push line: ${lastLine.slice(0, 80)}`
4469
+ );
4470
+ }
4471
+ } else {
4472
+ c3 = check(
4473
+ "warn",
4474
+ "vault_sync_last_push_age",
4475
+ "Vault sync last push recency",
4476
+ `Last log entry: ${lastLine.slice(0, 80)}`
4477
+ );
4478
+ }
4479
+ }
4480
+ } catch {
4481
+ c3 = existsSync7(logDir) ? check(
4482
+ "warn",
4483
+ "vault_sync_last_push_age",
4484
+ "Vault sync last push recency",
4485
+ `Log file not found at ${logFile}`
4486
+ ) : check(
4487
+ "error",
4488
+ "vault_sync_last_push_age",
4489
+ "Vault sync last push recency",
4490
+ `Log directory not found at ${logDir}`
4491
+ );
4492
+ }
4493
+ const fetchLogFile = join24(logDir, "wiki-fetch.log");
4494
+ let cFetch;
4495
+ try {
4496
+ const logContent = readFileSync7(fetchLogFile, "utf8");
4497
+ const lines = logContent.trim().split("\n").filter(Boolean);
4498
+ if (lines.length === 0) {
4499
+ cFetch = check(
4500
+ "warn",
4501
+ "vault_sync_last_fetch_status",
4502
+ "Vault sync last fetch status",
4503
+ "Fetch log file is empty"
4504
+ );
4505
+ } else {
4506
+ const lastLine = lines[lines.length - 1];
4507
+ if (/fetch failed/i.test(lastLine)) {
4508
+ cFetch = check(
4509
+ "error",
4510
+ "vault_sync_last_fetch_status",
4511
+ "Vault sync last fetch status",
4512
+ `Last fetch failed: ${lastLine.slice(0, 100)}`
4513
+ );
4514
+ } else if (/OK/.test(lastLine)) {
4515
+ cFetch = check(
4516
+ "pass",
4517
+ "vault_sync_last_fetch_status",
4518
+ "Vault sync last fetch status",
4519
+ lastLine.slice(0, 100)
4520
+ );
4521
+ } else {
4522
+ cFetch = check(
4523
+ "warn",
4524
+ "vault_sync_last_fetch_status",
4525
+ "Vault sync last fetch status",
4526
+ `Last fetch log entry: ${lastLine.slice(0, 80)}`
4527
+ );
4528
+ }
4529
+ }
4530
+ } catch {
4531
+ cFetch = check(
4532
+ "warn",
4533
+ "vault_sync_last_fetch_status",
4534
+ "Vault sync last fetch status",
4535
+ `Fetch log not found at ${fetchLogFile}`
4536
+ );
4537
+ }
4538
+ let c4;
4539
+ try {
4540
+ if (!existsSync7(filterPath)) {
4541
+ c4 = check(
4542
+ "error",
4543
+ "vault_sync_filter_present",
4544
+ "Vault sync filter file present",
4545
+ `Filter file not found at ${filterPath}`
4546
+ );
4547
+ } else {
4548
+ const content = readFileSync7(filterPath, "utf8");
4549
+ const requiredExcludes = [
4550
+ "remotely-save/data.json",
4551
+ ".skillwiki/sync.lock",
4552
+ ".claude/settings.local.json"
4553
+ ];
4554
+ const missing = requiredExcludes.filter((ex) => !content.includes(ex));
4555
+ if (missing.length > 0) {
4556
+ c4 = check(
4557
+ "warn",
4558
+ "vault_sync_filter_present",
4559
+ "Vault sync filter file present",
4560
+ `Missing required excludes: ${missing.join(", ")}`
4561
+ );
4562
+ } else {
4563
+ c4 = check(
4564
+ "pass",
4565
+ "vault_sync_filter_present",
4566
+ "Vault sync filter file present",
4567
+ `Found with required excludes at ${filterPath}`
4568
+ );
4569
+ }
4570
+ }
4571
+ } catch {
4572
+ c4 = check(
4573
+ "error",
4574
+ "vault_sync_filter_present",
4575
+ "Vault sync filter file present",
4576
+ `Cannot read filter file at ${filterPath}`
4577
+ );
4578
+ }
4579
+ let c5;
4580
+ if (input.vaultSyncRole !== "snapshotter") {
4581
+ c5 = check(
4582
+ "pass",
4583
+ "vault_sync_snapshot_guard",
4584
+ "Snapshot script guard",
4585
+ "Not a snapshotter host \u2014 check skipped"
4586
+ );
4587
+ } else {
4588
+ try {
4589
+ if (!existsSync7(snapshotPath)) {
4590
+ c5 = check(
4591
+ "error",
4592
+ "vault_sync_snapshot_guard",
4593
+ "Snapshot script guard",
4594
+ `Snapshot script not found at ${snapshotPath}`
4595
+ );
4596
+ } else {
4597
+ const content = readFileSync7(snapshotPath, "utf8");
4598
+ if (!content.includes("--max-delete")) {
4599
+ c5 = check(
4600
+ "error",
4601
+ "vault_sync_snapshot_guard",
4602
+ "Snapshot script guard",
4603
+ `${snapshotPath} is missing --max-delete guard \u2014 dangerous without it`
4604
+ );
4605
+ } else {
4606
+ c5 = check(
4607
+ "pass",
4608
+ "vault_sync_snapshot_guard",
4609
+ "Snapshot script guard",
4610
+ `--max-delete present in ${snapshotPath}`
4611
+ );
4612
+ }
4613
+ }
4614
+ } catch {
4615
+ c5 = check(
4616
+ "error",
4617
+ "vault_sync_snapshot_guard",
4618
+ "Snapshot script guard",
4619
+ `Cannot read ${snapshotPath}`
4620
+ );
4621
+ }
4622
+ }
4623
+ return [c1, c2, c3, cFetch, c4, c5];
4624
+ }
4329
4625
  function findSkillMd(dir) {
4330
4626
  const results = [];
4331
4627
  let entries;
@@ -4360,6 +4656,7 @@ function findSkillNames(dir) {
4360
4656
  }
4361
4657
  async function runDoctor(input) {
4362
4658
  const checks = [];
4659
+ const vsConfig = readVaultSyncConfig(input.home);
4363
4660
  checks.push(checkNodeVersion());
4364
4661
  checks.push(checkCliChannels(input.argv, input.home));
4365
4662
  checks.push(await checkConfigFile(input.home));
@@ -4380,13 +4677,18 @@ async function runDoctor(input) {
4380
4677
  checks.push(checkDotStoreClean(resolvedPath));
4381
4678
  checks.push(checkS3MountPerf(resolvedPath));
4382
4679
  checks.push(checkRcloneFlagAudit(resolvedPath));
4383
- checks.push(checkRcloneVersion(resolvedPath));
4680
+ checks.push(checkRcloneVersion(resolvedPath, vsConfig.installed));
4384
4681
  checks.push(checkWriteTest(resolvedPath));
4385
4682
  checks.push(checkVfsCacheHealth(resolvedPath));
4386
4683
  checks.push(checkSkillsInstalled(input.home, input.cwd));
4387
4684
  checks.push(checkDuplicateSkills(input.home));
4388
4685
  checks.push(checkNpmUpdate(input.home, input.currentVersion));
4389
4686
  checks.push(checkPluginVersionDrift(input.home, input.currentVersion));
4687
+ checks.push(...vaultSyncChecks({
4688
+ home: input.home,
4689
+ vaultSyncInstalled: vsConfig.installed,
4690
+ vaultSyncRole: vsConfig.role
4691
+ }));
4390
4692
  const summary = {
4391
4693
  pass: checks.filter((c) => c.status === "pass").length,
4392
4694
  info: checks.filter((c) => c.status === "info").length,
@@ -4909,7 +5211,7 @@ ${newBody}`;
4909
5211
 
4910
5212
  // src/commands/update.ts
4911
5213
  import { execSync as execSync3 } from "child_process";
4912
- import { readFileSync as readFileSync7 } from "fs";
5214
+ import { readFileSync as readFileSync8 } from "fs";
4913
5215
  import { join as join26 } from "path";
4914
5216
  function resolveGlobalSkillsRoot() {
4915
5217
  try {
@@ -4939,7 +5241,7 @@ async function refreshInstalledSkills(target) {
4939
5241
  }
4940
5242
  async function runUpdate(input) {
4941
5243
  const pkg2 = JSON.parse(
4942
- readFileSync7(new URL("../../package.json", import.meta.url), "utf8")
5244
+ readFileSync8(new URL("../../package.json", import.meta.url), "utf8")
4943
5245
  );
4944
5246
  const currentVersion = pkg2.version;
4945
5247
  const tag = input.distTag ?? "latest";
@@ -5013,12 +5315,12 @@ async function runUpdate(input) {
5013
5315
 
5014
5316
  // src/commands/self-update.ts
5015
5317
  import { execSync as execSync4 } from "child_process";
5016
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
5318
+ import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
5017
5319
  import { join as join27 } from "path";
5018
5320
  var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
5019
5321
  async function runSelfUpdate(input) {
5020
5322
  const currentVersion = JSON.parse(
5021
- readFileSync8(new URL("../../package.json", import.meta.url), "utf8")
5323
+ readFileSync9(new URL("../../package.json", import.meta.url), "utf8")
5022
5324
  ).version;
5023
5325
  const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
5024
5326
  const localPkgPath = join27(sourceRoot, "packages", "cli", "package.json");
@@ -5029,7 +5331,7 @@ async function runSelfUpdate(input) {
5029
5331
  if (hasLocalSource) {
5030
5332
  source = "local";
5031
5333
  try {
5032
- availableVersion = JSON.parse(readFileSync8(localPkgPath, "utf8")).version ?? null;
5334
+ availableVersion = JSON.parse(readFileSync9(localPkgPath, "utf8")).version ?? null;
5033
5335
  } catch {
5034
5336
  availableVersion = null;
5035
5337
  }
@@ -5087,7 +5389,7 @@ async function runSelfUpdate(input) {
5087
5389
  }
5088
5390
  const newVersion = (() => {
5089
5391
  try {
5090
- return JSON.parse(readFileSync8(localPkgPath, "utf8")).version ?? "unknown";
5392
+ return JSON.parse(readFileSync9(localPkgPath, "utf8")).version ?? "unknown";
5091
5393
  } catch {
5092
5394
  return "unknown";
5093
5395
  }
@@ -6127,7 +6429,7 @@ import { existsSync as existsSync12 } from "fs";
6127
6429
  import { join as join34 } from "path";
6128
6430
 
6129
6431
  // src/utils/sync-lock.ts
6130
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync9, renameSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
6432
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync10, renameSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
6131
6433
  import { join as join33 } from "path";
6132
6434
  import { createHash as createHash6 } from "crypto";
6133
6435
  function getSessionId() {
@@ -6142,7 +6444,7 @@ function readLock(vault) {
6142
6444
  const path = lockPath(vault);
6143
6445
  if (!existsSync11(path)) return null;
6144
6446
  try {
6145
- const raw = readFileSync9(path, "utf8");
6447
+ const raw = readFileSync10(path, "utf8");
6146
6448
  return JSON.parse(raw);
6147
6449
  } catch {
6148
6450
  return null;
@@ -6430,6 +6732,7 @@ async function runSyncPull(input) {
6430
6732
  let pulled = false;
6431
6733
  let conflicts = 0;
6432
6734
  let filesUpdated = 0;
6735
+ let autoResolved = 0;
6433
6736
  try {
6434
6737
  const pullOutput = gitStrict(vault, ["pull", "--rebase", "origin", "HEAD"]);
6435
6738
  pulled = true;
@@ -6438,25 +6741,71 @@ async function runSyncPull(input) {
6438
6741
  } catch (e) {
6439
6742
  const errString = String(e);
6440
6743
  if (errString.includes("conflict")) {
6441
- const porcelain = git(vault, ["diff", "--name-only", "--diff-filter=U"]);
6442
- conflicts = porcelain ? porcelain.split("\n").filter((l) => l.trim().length > 0).length : 0;
6744
+ let inConflict = true;
6745
+ while (inConflict) {
6746
+ const stoppedSha = git(vault, ["rev-parse", "--verify", "REBASE_HEAD"]);
6747
+ let commitMsg = "";
6748
+ if (stoppedSha) {
6749
+ commitMsg = git(vault, ["log", "--format=%s", "-1", stoppedSha]);
6750
+ }
6751
+ const isArchiveOrSnapshot = commitMsg.startsWith("archive: moved") || commitMsg.startsWith("Snapshot ");
6752
+ const conflictedFiles = git(vault, ["diff", "--name-only", "--diff-filter=U"]);
6753
+ const conflictedList = conflictedFiles ? conflictedFiles.split("\n").filter((l) => l.trim().length > 0) : [];
6754
+ if (conflictedList.length === 0) {
6755
+ try {
6756
+ gitStrict(vault, ["rebase", "--continue"]);
6757
+ inConflict = true;
6758
+ } catch {
6759
+ inConflict = false;
6760
+ }
6761
+ continue;
6762
+ }
6763
+ if (isArchiveOrSnapshot) {
6764
+ for (const f of conflictedList) {
6765
+ try {
6766
+ gitStrict(vault, ["checkout", "--ours", f]);
6767
+ gitStrict(vault, ["add", f]);
6768
+ } catch {
6769
+ }
6770
+ }
6771
+ autoResolved += conflictedList.length;
6772
+ try {
6773
+ gitStrict(vault, ["rebase", "--continue"]);
6774
+ } catch (continueErr) {
6775
+ continue;
6776
+ }
6777
+ } else {
6778
+ conflicts = conflictedList.length;
6779
+ return {
6780
+ exitCode: ExitCode.SYNC_PULL_FAILED,
6781
+ result: ok({
6782
+ fetched,
6783
+ pulled: false,
6784
+ files_updated: 0,
6785
+ conflicts,
6786
+ auto_resolved: 0,
6787
+ lint_errors: 0,
6788
+ lint_warnings: 0,
6789
+ humanHint: `pull failed with ${conflicts} conflict(s) on non-archive commit "${commitMsg}" \u2014 resolve manually`
6790
+ })
6791
+ };
6792
+ }
6793
+ }
6794
+ if (autoResolved > 0) {
6795
+ const diffOutput = git(vault, ["diff", "--stat", "HEAD@{1}..HEAD"]);
6796
+ if (diffOutput) {
6797
+ const fileMatch = diffOutput.match(/(\d+) file[s]? changed/);
6798
+ if (fileMatch) filesUpdated = parseInt(fileMatch[1], 10);
6799
+ }
6800
+ pulled = true;
6801
+ conflicts = 0;
6802
+ }
6803
+ } else {
6443
6804
  return {
6444
6805
  exitCode: ExitCode.SYNC_PULL_FAILED,
6445
- result: ok({
6446
- fetched,
6447
- pulled: false,
6448
- files_updated: 0,
6449
- conflicts,
6450
- lint_errors: 0,
6451
- lint_warnings: 0,
6452
- humanHint: `pull failed with ${conflicts} conflict(s) \u2014 resolve manually`
6453
- })
6806
+ result: err("GIT_PULL_FAILED", { message: errString })
6454
6807
  };
6455
6808
  }
6456
- return {
6457
- exitCode: ExitCode.SYNC_PULL_FAILED,
6458
- result: err("GIT_PULL_FAILED", { message: errString })
6459
- };
6460
6809
  }
6461
6810
  let lintErrors = 0;
6462
6811
  let lintWarnings = 0;
@@ -6468,6 +6817,7 @@ async function runSyncPull(input) {
6468
6817
  const hintParts = [];
6469
6818
  if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
6470
6819
  else hintParts.push("already up to date");
6820
+ if (autoResolved > 0) hintParts.push(`${autoResolved} conflict(s) auto-resolved`);
6471
6821
  if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
6472
6822
  if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
6473
6823
  const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
@@ -6478,6 +6828,7 @@ async function runSyncPull(input) {
6478
6828
  pulled,
6479
6829
  files_updated: filesUpdated,
6480
6830
  conflicts,
6831
+ auto_resolved: autoResolved,
6481
6832
  lint_errors: lintErrors,
6482
6833
  lint_warnings: lintWarnings,
6483
6834
  humanHint: hintParts.join(", ")
@@ -6598,7 +6949,7 @@ function runSyncUnlock(input) {
6598
6949
  }
6599
6950
 
6600
6951
  // src/commands/backup.ts
6601
- import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
6952
+ import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
6602
6953
  import { join as join35, relative as relative3, dirname as dirname11 } from "path";
6603
6954
  import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
6604
6955
 
@@ -6665,7 +7016,7 @@ async function runBackupSync(input) {
6665
7016
  continue;
6666
7017
  }
6667
7018
  try {
6668
- const body = readFileSync10(absPath);
7019
+ const body = readFileSync11(absPath);
6669
7020
  await client.send(new PutObjectCommand({ Bucket: input.bucket, Key: relPath, Body: body }));
6670
7021
  uploaded++;
6671
7022
  } catch {
@@ -7292,7 +7643,7 @@ async function postCommit(vault, exitCode) {
7292
7643
  }
7293
7644
 
7294
7645
  // src/cli.ts
7295
- var pkg = JSON.parse(readFileSync11(new URL("../package.json", import.meta.url), "utf8"));
7646
+ var pkg = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf8"));
7296
7647
  var program = new Command2();
7297
7648
  program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(pkg.version);
7298
7649
  program.option("--human", "render terminal-readable output instead of JSON");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.6.2-beta.1",
3
+ "version": "0.7.0",
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.6.2-beta.1",
3
+ "version": "0.7.0",
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.6.2-beta.1",
3
+ "version": "0.7.0",
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.6.2-beta.1",
3
+ "version": "0.7.0",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",