skillwiki 0.6.2-beta.1 → 0.8.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
|
|
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
|
|
|
@@ -2364,7 +2364,7 @@ Chronological action log. Newest entries last. Skill writes append entries; lint
|
|
|
2364
2364
|
|
|
2365
2365
|
// src/commands/lint.ts
|
|
2366
2366
|
import { existsSync as existsSync3 } from "fs";
|
|
2367
|
-
import { readFile as readFile15 } from "fs/promises";
|
|
2367
|
+
import { readFile as readFile15, rename as rename5 } from "fs/promises";
|
|
2368
2368
|
import { join as join19 } from "path";
|
|
2369
2369
|
|
|
2370
2370
|
// src/commands/topic-map-check.ts
|
|
@@ -2607,6 +2607,60 @@ async function runRawBodyDedup(vault) {
|
|
|
2607
2607
|
};
|
|
2608
2608
|
}
|
|
2609
2609
|
|
|
2610
|
+
// src/commands/path-too-long.ts
|
|
2611
|
+
var MAX_PATH_LENGTH = 240;
|
|
2612
|
+
async function runPathTooLong(input) {
|
|
2613
|
+
const scan = await scanVault(input.vault);
|
|
2614
|
+
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
2615
|
+
const allPages = [...scan.data.typedKnowledge, ...scan.data.raw, ...scan.data.workItems, ...scan.data.compound];
|
|
2616
|
+
const violations = [];
|
|
2617
|
+
for (const page of allPages) {
|
|
2618
|
+
if (page.relPath.length > MAX_PATH_LENGTH) {
|
|
2619
|
+
violations.push({ relPath: page.relPath, length: page.relPath.length });
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
if (violations.length > 0) {
|
|
2623
|
+
return {
|
|
2624
|
+
exitCode: ExitCode.LINT_HAS_ERRORS,
|
|
2625
|
+
result: ok({
|
|
2626
|
+
violations,
|
|
2627
|
+
humanHint: violations.map((v) => `${v.relPath}: ${v.length} chars (max ${MAX_PATH_LENGTH})`).join("\n")
|
|
2628
|
+
})
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
return { exitCode: ExitCode.OK, result: ok({ violations, humanHint: "all paths within length limit" }) };
|
|
2632
|
+
}
|
|
2633
|
+
function truncateFilename(relPath, maxLength = MAX_PATH_LENGTH) {
|
|
2634
|
+
if (relPath.length <= maxLength) return relPath;
|
|
2635
|
+
const lastSlash = relPath.lastIndexOf("/");
|
|
2636
|
+
const dir = lastSlash >= 0 ? relPath.slice(0, lastSlash) : "";
|
|
2637
|
+
const filename = lastSlash >= 0 ? relPath.slice(lastSlash + 1) : relPath;
|
|
2638
|
+
const hash = computeShortHash(relPath);
|
|
2639
|
+
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
2640
|
+
const base = filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
2641
|
+
const suffix = `-${hash}${ext}`;
|
|
2642
|
+
const dirPrefix = dir ? dir + "/" : "";
|
|
2643
|
+
const maxPrefixLen = maxLength - dirPrefix.length - suffix.length;
|
|
2644
|
+
if (maxPrefixLen <= 0) {
|
|
2645
|
+
const fallback = dirPrefix + hash + ext;
|
|
2646
|
+
if (fallback.length > maxLength) {
|
|
2647
|
+
const dirBudget = maxLength - suffix.length;
|
|
2648
|
+
return dirPrefix.slice(0, Math.max(0, dirBudget)) + suffix;
|
|
2649
|
+
}
|
|
2650
|
+
return fallback;
|
|
2651
|
+
}
|
|
2652
|
+
const prefix = base.slice(0, maxPrefixLen).replace(/[-_\s]+$/, "");
|
|
2653
|
+
return dirPrefix + prefix + suffix;
|
|
2654
|
+
}
|
|
2655
|
+
function computeShortHash(input) {
|
|
2656
|
+
let hash = 2166136261;
|
|
2657
|
+
for (let i = 0; i < input.length; i++) {
|
|
2658
|
+
hash ^= input.charCodeAt(i);
|
|
2659
|
+
hash = Math.imul(hash, 16777619);
|
|
2660
|
+
}
|
|
2661
|
+
return (hash >>> 0).toString(16).padStart(8, "0").slice(0, 8);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2610
2664
|
// src/utils/cli-surface.ts
|
|
2611
2665
|
import { Command } from "commander";
|
|
2612
2666
|
function buildCliSurface() {
|
|
@@ -2779,7 +2833,7 @@ function extractSourceEntries(rawFm) {
|
|
|
2779
2833
|
}
|
|
2780
2834
|
return entries;
|
|
2781
2835
|
}
|
|
2782
|
-
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "broken_sources", "tag_not_in_taxonomy"];
|
|
2836
|
+
var ERROR_ORDER = ["broken_wikilinks", "invalid_frontmatter", "raw_dedup", "broken_sources", "tag_not_in_taxonomy", "path_too_long"];
|
|
2783
2837
|
var WARNING_ORDER = ["raw_body_duplicate", "raw_subdirectory_duplicate", "file_source_url", "index_incomplete", "index_link_format", "stale_page", "page_too_large", "log_rotate_needed", "orphans", "compound_refs", "legacy_citation_style", "orphaned_citations", "duplicate_frontmatter", "work_item_health", "orphaned_project_pages", "missing_overview", "missing_diagram"];
|
|
2784
2838
|
var INFO_ORDER = ["bridges", "page_structure", "topic_map_recommended", "frontmatter_wikilink", "wikilink_citation", "missing_tldr", "stale_sections", "cli_refs"];
|
|
2785
2839
|
async function runLint(input) {
|
|
@@ -2839,6 +2893,8 @@ async function runLint(input) {
|
|
|
2839
2893
|
}
|
|
2840
2894
|
const compoundRefs = await validateCompoundReferences(input.vault);
|
|
2841
2895
|
if (compoundRefs.ok && compoundRefs.data.length > 0) buckets.compound_refs = compoundRefs.data;
|
|
2896
|
+
const pathCheck = await runPathTooLong({ vault: input.vault });
|
|
2897
|
+
if (pathCheck.result.ok && pathCheck.result.data.violations.length > 0) buckets.path_too_long = pathCheck.result.data.violations;
|
|
2842
2898
|
const scan = await scanVault(input.vault);
|
|
2843
2899
|
const allPages = scan.ok ? [...scan.data.typedKnowledge, ...scan.data.raw, ...scan.data.workItems, ...scan.data.compound] : [];
|
|
2844
2900
|
const slugs = scan.ok ? buildSlugMap(allPages) : /* @__PURE__ */ new Map();
|
|
@@ -3353,6 +3409,45 @@ ${newBody}`;
|
|
|
3353
3409
|
else delete buckets.file_source_url;
|
|
3354
3410
|
}
|
|
3355
3411
|
}
|
|
3412
|
+
const pathViolations = buckets.path_too_long;
|
|
3413
|
+
if (input.fix && pathViolations && pathViolations.length > 0) {
|
|
3414
|
+
const pathFixed = [];
|
|
3415
|
+
for (const v of pathViolations) {
|
|
3416
|
+
try {
|
|
3417
|
+
const absPath = `${input.vault}/${v.relPath}`;
|
|
3418
|
+
const newRelPath = truncateFilename(v.relPath);
|
|
3419
|
+
const newAbsPath = `${input.vault}/${newRelPath}`;
|
|
3420
|
+
await rename5(absPath, newAbsPath);
|
|
3421
|
+
const oldPathEscaped = v.relPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3422
|
+
for (const page of allPages) {
|
|
3423
|
+
if (page.relPath === v.relPath) continue;
|
|
3424
|
+
const content = await readFile15(page.absPath, "utf8");
|
|
3425
|
+
if (!content.includes(v.relPath)) continue;
|
|
3426
|
+
let updated = content;
|
|
3427
|
+
const citationRe = new RegExp(`\\^\\[${oldPathEscaped}\\]`, "g");
|
|
3428
|
+
updated = updated.replace(citationRe, `^[${newRelPath}]`);
|
|
3429
|
+
const wikilinkRe = new RegExp(`\\[\\[${oldPathEscaped}(\\|[^\\]]*)?\\]\\]`, "g");
|
|
3430
|
+
updated = updated.replace(wikilinkRe, (_m, alias) => `[[${newRelPath}${alias ?? ""}]]`);
|
|
3431
|
+
if (updated !== content) {
|
|
3432
|
+
const w = await safeWritePage(page.absPath, updated);
|
|
3433
|
+
if (!w.ok) {
|
|
3434
|
+
unresolved.push(`${page.relPath} (rewire)`);
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
pathFixed.push(v.relPath);
|
|
3439
|
+
} catch {
|
|
3440
|
+
unresolved.push(v.relPath);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
fixed.push(...pathFixed);
|
|
3444
|
+
if (pathFixed.length > 0) {
|
|
3445
|
+
const fixedSet = new Set(pathFixed);
|
|
3446
|
+
const remaining = pathViolations.filter((v) => !fixedSet.has(v.relPath));
|
|
3447
|
+
if (remaining.length > 0) buckets.path_too_long = remaining;
|
|
3448
|
+
else delete buckets.path_too_long;
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3356
3451
|
}
|
|
3357
3452
|
const errorOut = ERROR_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
3358
3453
|
const warningOut = WARNING_ORDER.flatMap((k) => buckets[k] ? [{ kind: k, items: buckets[k] }] : []);
|
|
@@ -3489,9 +3584,10 @@ async function runConfigPath(input) {
|
|
|
3489
3584
|
}
|
|
3490
3585
|
|
|
3491
3586
|
// src/commands/doctor.ts
|
|
3492
|
-
import { existsSync as existsSync7, lstatSync, readlinkSync, readdirSync, statSync as statSync2 } from "fs";
|
|
3587
|
+
import { existsSync as existsSync7, lstatSync, readlinkSync, readdirSync, statSync as statSync2, readFileSync as readFileSync7 } from "fs";
|
|
3493
3588
|
import { join as join24, resolve as resolve4 } from "path";
|
|
3494
3589
|
import { execSync as execSync2 } from "child_process";
|
|
3590
|
+
import { platform as platform2 } from "os";
|
|
3495
3591
|
|
|
3496
3592
|
// src/utils/auto-update.ts
|
|
3497
3593
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -4209,12 +4305,12 @@ function checkRcloneFlagAudit(resolvedPath) {
|
|
|
4209
4305
|
}
|
|
4210
4306
|
return check("pass", "rclone_flags", "rclone VFS flags", `PID ${pid}: all critical flags at safe values`);
|
|
4211
4307
|
}
|
|
4212
|
-
function checkRcloneVersion(resolvedPath) {
|
|
4213
|
-
if (!resolvedPath) {
|
|
4308
|
+
function checkRcloneVersion(resolvedPath, vaultSyncInstalled) {
|
|
4309
|
+
if (!resolvedPath && !vaultSyncInstalled) {
|
|
4214
4310
|
return check("pass", "rclone_version", "rclone version", "No vault path \u2014 check skipped");
|
|
4215
4311
|
}
|
|
4216
|
-
const fuse = detectFuseMount(resolvedPath);
|
|
4217
|
-
if (!fuse) {
|
|
4312
|
+
const fuse = resolvedPath ? detectFuseMount(resolvedPath) : null;
|
|
4313
|
+
if (!fuse && !vaultSyncInstalled) {
|
|
4218
4314
|
return check("pass", "rclone_version", "rclone version", "local disk \u2014 check skipped");
|
|
4219
4315
|
}
|
|
4220
4316
|
const ver = getRcloneVersion();
|
|
@@ -4326,6 +4422,301 @@ function checkVfsCacheHealth(resolvedPath) {
|
|
|
4326
4422
|
`${stats.files} files, ${(stats.bytesUsed / 1024 / 1024).toFixed(1)}MB \u2014 clean (0 errored, 0 pending)`
|
|
4327
4423
|
);
|
|
4328
4424
|
}
|
|
4425
|
+
function readVaultSyncConfig(home) {
|
|
4426
|
+
try {
|
|
4427
|
+
const content = readFileSync7(join24(home, ".skillwiki", ".env"), "utf8");
|
|
4428
|
+
let installed = false;
|
|
4429
|
+
let role;
|
|
4430
|
+
for (const line of content.split(/\r?\n/)) {
|
|
4431
|
+
const trimmed = line.trim();
|
|
4432
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
4433
|
+
const eq = trimmed.indexOf("=");
|
|
4434
|
+
if (eq <= 0) continue;
|
|
4435
|
+
const k = trimmed.slice(0, eq).trim();
|
|
4436
|
+
const v = trimmed.slice(eq + 1).trim();
|
|
4437
|
+
if (v.length === 0) continue;
|
|
4438
|
+
if (k === "vault_sync.installed" && v === "true") installed = true;
|
|
4439
|
+
if (k === "vault_sync.role") role = v;
|
|
4440
|
+
}
|
|
4441
|
+
return { installed, role };
|
|
4442
|
+
} catch {
|
|
4443
|
+
return { installed: false };
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
function vaultSyncChecks(input) {
|
|
4447
|
+
const os = input.os ?? platform2();
|
|
4448
|
+
const home = input.home;
|
|
4449
|
+
if (!input.vaultSyncInstalled) {
|
|
4450
|
+
const skip = (id, label) => check("pass", id, label, "vault-sync not installed \u2014 check skipped");
|
|
4451
|
+
return [
|
|
4452
|
+
skip("vault_sync_installed", "Vault sync installed"),
|
|
4453
|
+
skip("vault_sync_jobs_enabled", "Vault sync jobs enabled"),
|
|
4454
|
+
skip("vault_sync_last_push_age", "Vault sync last push recency"),
|
|
4455
|
+
skip("vault_sync_last_fetch_status", "Vault sync last fetch status"),
|
|
4456
|
+
skip("vault_sync_filter_present", "Vault sync filter file present"),
|
|
4457
|
+
skip("vault_sync_snapshot_guard", "Snapshot script guard")
|
|
4458
|
+
];
|
|
4459
|
+
}
|
|
4460
|
+
const isMac = os === "darwin";
|
|
4461
|
+
const logDir = input.logDir ?? (isMac ? join24(home, "Library", "Logs") : join24(home, ".local", "state", "vault-sync", "log"));
|
|
4462
|
+
const shareDir = input.shareDir ?? (isMac ? join24(home, "Library", "Application Support", "vault-sync", "bin") : join24(home, ".local", "share", "vault-sync", "bin"));
|
|
4463
|
+
const filterPath = input.filterPath ?? join24(home, ".config", "rclone", "wiki-push-filters.txt");
|
|
4464
|
+
const snapshotPath = input.snapshotScriptPath ?? "/root/.hermes/scripts/wiki-snapshot-v3.sh";
|
|
4465
|
+
const pushScriptPath = join24(shareDir, "wiki-push.sh");
|
|
4466
|
+
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`);
|
|
4467
|
+
let c2;
|
|
4468
|
+
try {
|
|
4469
|
+
if (isMac) {
|
|
4470
|
+
const uidStr = execSync2("id -u", {
|
|
4471
|
+
encoding: "utf8",
|
|
4472
|
+
timeout: 2e3,
|
|
4473
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4474
|
+
}).trim();
|
|
4475
|
+
const uid = parseInt(uidStr, 10);
|
|
4476
|
+
execSync2(`launchctl print gui/${uid}/com.karlchow.wiki-push`, {
|
|
4477
|
+
encoding: "utf8",
|
|
4478
|
+
timeout: 2e3,
|
|
4479
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4480
|
+
});
|
|
4481
|
+
c2 = check(
|
|
4482
|
+
"pass",
|
|
4483
|
+
"vault_sync_jobs_enabled",
|
|
4484
|
+
"Vault sync jobs enabled",
|
|
4485
|
+
"launchd: com.karlchow.wiki-push loaded"
|
|
4486
|
+
);
|
|
4487
|
+
} else {
|
|
4488
|
+
const out = execSync2("systemctl --user is-enabled wiki-push.timer", {
|
|
4489
|
+
encoding: "utf8",
|
|
4490
|
+
timeout: 2e3,
|
|
4491
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4492
|
+
}).trim();
|
|
4493
|
+
if (out === "enabled") {
|
|
4494
|
+
c2 = check(
|
|
4495
|
+
"pass",
|
|
4496
|
+
"vault_sync_jobs_enabled",
|
|
4497
|
+
"Vault sync jobs enabled",
|
|
4498
|
+
"systemd: wiki-push.timer enabled"
|
|
4499
|
+
);
|
|
4500
|
+
} else {
|
|
4501
|
+
c2 = check(
|
|
4502
|
+
"error",
|
|
4503
|
+
"vault_sync_jobs_enabled",
|
|
4504
|
+
"Vault sync jobs enabled",
|
|
4505
|
+
`systemd: wiki-push.timer is ${out} \u2014 run vault-sync-install`
|
|
4506
|
+
);
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
} catch {
|
|
4510
|
+
c2 = check(
|
|
4511
|
+
"error",
|
|
4512
|
+
"vault_sync_jobs_enabled",
|
|
4513
|
+
"Vault sync jobs enabled",
|
|
4514
|
+
"Scheduler check failed \u2014 run vault-sync-install"
|
|
4515
|
+
);
|
|
4516
|
+
}
|
|
4517
|
+
const logFile = join24(logDir, "wiki-push.log");
|
|
4518
|
+
let c3;
|
|
4519
|
+
try {
|
|
4520
|
+
const logContent = readFileSync7(logFile, "utf8");
|
|
4521
|
+
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
4522
|
+
if (lines.length === 0) {
|
|
4523
|
+
c3 = check(
|
|
4524
|
+
"warn",
|
|
4525
|
+
"vault_sync_last_push_age",
|
|
4526
|
+
"Vault sync last push recency",
|
|
4527
|
+
"Log file is empty"
|
|
4528
|
+
);
|
|
4529
|
+
} else {
|
|
4530
|
+
const lastLine = lines[lines.length - 1];
|
|
4531
|
+
if (/FAIL/.test(lastLine)) {
|
|
4532
|
+
c3 = check(
|
|
4533
|
+
"error",
|
|
4534
|
+
"vault_sync_last_push_age",
|
|
4535
|
+
"Vault sync last push recency",
|
|
4536
|
+
`Last push failed: ${lastLine}`
|
|
4537
|
+
);
|
|
4538
|
+
} else if (/OK push/.test(lastLine)) {
|
|
4539
|
+
const tsMatch = lastLine.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/);
|
|
4540
|
+
if (tsMatch) {
|
|
4541
|
+
const lastPush = new Date(tsMatch[1]).getTime();
|
|
4542
|
+
const ageSec = (Date.now() - lastPush) / 1e3;
|
|
4543
|
+
if (ageSec <= 180) {
|
|
4544
|
+
c3 = check(
|
|
4545
|
+
"pass",
|
|
4546
|
+
"vault_sync_last_push_age",
|
|
4547
|
+
"Vault sync last push recency",
|
|
4548
|
+
`Last push ${ageSec.toFixed(0)}s ago`
|
|
4549
|
+
);
|
|
4550
|
+
} else {
|
|
4551
|
+
c3 = check(
|
|
4552
|
+
"warn",
|
|
4553
|
+
"vault_sync_last_push_age",
|
|
4554
|
+
"Vault sync last push recency",
|
|
4555
|
+
`Last push ${Math.round(ageSec)}s ago (>3 min)`
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
} else {
|
|
4559
|
+
c3 = check(
|
|
4560
|
+
"warn",
|
|
4561
|
+
"vault_sync_last_push_age",
|
|
4562
|
+
"Vault sync last push recency",
|
|
4563
|
+
`Unparseable push line: ${lastLine.slice(0, 80)}`
|
|
4564
|
+
);
|
|
4565
|
+
}
|
|
4566
|
+
} else {
|
|
4567
|
+
c3 = check(
|
|
4568
|
+
"warn",
|
|
4569
|
+
"vault_sync_last_push_age",
|
|
4570
|
+
"Vault sync last push recency",
|
|
4571
|
+
`Last log entry: ${lastLine.slice(0, 80)}`
|
|
4572
|
+
);
|
|
4573
|
+
}
|
|
4574
|
+
}
|
|
4575
|
+
} catch {
|
|
4576
|
+
c3 = existsSync7(logDir) ? check(
|
|
4577
|
+
"warn",
|
|
4578
|
+
"vault_sync_last_push_age",
|
|
4579
|
+
"Vault sync last push recency",
|
|
4580
|
+
`Log file not found at ${logFile}`
|
|
4581
|
+
) : check(
|
|
4582
|
+
"error",
|
|
4583
|
+
"vault_sync_last_push_age",
|
|
4584
|
+
"Vault sync last push recency",
|
|
4585
|
+
`Log directory not found at ${logDir}`
|
|
4586
|
+
);
|
|
4587
|
+
}
|
|
4588
|
+
const fetchLogFile = join24(logDir, "wiki-fetch.log");
|
|
4589
|
+
let cFetch;
|
|
4590
|
+
try {
|
|
4591
|
+
const logContent = readFileSync7(fetchLogFile, "utf8");
|
|
4592
|
+
const lines = logContent.trim().split("\n").filter(Boolean);
|
|
4593
|
+
if (lines.length === 0) {
|
|
4594
|
+
cFetch = check(
|
|
4595
|
+
"warn",
|
|
4596
|
+
"vault_sync_last_fetch_status",
|
|
4597
|
+
"Vault sync last fetch status",
|
|
4598
|
+
"Fetch log file is empty"
|
|
4599
|
+
);
|
|
4600
|
+
} else {
|
|
4601
|
+
const lastLine = lines[lines.length - 1];
|
|
4602
|
+
if (/fetch failed/i.test(lastLine)) {
|
|
4603
|
+
cFetch = check(
|
|
4604
|
+
"error",
|
|
4605
|
+
"vault_sync_last_fetch_status",
|
|
4606
|
+
"Vault sync last fetch status",
|
|
4607
|
+
`Last fetch failed: ${lastLine.slice(0, 100)}`
|
|
4608
|
+
);
|
|
4609
|
+
} else if (/OK/.test(lastLine)) {
|
|
4610
|
+
cFetch = check(
|
|
4611
|
+
"pass",
|
|
4612
|
+
"vault_sync_last_fetch_status",
|
|
4613
|
+
"Vault sync last fetch status",
|
|
4614
|
+
lastLine.slice(0, 100)
|
|
4615
|
+
);
|
|
4616
|
+
} else {
|
|
4617
|
+
cFetch = check(
|
|
4618
|
+
"warn",
|
|
4619
|
+
"vault_sync_last_fetch_status",
|
|
4620
|
+
"Vault sync last fetch status",
|
|
4621
|
+
`Last fetch log entry: ${lastLine.slice(0, 80)}`
|
|
4622
|
+
);
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
4625
|
+
} catch {
|
|
4626
|
+
cFetch = check(
|
|
4627
|
+
"warn",
|
|
4628
|
+
"vault_sync_last_fetch_status",
|
|
4629
|
+
"Vault sync last fetch status",
|
|
4630
|
+
`Fetch log not found at ${fetchLogFile}`
|
|
4631
|
+
);
|
|
4632
|
+
}
|
|
4633
|
+
let c4;
|
|
4634
|
+
try {
|
|
4635
|
+
if (!existsSync7(filterPath)) {
|
|
4636
|
+
c4 = check(
|
|
4637
|
+
"error",
|
|
4638
|
+
"vault_sync_filter_present",
|
|
4639
|
+
"Vault sync filter file present",
|
|
4640
|
+
`Filter file not found at ${filterPath}`
|
|
4641
|
+
);
|
|
4642
|
+
} else {
|
|
4643
|
+
const content = readFileSync7(filterPath, "utf8");
|
|
4644
|
+
const requiredExcludes = [
|
|
4645
|
+
"remotely-save/data.json",
|
|
4646
|
+
".skillwiki/sync.lock",
|
|
4647
|
+
".claude/settings.local.json"
|
|
4648
|
+
];
|
|
4649
|
+
const missing = requiredExcludes.filter((ex) => !content.includes(ex));
|
|
4650
|
+
if (missing.length > 0) {
|
|
4651
|
+
c4 = check(
|
|
4652
|
+
"warn",
|
|
4653
|
+
"vault_sync_filter_present",
|
|
4654
|
+
"Vault sync filter file present",
|
|
4655
|
+
`Missing required excludes: ${missing.join(", ")}`
|
|
4656
|
+
);
|
|
4657
|
+
} else {
|
|
4658
|
+
c4 = check(
|
|
4659
|
+
"pass",
|
|
4660
|
+
"vault_sync_filter_present",
|
|
4661
|
+
"Vault sync filter file present",
|
|
4662
|
+
`Found with required excludes at ${filterPath}`
|
|
4663
|
+
);
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
} catch {
|
|
4667
|
+
c4 = check(
|
|
4668
|
+
"error",
|
|
4669
|
+
"vault_sync_filter_present",
|
|
4670
|
+
"Vault sync filter file present",
|
|
4671
|
+
`Cannot read filter file at ${filterPath}`
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
let c5;
|
|
4675
|
+
if (input.vaultSyncRole !== "snapshotter") {
|
|
4676
|
+
c5 = check(
|
|
4677
|
+
"pass",
|
|
4678
|
+
"vault_sync_snapshot_guard",
|
|
4679
|
+
"Snapshot script guard",
|
|
4680
|
+
"Not a snapshotter host \u2014 check skipped"
|
|
4681
|
+
);
|
|
4682
|
+
} else {
|
|
4683
|
+
try {
|
|
4684
|
+
if (!existsSync7(snapshotPath)) {
|
|
4685
|
+
c5 = check(
|
|
4686
|
+
"error",
|
|
4687
|
+
"vault_sync_snapshot_guard",
|
|
4688
|
+
"Snapshot script guard",
|
|
4689
|
+
`Snapshot script not found at ${snapshotPath}`
|
|
4690
|
+
);
|
|
4691
|
+
} else {
|
|
4692
|
+
const content = readFileSync7(snapshotPath, "utf8");
|
|
4693
|
+
if (!content.includes("--max-delete")) {
|
|
4694
|
+
c5 = check(
|
|
4695
|
+
"error",
|
|
4696
|
+
"vault_sync_snapshot_guard",
|
|
4697
|
+
"Snapshot script guard",
|
|
4698
|
+
`${snapshotPath} is missing --max-delete guard \u2014 dangerous without it`
|
|
4699
|
+
);
|
|
4700
|
+
} else {
|
|
4701
|
+
c5 = check(
|
|
4702
|
+
"pass",
|
|
4703
|
+
"vault_sync_snapshot_guard",
|
|
4704
|
+
"Snapshot script guard",
|
|
4705
|
+
`--max-delete present in ${snapshotPath}`
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
} catch {
|
|
4710
|
+
c5 = check(
|
|
4711
|
+
"error",
|
|
4712
|
+
"vault_sync_snapshot_guard",
|
|
4713
|
+
"Snapshot script guard",
|
|
4714
|
+
`Cannot read ${snapshotPath}`
|
|
4715
|
+
);
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
return [c1, c2, c3, cFetch, c4, c5];
|
|
4719
|
+
}
|
|
4329
4720
|
function findSkillMd(dir) {
|
|
4330
4721
|
const results = [];
|
|
4331
4722
|
let entries;
|
|
@@ -4360,6 +4751,7 @@ function findSkillNames(dir) {
|
|
|
4360
4751
|
}
|
|
4361
4752
|
async function runDoctor(input) {
|
|
4362
4753
|
const checks = [];
|
|
4754
|
+
const vsConfig = readVaultSyncConfig(input.home);
|
|
4363
4755
|
checks.push(checkNodeVersion());
|
|
4364
4756
|
checks.push(checkCliChannels(input.argv, input.home));
|
|
4365
4757
|
checks.push(await checkConfigFile(input.home));
|
|
@@ -4380,13 +4772,18 @@ async function runDoctor(input) {
|
|
|
4380
4772
|
checks.push(checkDotStoreClean(resolvedPath));
|
|
4381
4773
|
checks.push(checkS3MountPerf(resolvedPath));
|
|
4382
4774
|
checks.push(checkRcloneFlagAudit(resolvedPath));
|
|
4383
|
-
checks.push(checkRcloneVersion(resolvedPath));
|
|
4775
|
+
checks.push(checkRcloneVersion(resolvedPath, vsConfig.installed));
|
|
4384
4776
|
checks.push(checkWriteTest(resolvedPath));
|
|
4385
4777
|
checks.push(checkVfsCacheHealth(resolvedPath));
|
|
4386
4778
|
checks.push(checkSkillsInstalled(input.home, input.cwd));
|
|
4387
4779
|
checks.push(checkDuplicateSkills(input.home));
|
|
4388
4780
|
checks.push(checkNpmUpdate(input.home, input.currentVersion));
|
|
4389
4781
|
checks.push(checkPluginVersionDrift(input.home, input.currentVersion));
|
|
4782
|
+
checks.push(...vaultSyncChecks({
|
|
4783
|
+
home: input.home,
|
|
4784
|
+
vaultSyncInstalled: vsConfig.installed,
|
|
4785
|
+
vaultSyncRole: vsConfig.role
|
|
4786
|
+
}));
|
|
4390
4787
|
const summary = {
|
|
4391
4788
|
pass: checks.filter((c) => c.status === "pass").length,
|
|
4392
4789
|
info: checks.filter((c) => c.status === "info").length,
|
|
@@ -4410,7 +4807,7 @@ async function runDoctor(input) {
|
|
|
4410
4807
|
}
|
|
4411
4808
|
|
|
4412
4809
|
// src/commands/archive.ts
|
|
4413
|
-
import { rename as
|
|
4810
|
+
import { rename as rename6, mkdir as mkdir8, readFile as readFile18, writeFile as writeFile9 } from "fs/promises";
|
|
4414
4811
|
import { join as join25, dirname as dirname9 } from "path";
|
|
4415
4812
|
function countWikilinks(body, slug) {
|
|
4416
4813
|
const escaped = slug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4523,7 +4920,7 @@ ${fmRewritten}
|
|
|
4523
4920
|
if (e instanceof Error && "code" in e && e.code !== "ENOENT") throw e;
|
|
4524
4921
|
}
|
|
4525
4922
|
}
|
|
4526
|
-
await
|
|
4923
|
+
await rename6(join25(input.vault, relPath), join25(input.vault, archivePath));
|
|
4527
4924
|
appendLastOp(input.vault, {
|
|
4528
4925
|
operation: input.cascade ? "archive-cascade" : "archive",
|
|
4529
4926
|
summary: `moved ${relPath} to ${archivePath}${input.cascade ? ` (cascade: ${cascade?.source_array_refs.length ?? 0} source arrays updated)` : ""}`,
|
|
@@ -4909,7 +5306,7 @@ ${newBody}`;
|
|
|
4909
5306
|
|
|
4910
5307
|
// src/commands/update.ts
|
|
4911
5308
|
import { execSync as execSync3 } from "child_process";
|
|
4912
|
-
import { readFileSync as
|
|
5309
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
4913
5310
|
import { join as join26 } from "path";
|
|
4914
5311
|
function resolveGlobalSkillsRoot() {
|
|
4915
5312
|
try {
|
|
@@ -4939,7 +5336,7 @@ async function refreshInstalledSkills(target) {
|
|
|
4939
5336
|
}
|
|
4940
5337
|
async function runUpdate(input) {
|
|
4941
5338
|
const pkg2 = JSON.parse(
|
|
4942
|
-
|
|
5339
|
+
readFileSync8(new URL("../../package.json", import.meta.url), "utf8")
|
|
4943
5340
|
);
|
|
4944
5341
|
const currentVersion = pkg2.version;
|
|
4945
5342
|
const tag = input.distTag ?? "latest";
|
|
@@ -5013,12 +5410,12 @@ async function runUpdate(input) {
|
|
|
5013
5410
|
|
|
5014
5411
|
// src/commands/self-update.ts
|
|
5015
5412
|
import { execSync as execSync4 } from "child_process";
|
|
5016
|
-
import { existsSync as existsSync8, readFileSync as
|
|
5413
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
|
|
5017
5414
|
import { join as join27 } from "path";
|
|
5018
5415
|
var DEFAULT_SOURCE_ROOT_SUFFIX = "/Desktop/code/llm-wiki";
|
|
5019
5416
|
async function runSelfUpdate(input) {
|
|
5020
5417
|
const currentVersion = JSON.parse(
|
|
5021
|
-
|
|
5418
|
+
readFileSync9(new URL("../../package.json", import.meta.url), "utf8")
|
|
5022
5419
|
).version;
|
|
5023
5420
|
const sourceRoot = input.sourceRoot ?? `${input.home}${DEFAULT_SOURCE_ROOT_SUFFIX}`;
|
|
5024
5421
|
const localPkgPath = join27(sourceRoot, "packages", "cli", "package.json");
|
|
@@ -5029,7 +5426,7 @@ async function runSelfUpdate(input) {
|
|
|
5029
5426
|
if (hasLocalSource) {
|
|
5030
5427
|
source = "local";
|
|
5031
5428
|
try {
|
|
5032
|
-
availableVersion = JSON.parse(
|
|
5429
|
+
availableVersion = JSON.parse(readFileSync9(localPkgPath, "utf8")).version ?? null;
|
|
5033
5430
|
} catch {
|
|
5034
5431
|
availableVersion = null;
|
|
5035
5432
|
}
|
|
@@ -5087,7 +5484,7 @@ async function runSelfUpdate(input) {
|
|
|
5087
5484
|
}
|
|
5088
5485
|
const newVersion = (() => {
|
|
5089
5486
|
try {
|
|
5090
|
-
return JSON.parse(
|
|
5487
|
+
return JSON.parse(readFileSync9(localPkgPath, "utf8")).version ?? "unknown";
|
|
5091
5488
|
} catch {
|
|
5092
5489
|
return "unknown";
|
|
5093
5490
|
}
|
|
@@ -6127,7 +6524,7 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
6127
6524
|
import { join as join34 } from "path";
|
|
6128
6525
|
|
|
6129
6526
|
// src/utils/sync-lock.ts
|
|
6130
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as
|
|
6527
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync10, renameSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
6131
6528
|
import { join as join33 } from "path";
|
|
6132
6529
|
import { createHash as createHash6 } from "crypto";
|
|
6133
6530
|
function getSessionId() {
|
|
@@ -6142,7 +6539,7 @@ function readLock(vault) {
|
|
|
6142
6539
|
const path = lockPath(vault);
|
|
6143
6540
|
if (!existsSync11(path)) return null;
|
|
6144
6541
|
try {
|
|
6145
|
-
const raw =
|
|
6542
|
+
const raw = readFileSync10(path, "utf8");
|
|
6146
6543
|
return JSON.parse(raw);
|
|
6147
6544
|
} catch {
|
|
6148
6545
|
return null;
|
|
@@ -6430,6 +6827,7 @@ async function runSyncPull(input) {
|
|
|
6430
6827
|
let pulled = false;
|
|
6431
6828
|
let conflicts = 0;
|
|
6432
6829
|
let filesUpdated = 0;
|
|
6830
|
+
let autoResolved = 0;
|
|
6433
6831
|
try {
|
|
6434
6832
|
const pullOutput = gitStrict(vault, ["pull", "--rebase", "origin", "HEAD"]);
|
|
6435
6833
|
pulled = true;
|
|
@@ -6438,25 +6836,71 @@ async function runSyncPull(input) {
|
|
|
6438
6836
|
} catch (e) {
|
|
6439
6837
|
const errString = String(e);
|
|
6440
6838
|
if (errString.includes("conflict")) {
|
|
6441
|
-
|
|
6442
|
-
|
|
6839
|
+
let inConflict = true;
|
|
6840
|
+
while (inConflict) {
|
|
6841
|
+
const stoppedSha = git(vault, ["rev-parse", "--verify", "REBASE_HEAD"]);
|
|
6842
|
+
let commitMsg = "";
|
|
6843
|
+
if (stoppedSha) {
|
|
6844
|
+
commitMsg = git(vault, ["log", "--format=%s", "-1", stoppedSha]);
|
|
6845
|
+
}
|
|
6846
|
+
const isArchiveOrSnapshot = commitMsg.startsWith("archive: moved") || commitMsg.startsWith("Snapshot ");
|
|
6847
|
+
const conflictedFiles = git(vault, ["diff", "--name-only", "--diff-filter=U"]);
|
|
6848
|
+
const conflictedList = conflictedFiles ? conflictedFiles.split("\n").filter((l) => l.trim().length > 0) : [];
|
|
6849
|
+
if (conflictedList.length === 0) {
|
|
6850
|
+
try {
|
|
6851
|
+
gitStrict(vault, ["rebase", "--continue"]);
|
|
6852
|
+
inConflict = true;
|
|
6853
|
+
} catch {
|
|
6854
|
+
inConflict = false;
|
|
6855
|
+
}
|
|
6856
|
+
continue;
|
|
6857
|
+
}
|
|
6858
|
+
if (isArchiveOrSnapshot) {
|
|
6859
|
+
for (const f of conflictedList) {
|
|
6860
|
+
try {
|
|
6861
|
+
gitStrict(vault, ["checkout", "--ours", f]);
|
|
6862
|
+
gitStrict(vault, ["add", f]);
|
|
6863
|
+
} catch {
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
autoResolved += conflictedList.length;
|
|
6867
|
+
try {
|
|
6868
|
+
gitStrict(vault, ["rebase", "--continue"]);
|
|
6869
|
+
} catch (continueErr) {
|
|
6870
|
+
continue;
|
|
6871
|
+
}
|
|
6872
|
+
} else {
|
|
6873
|
+
conflicts = conflictedList.length;
|
|
6874
|
+
return {
|
|
6875
|
+
exitCode: ExitCode.SYNC_PULL_FAILED,
|
|
6876
|
+
result: ok({
|
|
6877
|
+
fetched,
|
|
6878
|
+
pulled: false,
|
|
6879
|
+
files_updated: 0,
|
|
6880
|
+
conflicts,
|
|
6881
|
+
auto_resolved: 0,
|
|
6882
|
+
lint_errors: 0,
|
|
6883
|
+
lint_warnings: 0,
|
|
6884
|
+
humanHint: `pull failed with ${conflicts} conflict(s) on non-archive commit "${commitMsg}" \u2014 resolve manually`
|
|
6885
|
+
})
|
|
6886
|
+
};
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
if (autoResolved > 0) {
|
|
6890
|
+
const diffOutput = git(vault, ["diff", "--stat", "HEAD@{1}..HEAD"]);
|
|
6891
|
+
if (diffOutput) {
|
|
6892
|
+
const fileMatch = diffOutput.match(/(\d+) file[s]? changed/);
|
|
6893
|
+
if (fileMatch) filesUpdated = parseInt(fileMatch[1], 10);
|
|
6894
|
+
}
|
|
6895
|
+
pulled = true;
|
|
6896
|
+
conflicts = 0;
|
|
6897
|
+
}
|
|
6898
|
+
} else {
|
|
6443
6899
|
return {
|
|
6444
6900
|
exitCode: ExitCode.SYNC_PULL_FAILED,
|
|
6445
|
-
result:
|
|
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
|
-
})
|
|
6901
|
+
result: err("GIT_PULL_FAILED", { message: errString })
|
|
6454
6902
|
};
|
|
6455
6903
|
}
|
|
6456
|
-
return {
|
|
6457
|
-
exitCode: ExitCode.SYNC_PULL_FAILED,
|
|
6458
|
-
result: err("GIT_PULL_FAILED", { message: errString })
|
|
6459
|
-
};
|
|
6460
6904
|
}
|
|
6461
6905
|
let lintErrors = 0;
|
|
6462
6906
|
let lintWarnings = 0;
|
|
@@ -6468,6 +6912,7 @@ async function runSyncPull(input) {
|
|
|
6468
6912
|
const hintParts = [];
|
|
6469
6913
|
if (filesUpdated > 0) hintParts.push(`updated ${filesUpdated} file(s)`);
|
|
6470
6914
|
else hintParts.push("already up to date");
|
|
6915
|
+
if (autoResolved > 0) hintParts.push(`${autoResolved} conflict(s) auto-resolved`);
|
|
6471
6916
|
if (lintErrors > 0) hintParts.push(`${lintErrors} lint error(s)`);
|
|
6472
6917
|
if (lintWarnings > 0) hintParts.push(`${lintWarnings} lint warning(s)`);
|
|
6473
6918
|
const exitCode = lintErrors > 0 ? ExitCode.LINT_HAS_ERRORS : lintWarnings > 0 ? ExitCode.LINT_HAS_WARNINGS : ExitCode.OK;
|
|
@@ -6478,6 +6923,7 @@ async function runSyncPull(input) {
|
|
|
6478
6923
|
pulled,
|
|
6479
6924
|
files_updated: filesUpdated,
|
|
6480
6925
|
conflicts,
|
|
6926
|
+
auto_resolved: autoResolved,
|
|
6481
6927
|
lint_errors: lintErrors,
|
|
6482
6928
|
lint_warnings: lintWarnings,
|
|
6483
6929
|
humanHint: hintParts.join(", ")
|
|
@@ -6598,7 +7044,7 @@ function runSyncUnlock(input) {
|
|
|
6598
7044
|
}
|
|
6599
7045
|
|
|
6600
7046
|
// src/commands/backup.ts
|
|
6601
|
-
import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as
|
|
7047
|
+
import { statSync as statSync4, readdirSync as readdirSync2, readFileSync as readFileSync11, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
6602
7048
|
import { join as join35, relative as relative3, dirname as dirname11 } from "path";
|
|
6603
7049
|
import { PutObjectCommand, HeadObjectCommand, ListObjectsV2Command, GetObjectCommand, DeleteObjectsCommand } from "@aws-sdk/client-s3";
|
|
6604
7050
|
|
|
@@ -6665,7 +7111,7 @@ async function runBackupSync(input) {
|
|
|
6665
7111
|
continue;
|
|
6666
7112
|
}
|
|
6667
7113
|
try {
|
|
6668
|
-
const body =
|
|
7114
|
+
const body = readFileSync11(absPath);
|
|
6669
7115
|
await client.send(new PutObjectCommand({ Bucket: input.bucket, Key: relPath, Body: body }));
|
|
6670
7116
|
uploaded++;
|
|
6671
7117
|
} catch {
|
|
@@ -7292,7 +7738,7 @@ async function postCommit(vault, exitCode) {
|
|
|
7292
7738
|
}
|
|
7293
7739
|
|
|
7294
7740
|
// src/cli.ts
|
|
7295
|
-
var pkg = JSON.parse(
|
|
7741
|
+
var pkg = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf8"));
|
|
7296
7742
|
var program = new Command2();
|
|
7297
7743
|
program.name("skillwiki").description("Deterministic helpers for CodeWiki skills").version(pkg.version);
|
|
7298
7744
|
program.option("--human", "render terminal-readable output instead of JSON");
|
|
@@ -7440,7 +7886,7 @@ program.command("log-rotate [vault]").description("rotate or trim the vault log
|
|
|
7440
7886
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
7441
7887
|
else emit(await runLogRotate({ vault: v.vault, threshold: opts.threshold, apply: !!opts.apply }), v.vault);
|
|
7442
7888
|
});
|
|
7443
|
-
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
|
|
7889
|
+
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("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
7444
7890
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
7445
7891
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
7446
7892
|
else emit(await runLint({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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": {
|
package/skills/package.json
CHANGED
|
@@ -8,7 +8,7 @@ Capture ad-hoc ideas, bugs, tasks, and notes into the vault. Three entry points
|
|
|
8
8
|
| Entry | When | What happens |
|
|
9
9
|
|-------|------|-------------|
|
|
10
10
|
| `/wiki-add-task <text>` | You're in a Claude Code session (NOT Hermes compact) | Creates `raw/transcripts/YYYY-MM-DD-{type}-{slug}.md` with ad-hoc capture frontmatter |
|
|
11
|
-
|
|
|
11
|
+
| Filesystem drop | Hermes Agent compact mode (no slash commands available) | Same as above — create `.md` in `raw/transcripts/`, dev-loop discovers it |
|
|
12
12
|
| Filesystem drop | You're NOT in a Claude session (Obsidian, editor, sync) | Create any `.md` file in `raw/transcripts/` using the vault template — dev-loop discovers it on next cycle |
|
|
13
13
|
| Dev-loop discovery | Automatic, next cycle | Scans `raw/transcripts/` for new files since last cycle, surfaces as claimable work |
|
|
14
14
|
**Path Rule:** Captures ALWAYS go to `$(skillwiki path)/raw/transcripts/` (Layer 1). Never under `projects/{slug}/raw/` — that violates SCHEMA.md Layer 1 immutability.
|